Commit 0bfaa037 by vincent

check in retrained tiny 68 point landmark model

parent 7126f5c0
import * as tf from '@tensorflow/tfjs-core';
import { NetInput, normalize } from 'tfjs-image-recognition-base';
import { ConvParams } from 'tfjs-tiny-yolov2';
import { SeparableConvParams } from 'tfjs-tiny-yolov2/build/tinyYolov2/types';
import { depthwiseSeparableConv } from './depthwiseSeparableConv';
import { extractParamsTiny } from './extractParamsTiny';
import { FaceLandmark68NetBase } from './FaceLandmark68NetBase';
import { fullyConnectedLayer } from './fullyConnectedLayer';
import { loadQuantizedParamsTiny } from './loadQuantizedParamsTiny';
import { DenseBlockParams, TinyNetParams } from './types';
function denseBlock(
x: tf.Tensor4D,
denseBlockParams: DenseBlockParams,
isFirstLayer: boolean = false
): tf.Tensor4D {
return tf.tidy(() => {
const out1 = tf.relu(
isFirstLayer
? tf.add(
tf.conv2d(x, (denseBlockParams.conv0 as ConvParams).filters, [2, 2], 'same'),
denseBlockParams.conv0.bias
)
: depthwiseSeparableConv(x, denseBlockParams.conv0 as SeparableConvParams, [2, 2])
) as tf.Tensor4D
const out2 = depthwiseSeparableConv(out1, denseBlockParams.conv1, [1, 1])
const in3 = tf.relu(tf.add(out1, out2)) as tf.Tensor4D
const out3 = depthwiseSeparableConv(in3, denseBlockParams.conv2, [1, 1])
return tf.relu(tf.add(out1, tf.add(out2, out3))) as tf.Tensor4D
})
}
export class FaceLandmark68TinyNet extends FaceLandmark68NetBase<TinyNetParams> {
constructor() {
super('FaceLandmark68TinyNet')
}
public runNet(input: NetInput): tf.Tensor2D {
const { params } = this
if (!params) {
throw new Error('FaceLandmark68TinyNet - load model before inference')
}
return tf.tidy(() => {
const batchTensor = input.toBatchTensor(112, true)
const meanRgb = [122.782, 117.001, 104.298]
const normalized = normalize(batchTensor, meanRgb).div(tf.scalar(255)) as tf.Tensor4D
let out = denseBlock(normalized, params.dense0, true)
out = denseBlock(out, params.dense1)
out = denseBlock(out, params.dense2)
out = tf.avgPool(out, [14, 14], [2, 2], 'valid')
return fullyConnectedLayer(out.as2D(out.shape[0], -1), params.fc)
})
}
protected loadQuantizedParams(uri: string | undefined) {
return loadQuantizedParamsTiny(uri)
}
protected extractParams(weights: Float32Array) {
return extractParamsTiny(weights)
}
}
\ No newline at end of file
import * as tf from '@tensorflow/tfjs-core';
import { SeparableConvParams } from 'tfjs-tiny-yolov2/build/tinyYolov2/types';
export function depthwiseSeparableConv(
x: tf.Tensor4D,
params: SeparableConvParams,
stride: [number, number]
): tf.Tensor4D {
return tf.tidy(() => {
let out = tf.separableConv2d(x, params.depthwise_filter, params.pointwise_filter, stride, 'same')
out = tf.add(out, params.bias)
return out
})
}
\ No newline at end of file
import * as tf from '@tensorflow/tfjs-core';
import { extractWeightsFactory, ExtractWeightsFunction, ParamMapping } from 'tfjs-image-recognition-base';
import { extractConvParamsFactory, FCParams } from 'tfjs-tiny-yolov2';
import { SeparableConvParams } from 'tfjs-tiny-yolov2/build/tinyYolov2/types';
import { DenseBlockParams, TinyNetParams } from './types';
function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings: ParamMapping[]) {
function extractSeparableConvParams(channelsIn: number, channelsOut: number, mappedPrefix: string): SeparableConvParams {
const depthwise_filter = tf.tensor4d(extractWeights(3 * 3 * channelsIn), [3, 3, channelsIn, 1])
const pointwise_filter = tf.tensor4d(extractWeights(channelsIn * channelsOut), [1, 1, channelsIn, channelsOut])
const bias = tf.tensor1d(extractWeights(channelsOut))
paramMappings.push(
{ paramPath: `${mappedPrefix}/depthwise_filter` },
{ paramPath: `${mappedPrefix}/pointwise_filter` },
{ paramPath: `${mappedPrefix}/bias` }
)
return new SeparableConvParams(
depthwise_filter,
pointwise_filter,
bias
)
}
function extractFCParams(channelsIn: number, channelsOut: number, mappedPrefix: string): FCParams {
const weights = tf.tensor2d(extractWeights(channelsIn * channelsOut), [channelsIn, channelsOut])
const bias = tf.tensor1d(extractWeights(channelsOut))
paramMappings.push(
{ paramPath: `${mappedPrefix}/weights` },
{ paramPath: `${mappedPrefix}/bias` }
)
return {
weights,
bias
}
}
return {
extractSeparableConvParams,
extractFCParams
}
}
export function extractParamsTiny(weights: Float32Array): { params: TinyNetParams, paramMappings: ParamMapping[] } {
const paramMappings: ParamMapping[] = []
const {
extractWeights,
getRemainingWeights
} = extractWeightsFactory(weights)
const {
extractSeparableConvParams,
extractFCParams
} = extractorsFactory(extractWeights, paramMappings)
const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings)
function extractDenseBlockParams(channelsIn: number, channelsOut: number, mappedPrefix: string, isFirstLayer: boolean = false): DenseBlockParams {
const conv0 = isFirstLayer
? extractConvParams(channelsIn, channelsOut, 3, `${mappedPrefix}/conv0`)
: extractSeparableConvParams(channelsIn, channelsOut, `${mappedPrefix}/conv0`)
const conv1 = extractSeparableConvParams(channelsOut, channelsOut, `${mappedPrefix}/conv1`)
const conv2 = extractSeparableConvParams(channelsOut, channelsOut, `${mappedPrefix}/conv2`)
return { conv0, conv1, conv2 }
}
const dense0 = extractDenseBlockParams(3, 32, 'dense0', true)
const dense1 = extractDenseBlockParams(32, 64, 'dense1')
const dense2 = extractDenseBlockParams(64, 128, 'dense2')
const fc = extractFCParams(128, 136, 'fc')
if (getRemainingWeights().length !== 0) {
throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`)
}
return {
paramMappings,
params: { dense0, dense1, dense2, fc }
}
}
\ No newline at end of file
import { FaceLandmark68Net } from './FaceLandmark68Net'; import { FaceLandmark68Net } from './FaceLandmark68Net';
export * from './FaceLandmark68Net'; export * from './FaceLandmark68Net';
export * from './FaceLandmark68TinyNet';
export class FaceLandmarkNet extends FaceLandmark68Net {} export class FaceLandmarkNet extends FaceLandmark68Net {}
......
import * as tf from '@tensorflow/tfjs-core';
import {
disposeUnusedWeightTensors,
extractWeightEntryFactory,
loadWeightMap,
ParamMapping,
} from 'tfjs-image-recognition-base';
import { ConvParams, FCParams } from 'tfjs-tiny-yolov2';
import { SeparableConvParams } from 'tfjs-tiny-yolov2/build/tinyYolov2/types';
import { DenseBlockParams, TinyNetParams } from './types';
const DEFAULT_MODEL_NAME = 'face_landmark_68_tiny_model'
function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings)
function extractConvParams(prefix: string): ConvParams {
const filters = extractWeightEntry<tf.Tensor4D>(`${prefix}/filters`, 4)
const bias = extractWeightEntry<tf.Tensor1D>(`${prefix}/bias`, 1)
return { filters, bias }
}
function extractSeparableConvParams(prefix: string): SeparableConvParams {
const depthwise_filter = extractWeightEntry<tf.Tensor4D>(`${prefix}/depthwise_filter`, 4)
const pointwise_filter = extractWeightEntry<tf.Tensor4D>(`${prefix}/pointwise_filter`, 4)
const bias = extractWeightEntry<tf.Tensor1D>(`${prefix}/bias`, 1)
return new SeparableConvParams(
depthwise_filter,
pointwise_filter,
bias
)
}
function extractDenseBlockParams(prefix: string, isFirstLayer: boolean = false): DenseBlockParams {
const conv0 = isFirstLayer
? extractConvParams(`${prefix}/conv0`)
: extractSeparableConvParams(`${prefix}/conv0`)
const conv1 = extractSeparableConvParams(`${prefix}/conv1`)
const conv2 = extractSeparableConvParams(`${prefix}/conv2`)
return { conv0, conv1, conv2 }
}
function extractFcParams(prefix: string): FCParams {
const weights = extractWeightEntry<tf.Tensor2D>(`${prefix}/weights`, 2)
const bias = extractWeightEntry<tf.Tensor1D>(`${prefix}/bias`, 1)
return { weights, bias }
}
return {
extractDenseBlockParams,
extractFcParams
}
}
export async function loadQuantizedParamsTiny(
uri: string | undefined
): Promise<{ params: TinyNetParams, paramMappings: ParamMapping[] }> {
const weightMap = await loadWeightMap(uri, DEFAULT_MODEL_NAME)
const paramMappings: ParamMapping[] = []
const {
extractDenseBlockParams,
extractFcParams
} = extractorsFactory(weightMap, paramMappings)
const params = {
dense0: extractDenseBlockParams('dense0', true),
dense1: extractDenseBlockParams('dense1'),
dense2: extractDenseBlockParams('dense2'),
fc: extractFcParams('fc')
}
disposeUnusedWeightTensors(weightMap, paramMappings)
return { params, paramMappings }
}
\ No newline at end of file
import * as tf from '@tensorflow/tfjs-core';
import { ConvParams, FCParams } from 'tfjs-tiny-yolov2'; import { ConvParams, FCParams } from 'tfjs-tiny-yolov2';
import { SeparableConvParams } from 'tfjs-tiny-yolov2/build/tinyYolov2/types';
export type NetParams = { export type NetParams = {
conv0: ConvParams conv0: ConvParams
...@@ -11,4 +13,49 @@ export type NetParams = { ...@@ -11,4 +13,49 @@ export type NetParams = {
conv7: ConvParams conv7: ConvParams
fc0: FCParams fc0: FCParams
fc1: FCParams fc1: FCParams
} }
\ No newline at end of file
export type ConvWithBatchNormParams = BatchNormParams & {
filter: tf.Tensor4D
}
export type BatchNormParams = {
mean: tf.Tensor1D
variance: tf.Tensor1D
scale: tf.Tensor1D
offset: tf.Tensor1D
}
export type SeparableConvWithBatchNormParams = {
depthwise: ConvWithBatchNormParams
pointwise: ConvWithBatchNormParams
}
export declare type FCWithBatchNormParams = BatchNormParams & {
weights: tf.Tensor2D
}
export type DenseBlockParams = {
conv0: SeparableConvParams | ConvParams
conv1: SeparableConvParams
conv2: SeparableConvParams
//conv3: SeparableConvParams
}
export type TinyNetParams = {
dense0: DenseBlockParams
dense1: DenseBlockParams
dense2: DenseBlockParams
fc: FCParams
}
export type MobileResnetParams = {
conv0: SeparableConvParams
conv1: SeparableConvParams
conv2: SeparableConvParams
conv3: SeparableConvParams
conv4: SeparableConvParams
conv5: SeparableConvParams
fc: FCParams
}
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
<script> <script>
tf = faceapi.tf tf = faceapi.tf
const modelName = 'tiny_yolov2_separable_conv' const modelName = 'face_landmark_68_tiny_model'
const uncompressedWeightsUri = `tiny_yolov2_separable_conv_model_v1.weights` const uncompressedWeightsUri = `face_landmark_68_tiny_model.weights`
const net = new faceapi.TinyYolov2(true) const net = new faceapi.FaceLandmark68TinyNet()
async function loadNetWeights(uri) { async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer()) return new Float32Array(await (await fetch(uri)).arrayBuffer())
......
async function trainStep(batchCreators) {
await promiseSequential(batchCreators.map((batchCreator, dataIdx) => async () => {
const { batchInput, landmarksBatchTensor } = await batchCreator()
let ts = Date.now()
const cost = optimizer.minimize(() => {
const out = window.trainNet.forwardInput(batchInput.managed())
const loss = lossFunction(
landmarksBatchTensor,
out
)
return tf.sum(out)
}, true)
ts = Date.now() - ts
console.log(`loss[${dataIdx}]: ${await cost.data()}, ${ts} ms (${ts / batchInput.batchSize} ms / batch element)`)
landmarksBatchTensor.dispose()
cost.dispose()
await tf.nextFrame()
console.log(tf.memory())
}))
}
function createBatchCreators(data, batchSize) {
if (batchSize < 1) {
throw new Error('invalid batch size: ' + batchSize)
}
const batches = []
const pushToBatch = (remaining) => {
if (remaining.length) {
batches.push(remaining.slice(0, batchSize))
pushToBatch(remaining.slice(batchSize))
}
return batches
}
pushToBatch(data)
const batchCreators = batches.map(dataForBatch => async () => {
const imgs = dataForBatch.map(d => d.img)
const allLandmarks = dataForBatch.map(d => landmarkPositionsToArray(d.landmarks))
const batchInput = await faceapi.toNetInput(imgs)
const landmarksBatchTensor = tf.tidy(() => {
const landmarkTensors = allLandmarks.map(arr => tf.tensor2d(arr, [1, 136]))
return tf.stack(landmarkTensors, 0).as2D(-1, 136)
})
return {
batchInput,
landmarksBatchTensor
}
})
return batchCreators
}
function landmarkPositionsToArray(landmarks) {
return landmarks.getRelativePositions().map(pt => [pt.x, pt.y])
.reduce((flat, arr) => flat.concat(arr))
}
function toFaceLandmarks(landmarks, { naturalWidth, naturalHeight }) {
return new faceapi.FaceLandmarks68(
landmarks.map(l => new faceapi.Point(l.x / naturalWidth, l.y / naturalHeight)),
{ width: naturalWidth, height: naturalHeight }
)
}
async function loadImagesInBatch(allLandmarks, offset = 0) {
return Promise.all(allLandmarks.map(async (landmarks, i) => {
const imgUri = `/train_images/${i + offset}.png`
const img = await faceapi.bufferToImage(await fetchImage(imgUri))
return {
imgUri,
img,
landmarks: toFaceLandmarks(landmarks, img)
}
}))
}
async function getTrainData() {
const landmarksJson = (await (await fetch('/train_landmarks.json')).json())
const numLandmarks = Object.keys(landmarksJson).length
const allLandmarks = Array.from(
new Array(numLandmarks),
(_, i) => landmarksJson[i]
)
return await loadImagesInBatch(allLandmarks.slice(0, 100))
/**
const batch1 = await loadImagesInBatch(allLandmarks.slice(0, 4000))
const batch2 = await loadImagesInBatch(allLandmarks.slice(4000), 4000)
return batch1.concat(batch2)
*/
}
\ No newline at end of file
function onKeyDown(e) {
e.target.value = (
parseInt(e.target.value) + (e.keyCode === 38 ? 1 : (e.keyCode === 40 ? -1 : 0))
) || e.target.value || 0
const uri = '/train_images/' + e.target.value + '.png'
console.log(uri)
onSelectionChanged(uri)
}
function onChangeDrawLines(e) {
window.drawLines = $(e.target).prop('checked')
redraw()
}
async function onSelectionChanged(uri) {
const imgBuf = await fetchImage(uri)
window.currentImg = await faceapi.bufferToImage(imgBuf)
window.landmarks = await Promise.all(window.nets.map(
async net => net.detectLandmarks(window.currentImg)
))
redraw(uri)
}
function drawLandmarkCanvas(img, landmarks, drawOpts) {
const canvas = faceapi.createCanvasFromMedia(img)
$('#faceContainer').append(canvas)
faceapi.drawLandmarks(
canvas,
landmarks,
drawOpts
)
}
function redraw(uri) {
$('#faceContainer').empty()
const drawOpts = { lineWidth: window.drawLines ? 2 : 4, drawLines: window.drawLines }
window.landmarks.forEach(landmarks => {
drawLandmarkCanvas(window.currentImg, landmarks, drawOpts)
})
const trainImgAndLandmarks = window.trainData.find(t => t.imgUri === uri)
if (trainImgAndLandmarks) {
drawLandmarkCanvas(trainImgAndLandmarks.img, trainImgAndLandmarks.landmarks, drawOpts)
}
}
async function loadNet(file) {
const res = await fetch(file)
const weights = new Float32Array(await res.arrayBuffer())
return faceapi.faceLandmarkNet(weights)
}
async function init() {
//await faceapi.loadFaceLandmarkModel('/')
//window.nets.push(faceapi.landmarkNet)
//window.nets.push(await loadNet('retrained/landmarks_v0.weights'))
//window.nets.push(await loadNet('retrained/landmarks_v2.weights'))
window.trainNet = await loadNet('/tmp/retrained/landmarks_v9.weights')
window.nets.push(trainNet)
$('#loader').hide()
await run()
await onSelectionChanged($('#selectList select').val())
}
$(document).ready(function() {
$('#imgByNr').keydown(onKeyDown);
renderFaceImageSelectList(
'#selectList',
onSelectionChanged,
{ className: 'sheldon', imageIdx: 1 }
)
init()
})
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.12.0"> </script>
<script src="../node_modules/file-saver/FileSaver.js"></script>
</head>
<body>
<div class="row side-by-side">
<button
class="waves-effect waves-light btn"
onclick="save()"
>
Save
</button>
<button
class="waves-effect waves-light btn"
onclick="save(true)"
>
Save Tiny
</button>
</div>
<script>
function toDataArray(tensor) {
return Array.from(tensor.dataSync())
}
function flatten(arrs) {
return arrs.reduce((flat, arr) => flat.concat(arr))
}
function initWeights(initializer, isTiny) {
function initConvWeights(inChannels, outChannels) {
return flatten(
[
// filter
initializer.apply([3, 3, inChannels, outChannels]),
// bias
tf.zeros([outChannels])
]
.map(toDataArray)
)
}
function initSeparableConvWeights(inChannels, outChannels) {
return flatten(
[
// depthwise filter
initializer.apply([3, 3, inChannels, 1]),
// pointwise filter
initializer.apply([1, 1, inChannels, outChannels]),
// bias
tf.zeros([outChannels])
]
.map(toDataArray)
)
}
const separableConvWeights = isTiny
? flatten([
initConvWeights(3, 32),
initSeparableConvWeights(32, 32),
initSeparableConvWeights(32, 32),
initSeparableConvWeights(32, 64),
initSeparableConvWeights(64, 64),
initSeparableConvWeights(64, 64),
initSeparableConvWeights(64, 128),
initSeparableConvWeights(128, 128),
initSeparableConvWeights(128, 128)
])
: flatten([
initConvWeights(3, 32),
initSeparableConvWeights(32, 32),
initSeparableConvWeights(32, 32),
initSeparableConvWeights(32, 32),
initSeparableConvWeights(32, 64),
initSeparableConvWeights(64, 64),
initSeparableConvWeights(64, 64),
initSeparableConvWeights(64, 64),
initSeparableConvWeights(64, 128),
initSeparableConvWeights(128, 128),
initSeparableConvWeights(128, 128),
initSeparableConvWeights(128, 128),
initSeparableConvWeights(256, 256),
initSeparableConvWeights(256, 256),
initSeparableConvWeights(256, 256),
initSeparableConvWeights(256, 256)
])
const fc = flatten(
[
initializer.apply([1, 1, isTiny ? 128 : 256, 136]),
// bias
tf.zeros([136])
]
.map(toDataArray)
)
return new Float32Array(separableConvWeights.concat(fc))
}
function save(isTiny = false) {
const initialWeights = initWeights(
tf.initializers.glorotNormal(),
isTiny
)
saveAs(new Blob([initialWeights]), `initial_glorot.weights`)
}
</script>
</body>
</html>
\ No newline at end of file
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB
let jsonsDb = null
let jpgsDb = null
const openJsons = indexedDB.open('jsons', 1)
const openJpgs = indexedDB.open('jpgs', 1)
openJsons.onupgradeneeded = function() {
jsonsDb = openJsons.result
jsonsDb.createObjectStore('jsons', { keyPath: 'id' })
}
openJpgs.onupgradeneeded = function() {
jpgsDb = openJpgs.result
jpgsDb.createObjectStore('jpgs', { keyPath: 'id' })
}
openJsons.onsuccess = function() {
console.log('connected to jsons')
jsonsDb = openJsons.result
}
openJpgs.onsuccess = function() {
console.log('connected to jpgs')
jpgsDb = openJpgs.result
}
function putReq(store, obj) {
return new Promise((res, rej) => {
const req = store.put(obj)
req.onsuccess = res
req.onerror = rej
})
}
function getReq(store, id, throwIfNoResult = true) {
return new Promise((res, rej) => {
const req = store.get(id)
req.onsuccess = () => {
if (!req.result && throwIfNoResult) {
return rej(`no result for id: ${id}`)
}
res(req.result)
}
req.onerror = rej
})
}
function existsReq(store, id) {
return getReq(store, id, false)
}
async function getNotFound(store, ids) {
return (await Promise.all(ids.map(async id => ({ id, exists: await existsReq(store, id) }))))
.filter(({ exists }) => !exists)
.map(({ id }) => id)
}
async function getNotFoundPts(ids) {
const store = jsonsDb.transaction('jsons', 'readonly').objectStore('jsons')
return getNotFound(store, ids)
}
async function getNotFoundJpegs(ids) {
const store = jpgsDb.transaction('jpgs', 'readonly').objectStore('jpgs')
return getNotFound(store, ids)
}
async function persistPts(ptsById, overwrite = false) {
const store = jsonsDb.transaction('jsons', 'readwrite').objectStore('jsons')
for (let i = 0; i < ptsById.length; i++) {
const { id, pts } = ptsById[i]
if (!await existsReq(store, id)) {
console.log('persistPts - inserting %s', id)
await putReq(store, { id, pts })
}
}
}
function getPts(ids) {
const store = jsonsDb.transaction('jsons', 'readonly').objectStore('jsons')
return Promise.all(ids.map(id => getReq(store, id)))
}
async function persistJpgs(jpgsById, overwrite = false) {
const store = jpgsDb.transaction('jpgs', 'readwrite').objectStore('jpgs')
for (let i = 0; i < jpgsById.length; i++) {
const { id, blob } = jpgsById[i]
if (!await existsReq(store, id)) {
console.log('persistJpgs - inserting %s', id)
await putReq(store, { id, blob })
}
}
}
function getJpgs(ids) {
const store = jpgsDb.transaction('jpgs', 'readonly').objectStore('jpgs')
return Promise.all(ids.map(id => getReq(store, id)))
}
// https://gist.github.com/tralves/9e5de2bd9f582007a52708d7d4209865
var getTableSize = function(db, dbName){
return new Promise((resolve,reject) => {
if (db == null) {
return reject();
}
var size = 0;
db = event.target.result;
var transaction = db.transaction([dbName])
.objectStore(dbName)
.openCursor();
transaction.onsuccess = function(event){
var cursor = event.target.result;
if(cursor){
var storedObject = cursor.value;
var json = JSON.stringify(storedObject);
size += json.length;
cursor.continue();
}
else{
resolve(size);
}
}.bind(this);
transaction.onerror = function(err){
reject("error in " + dbName + ": " + err);
}
});
};
var getDatabaseSize = function (dbName) {
var request = indexedDB.open(dbName);
var db;
var dbSize = 0;
request.onerror = function(event) {
alert("Why didn't you allow my web app to use IndexedDB?!");
};
request.onsuccess = function(event) {
db = event.target.result;
var tableNames = [ ...db.objectStoreNames ];
(function(tableNames, db) {
var tableSizeGetters = tableNames
.reduce( (acc, tableName) => {
acc.push( getTableSize(db, tableName) );
return acc;
}, []);
Promise.all(tableSizeGetters)
.then(sizes => {
console.log('--------- ' + db.name + ' -------------');
tableNames.forEach( (tableName,i) => {
console.log(" - " + tableName + "\t: " + humanReadableSize(sizes[i]));
});
var total = sizes.reduce(function(acc, val) {
return acc + val;
}, 0);
console.log("TOTAL: " + humanReadableSize(total))
});
})(tableNames, db);
};
};
var humanReadableSize = function (bytes) {
var thresh = 1024;
if(Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = ['KB','MB','GB','TB','PB','EB','ZB','YB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while(Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}
getDatabaseSize('jsons')
getDatabaseSize('jpgs')
\ No newline at end of file
function randomCrop(img, pts) {
const xCoords = pts.map(pt => pt.x)
const yCoords = pts.map(pt => pt.y)
const minX = xCoords.reduce((x, min) => x < min ? x : min, img.width)
const minY = yCoords.reduce((y, min) => y < min ? y : min, img.height)
const maxX = xCoords.reduce((x, max) => x < max ? max : x, 0)
const maxY = yCoords.reduce((y, max) => y < max ? max : y, 0)
const x0 = Math.random() * minX
const y0 = Math.random() * minY
const x1 = (Math.random() * Math.abs(img.width - maxX)) + maxX
const y1 = (Math.random() * Math.abs(img.height - maxY)) + maxY
const targetCanvas = faceapi.createCanvas({ width: x1 - x0, height: y1 - y0 })
const inputCanvas = img instanceof HTMLCanvasElement ? img : faceapi.createCanvasFromMedia(img)
const region = faceapi.getContext2dOrThrow(inputCanvas)
.getImageData(x0, y0, targetCanvas.width, targetCanvas.height)
faceapi.getContext2dOrThrow(targetCanvas).putImageData(region, 0, 0)
return {
croppedImage: targetCanvas,
shiftedPts: pts.map(pt => new faceapi.Point(pt.x - x0, pt.y - y0))
}
}
\ No newline at end of file
async function fillPtsDb(ids) {
const numFetchPerIteration = 4000
for (let i = 0; i < Math.floor(ids.length / numFetchPerIteration) + 1; i++) {
const ptsById = await Promise.all(
ids.slice(i * numFetchPerIteration, (i + 1) * numFetchPerIteration)
.map(async id => ({
id,
pts: await (await fetch(`${id}.json`)).json()
}))
)
console.log('persistPts')
console.time('persistPts')
await persistPts(ptsById)
console.timeEnd('persistPts')
}
}
async function fillJpgsDb(ids) {
const numFetchPerIteration = 4000
for (let i = 0; i < Math.floor(ids.length / numFetchPerIteration) + 1; i++) {
const jpgsById = await Promise.all(
ids.slice(i * numFetchPerIteration, (i + 1) * numFetchPerIteration)
.map(async id => ({
id,
blob: await fetchImage(`${id}.jpg`)
}))
)
console.log('persistJpgs')
console.time('persistJpgs')
await persistJpgs(jpgsById)
console.timeEnd('persistJpgs')
}
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script src="face-api.js"></script>
<script src="commons.js"></script>
<script src="js/randomCrop.js"></script>
<script src="js/indexedDb.js"></script>
<script src="js/synchronizeDb.js"></script>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
</head>
<body>
<div id="result" style="position: relative" class="margin">
<img id="inputImg" src="" style="max-width: 400px;" />
<canvas id="overlay" />
</div>
<script>
const net = new faceapi.FaceLandmark68TinyNet()
const modelCheckpoint = 'tmp/densenet3_conv0/checkpoints/landmarks_epoch59.weights'
const crops = 4
async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer())
}
async function load() {
await net.load('./')
//const weights = await loadNetWeights(modelCheckpoint)
//await net.load(weights)
console.log('model loaded')
}
load()
function pointsToFlatArray(pts) {
return pts.map(pt => [pt.x, pt.y]).reduce((flat, arr) => flat.concat(arr))
}
async function showResults(landmarks) {
const inputImgEl = $('#inputImg').get(0)
const { width, height } = inputImgEl
const canvas = $('#overlay').get(0)
canvas.width = width
canvas.height = height
faceapi.drawLandmarks('overlay', landmarks, { drawLines: true })
}
async function testAll() {
window.testIds = (await fetch('/face_landmarks_test_ids').then(res => res.json()))
test()
}
async function testSidewaysX(useTrainSet = false) {
window.testIds = (await fetch(useTrainSet ? '/sideways_x_train' : '/sideways_x_test').then(res => res.json()))
test()
}
async function testStrongSidewaysX(useTrainSet = false) {
window.testIds = (await fetch(useTrainSet ? '/strong_sideways_x_train' : '/strong_sideways_x_test').then(res => res.json()))
test()
}
async function testStrongSidewaysY(useTrainSet = false) {
window.testIds = (await fetch(useTrainSet ? '/strong_sideways_y_train' : '/strong_sideways_y_test').then(res => res.json()))
test()
}
async function test() {
let totalError = 0
for (let i = 0; i < window.testIds.length; i++) {
const dataId = window.testIds[i]
let originalImage = (await getJpgs([dataId]))[0]
originalImage = await faceapi.bufferToImage(originalImage.blob)
const groundTruthPts = (await getPts([dataId])).map(res => res.pts)[0]
for (let c = 0; c < (crops || 1); c++) {
console.log(i, c, i / window.testIds.length)
const { croppedImage, shiftedPts } = crops > 0
? randomCrop(originalImage, groundTruthPts)
: { croppedImage: originalImage, shiftedPts: groundTruthPts }
const squaredImg = faceapi.imageToSquare(croppedImage, 112, true)
const landmarks = (await net.detectLandmarks(squaredImg)).forSize(croppedImage.width, croppedImage.height)
const dist = faceapi.euclideanDistance(
pointsToFlatArray(landmarks.getPositions()),
pointsToFlatArray(shiftedPts)
)
const distByCoordinate = dist / 136
totalError += distByCoordinate
if (window.debug) {
const inputImgEl = $('#inputImg').get(0)
inputImgEl.src = croppedImage instanceof HTMLImageElement ? croppedImage.src : croppedImage.toDataURL()
inputImgEl.onload = () => showResults(landmarks)
for (let i = 0; i < 30; i++)
await faceapi.tf.nextFrame()
}
}
}
console.log('done...')
console.log('test set size:', window.testIds.length)
console.log('total error:', totalError / (crops || 1))
console.log('average error:', totalError / ((crops || 1) * window.testIds.length))
}
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<script src="face-api.js"></script> <script src="face-api.js"></script>
<script src="commons.js"></script> <script src="commons.js"></script>
<script src="faceLandmarksUi.js"></script> <script src="js/randomCrop.js"></script>
<script src="faceLandmarksTrain.js"></script> <script src="js/indexedDb.js"></script>
<script src="js/synchronizeDb.js"></script>
<script src="FileSaver.js"></script> <script src="FileSaver.js"></script>
<script src="trainUtils.js"></script>
</head> </head>
<body> <body>
<div id="navbar"></div> <div id="container"></div>
<div class="center-content page-container">
<div>
<div class="progress" id="loader">
<div class="indeterminate"></div>
</div>
<div class="row side-by-side">
<div class="center-content">
<div id="faceContainer"></div>
<div id="selectList"></div>
</div>
</div>
<p>
<input type="checkbox" id="drawLinesCheckbox" checked="checked" onchange="onChangeDrawLines(event)" />
<label for="drawLinesCheckbox">Draw Lines</label>
</p>
<div class="row">
<label for="imgByNr">Enter image NR: </label>
<input id="imgByNr" type="text" class="bold">
</div>
</div>
</div>
<script> <script>
tf = faceapi.tf tf = faceapi.tf
window.saveEveryNthIteration = 2
window.drawLines = true // uri to weights file of last checkpoint
window.nets = [] window.net = new faceapi.FaceLandmark68TinyNet()
window.landmarks = [] const modelCheckpoint = '/tmp/initial_glorot.weights.weights'
const startEpoch = 0
window.trainSteps = 100
window.learningRate = 0.01 const learningRate = 0.001 // 0.001
//window.optimizer = tf.train.sgd(learningRate) window.optimizer = tf.train.adam(learningRate, 0.9, 0.999, 1e-8)
window.optimizer = tf.train.adam(0.001, 0.9, 0.999, 1e-8)
window.saveEveryNthSample = Infinity
function lossFunction(labels, out) {
return tf.losses.meanSquaredError(labels, out) window.withRandomCrop = true
window.batchSize = 12
//window.batchSize = 32
window.lossValues = []
window.iterDelay = 0
window.withLogging = true
const log = (str, ...args) => console.log(`[${[(new Date()).toTimeString().substr(0, 8)]}] ${str || ''}`, ...args)
function saveWeights(net, filename = 'train_tmp') {
saveAs(new Blob([net.serializeParams()]), filename)
}
async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer())
}
async function delay(ms) {
return new Promise(res => setTimeout(res, ms))
} }
async function run() { async function fetchTrainDataIds() {
window.trainData = await getTrainData() return fetch('/face_landmarks_train_ids').then(res => res.json())
window.trainNet.variable() }
async function load() {
window.trainIds = (await fetchTrainDataIds())
const weights = await loadNetWeights(modelCheckpoint)
await window.net.load(weights)
window.net.variable()
}
async function onEpochDone(epoch) {
saveWeights(window.net, `landmarks_epoch59_sideways71_epoch8_sideways35_epoch${epoch}.weights`)
return const loss = window.lossValues[epoch]
saveAs(new Blob([JSON.stringify({ loss, avgLoss: loss / window.trainIds.length })]), `landmarks_epoch59_sideways71_epoch8_sideways35_epoch${epoch}.json`)
await train()
} }
async function train(batchSize = 1) { async function train() {
for (let i = 0; i < trainSteps; i++) { await load()
console.log('step', i)
const batchCreators = createBatchCreators(shuffle(window.trainData), batchSize) for (let epoch = startEpoch; epoch < Infinity; epoch++) {
let ts = Date.now()
await trainStep(batchCreators) if (epoch !== startEpoch) {
ts = Date.now() - ts // ugly hack to wait for loss datas for that epoch to be resolved
console.log('step %s done (%s ms)', i, ts) setTimeout(() => onEpochDone(epoch - 1), 10000)
if (((i + 1) % saveEveryNthIteration) === 0) { }
//saveWeights(window.trainNet, 'landmark_trained_weights_' + idx + '.weights') window.lossValues[epoch] = 0
const shuffledInputs = faceapi.shuffleArray(window.trainIds)
for (let dataIdx = 0; dataIdx < shuffledInputs.length; dataIdx += window.batchSize) {
const tsIter = Date.now()
const ids = shuffledInputs.slice(dataIdx, dataIdx + window.batchSize)
// fetch image and ground truth bounding boxes
let tsFetch = Date.now()
let tsFetchPts = Date.now()
const allPts = (await getPts(ids)).map(res => res.pts)
tsFetchPts = Date.now() - tsFetchPts
let tsFetchJpgs = Date.now()
const allJpgBlobs = (await getJpgs(ids)).map(res => res.blob)
tsFetchJpgs = Date.now() - tsFetchJpgs
let tsBufferToImage = Date.now()
const allJpgs = await Promise.all(
allJpgBlobs.map(blob => faceapi.bufferToImage(blob))
)
tsBufferToImage = Date.now() - tsBufferToImage
tsFetch = Date.now() - tsFetch
const bImages = []
const bPts = []
for (let batchIdx = 0; batchIdx < ids.length; batchIdx += 1) {
const pts = allPts[batchIdx]
const img = allJpgs[batchIdx]
const { croppedImage, shiftedPts } = window.withRandomCrop
? randomCrop(img, pts)
: { croppedImage: img, shiftedPts: pts }
const squaredImg = faceapi.imageToSquare(croppedImage, 112, true)
const reshapedDimensions = faceapi.computeReshapedDimensions(croppedImage, 112)
const pad = Math.abs(reshapedDimensions.width - reshapedDimensions.height) / (2 * 112)
const padX = reshapedDimensions.width < reshapedDimensions.height ? pad : 0
const padY = reshapedDimensions.height < reshapedDimensions.width ? pad : 0
const groundTruthLandmarks = shiftedPts.map(pt => new faceapi.Point(
padX + (pt.x / croppedImage.width) * (reshapedDimensions.width / 112),
padY + (pt.y / croppedImage.height)* (reshapedDimensions.height / 112)
))
bImages.push(squaredImg)
bPts.push(groundTruthLandmarks)
}
const netInput = await faceapi.toNetInput(bImages)
let tsBackward = Date.now()
let tsForward = Date.now()
const loss = optimizer.minimize(() => {
const out = window.net.runNet(netInput)
tsForward = Date.now() - tsForward
tsBackward = Date.now()
const landmarksTensor = tf.tensor2d(
bPts
.reduce((flat, arr) => flat.concat(arr))
.map(pt => [pt.x, pt.y])
.reduce((flat, arr) => flat.concat(arr)),
[ids.length, 136]
)
const loss = tf.losses.meanSquaredError(
landmarksTensor,
out,
tf.Reduction.MEAN
)
return loss
}, true)
tsBackward = Date.now() - tsBackward
// start next iteration without waiting for loss data
loss.data().then(data => {
const lossValue = data[0]
window.lossValues[epoch] += lossValue
window.withLogging && log(`epoch ${epoch}, dataIdx ${dataIdx} - loss: ${lossValue}, ( ${window.lossValues[epoch]})`)
loss.dispose()
})
window.withLogging && log(`epoch ${epoch}, dataIdx ${dataIdx} - forward: ${tsForward} ms, backprop: ${tsBackward} ms, iter: ${Date.now() - tsIter} ms`)
if (window.logsilly) {
log(`fetch: ${tsFetch} ms, pts: ${tsFetchPts} ms, jpgs: ${tsFetchJpgs} ms, bufferToImage: ${tsBufferToImage} ms`)
}
if (window.iterDelay) {
await delay(window.iterDelay)
} else {
await tf.nextFrame()
}
} }
} }
} }
</script> </script>
</body> </body>
</html> </html>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html>
<head>
<script src="face-api.js"></script>
<script src="commons.js"></script>
<script src="js/randomCrop.js"></script>
<script src="js/indexedDb.js"></script>
<script src="FileSaver.js"></script>
</head>
<body>
<div id="container"></div>
<script>
tf = faceapi.tf
// uri to weights file of last checkpoint
window.net = new faceapi.FaceLandmark68TinyNet()
const modelCheckpoint = '/tmp/initial_glorot.weights'
const startEpoch = 0
const learningRate = 0.001 // 0.001
window.optimizer = tf.train.adam(learningRate, 0.9, 0.999, 1e-8)
window.saveEveryNthSample = 50000000
window.withRandomCrop = false
window.batchSize = 18
window.lossValue = 0
window.drawResults = true
const log = (str, ...args) => console.log(`[${[(new Date()).toTimeString().substr(0, 8)]}] ${str || ''}`, ...args)
function saveWeights(net, filename = 'train_tmp') {
saveAs(new Blob([net.serializeParams()]), filename)
}
async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer())
}
async function fetchTrainDataFilenames() {
return fetch('/face_landmarks_train_ids').then(res => res.json())
}
async function load() {
window.ptsFiles = await fetchTrainDataFilenames()
window.ptsFiles = window.ptsFiles.slice(1000, 1000 + window.batchSize)
//window.ptsFiles = window.ptsFiles.slice(7, 8)
const weights = await loadNetWeights(modelCheckpoint)
await window.net.load(weights)
window.net.variable()
}
function randomCrop(img, pts) {
const xCoords = pts.map(pt => pt.x)
const yCoords = pts.map(pt => pt.y)
const minX = xCoords.reduce((x, min) => x < min ? x : min, img.width)
const minY = yCoords.reduce((y, min) => y < min ? y : min, img.height)
const maxX = xCoords.reduce((x, max) => x < max ? max : x, 0)
const maxY = yCoords.reduce((y, max) => y < max ? max : y, 0)
const x0 = Math.random() * minX
const y0 = Math.random() * minY
const x1 = (Math.random() * Math.abs(img.width - maxX)) + maxX
const y1 = (Math.random() * Math.abs(img.height - maxY)) + maxY
const targetCanvas = faceapi.createCanvas({ width: x1 - x0, height: y1 - y0 })
const inputCanvas = img instanceof HTMLCanvasElement ? img : faceapi.createCanvasFromMedia(img)
const region = faceapi.getContext2dOrThrow(inputCanvas)
.getImageData(x0, y0, targetCanvas.width, targetCanvas.height)
faceapi.getContext2dOrThrow(targetCanvas).putImageData(region, 0, 0)
return {
croppedImage: targetCanvas,
shiftedPts: pts.map(pt => new faceapi.Point(pt.x - x0, pt.y - y0))
}
}
async function train() {
await load()
for (let epoch = startEpoch; epoch < Infinity; epoch++) {
if (epoch !== startEpoch) {
//saveWeights(window.net, `landmarks_epoch${epoch - 1}.weights`)
//saveAs(new Blob([JSON.stringify({ loss: window.lossValue, avgLoss: window.lossValue / 20000 })]), `landmarks_epoch${epoch - 1}.json`)
window.lossValue = 0
}
const shuffledInputs = window.drawResults ? window.ptsFiles : faceapi.shuffleArray(window.ptsFiles)
for (let dataIdx = 0; dataIdx < shuffledInputs.length; dataIdx += window.batchSize) {
const ids = shuffledInputs.slice(dataIdx, dataIdx + window.batchSize)
// fetch image and ground truth bounding boxes
console.time('getPts')
const allPts = (await getPts(ids)).map(res => res.pts)
console.timeEnd('getPts')
console.time('getJpgs')
const allJpgs = await Promise.all(
(await getJpgs(ids)).map(res => res.blob).map(blob => faceapi.bufferToImage(blob))
)
console.timeEnd('getJpgs')
const bSquaredImages = []
const bCroppedImages = []
const bImages = []
const bPts = []
for (let batchIdx = 0; batchIdx < ids.length; batchIdx += 1) {
const pts = allPts[batchIdx]
const img = allJpgs[batchIdx]
const { croppedImage, shiftedPts } = window.withRandomCrop
? randomCrop(img, pts)
: { croppedImage: img, shiftedPts: pts }
const squaredImg = faceapi.imageToSquare(croppedImage, 112, true)
const reshapedDimensions = faceapi.computeReshapedDimensions(croppedImage, 112)
const pad = Math.abs(reshapedDimensions.width - reshapedDimensions.height) / (2 * 112)
const padX = reshapedDimensions.width < reshapedDimensions.height ? pad : 0
const padY = reshapedDimensions.height < reshapedDimensions.width ? pad : 0
const groundTruthLandmarks = shiftedPts.map(pt => new faceapi.Point(
padX + (pt.x / croppedImage.width) * (reshapedDimensions.width / 112),
padY + (pt.y / croppedImage.height)* (reshapedDimensions.height / 112)
))
bSquaredImages.push(squaredImg)
bCroppedImages.push(croppedImage)
bImages.push(squaredImg)
bPts.push(groundTruthLandmarks)
}
const netInput = await faceapi.toNetInput(bImages)
const ts = Date.now()
const loss = optimizer.minimize(() => {
console.time('runNet')
const out = window.net.runNet(netInput)
console.timeEnd('runNet')
if (window.drawResults) {
// debug
const landmarkTensors = tf.unstack(out)
landmarkTensors.forEach((t, i) => {
const croppedImage = bSquaredImages[i]
const landmarksArray = Array.from(t.dataSync())
const xCoords = landmarksArray.filter((_, i) => faceapi.isEven(i))
const yCoords = landmarksArray.filter((_, i) => !faceapi.isEven(i))
/*
const landmarksArray = bPts[i]
const xCoords = landmarksArray.map(pt => pt.x)
const yCoords = landmarksArray.map(pt => pt.y)
*/
const marks = new faceapi.FaceLandmarks68(
Array(68).fill(0).map((_, i) => new faceapi.Point(xCoords[i], yCoords[i])),
croppedImage
)
const canvas = croppedImage instanceof HTMLCanvasElement ? croppedImage : faceapi.createCanvasFromMedia(croppedImage)
faceapi.drawLandmarks(canvas, marks, { drawLines: true })
const container = document.getElementById('container')
if (container.children[i]) {
const oldChild = container.children[i]
container.insertBefore(canvas, oldChild)
container.removeChild(oldChild)
} else {
container.appendChild(canvas)
}
})
// debug
}
const landmarksTensor = tf.tensor2d(
bPts
.reduce((flat, arr) => flat.concat(arr))
.map(pt => [pt.x, pt.y])
.reduce((flat, arr) => flat.concat(arr)),
[window.batchSize, 136]
)
const loss = tf.losses.meanSquaredError(
landmarksTensor,
out,
tf.Reduction.MEAN
)
//return tf.sum(tf.abs(tf.sub(landmarksTensor, out)))
return loss
}, true)
// start next iteration without waiting for loss data
loss.data().then(data => {
const lossValue = data[0]
window.lossValue += lossValue
log(`epoch ${epoch}, dataIdx ${dataIdx} - loss: ${lossValue}`)
loss.dispose()
})
if (window.iterDelay) {
await delay(window.iterDelay)
} else {
await tf.nextFrame()
}
}
}
}
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script src="face-api.js"></script>
<script src="commons.js"></script>
<script src="js/randomCrop.js"></script>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
</head>
<body>
<div id="navbar"></div>
<div class="center-content page-container">
<div class="progress" id="loader">
<div class="indeterminate"></div>
</div>
<div id="results" class="side-by-side">
<div id="result0" style="position: relative" class="margin">
<img id="inputImg0" src="" style="max-width: 400px;" />
<canvas class="overlay" id="overlay0" />
</div>
<div id="result1" style="position: relative" class="margin">
<img id="inputImg1" src="" style="max-width: 400px;" />
<canvas class="overlay" id="overlay1" />
</div>
<div id="result2" style="position: relative" class="margin">
<img id="inputImg2" src="" style="max-width: 400px;" />
<canvas class="overlay" id="overlay2" />
</div>
</div>
<div class="row">
<label for="imgByNr">Enter image NR: </label>
<input id="imgByNr" type="text" class="bold" value="0"/>
</div>
</div>
<script>
const modelCheckpoints = [
//'tmp/densenet3_conv0/checkpoints/landmarks_epoch59.weights'
]
let originalImage = null
function cloneImage(img) {
var image = document.createElement("img")
image.src = img.src
return image
}
function getInputValue(input) {
input = input || $('#imgByNr').get(0)
return input.value || 0
}
function onKeyDown(e) {
e.target.value = (
parseInt(e.target.value) + (e.keyCode === 38 ? 1 : (e.keyCode === 40 ? -1 : 0))
) || getInputValue(e.target)
const imgUri = `${window.testIds[e.target.value]}.jpg`
console.log(imgUri)
onSelectionChanged(imgUri)
}
async function updateResults(_landmarks) {
const inputImgEl = $('#inputImg0').get(0)
const { width, height } = inputImgEl
for (let i = 0; i < window.nets.length; i++) {
const net = window.nets[i]
const canvas = $(`#overlay${i}`).get(0)
canvas.width = width
canvas.height = height
console.time('detectLandmarks')
const landmarks = await net.detectLandmarks(inputImgEl)
console.timeEnd('detectLandmarks')
faceapi.drawLandmarks(`overlay${i}`, landmarks.forSize(width, height), { drawLines: true })
}
}
async function onSelectionChanged(imgUri) {
const img = await faceapi.bufferToImage(await fetchImage(imgUri))
window.nets.forEach(async (_, i) => {
$(`#inputImg${i}`).get(0).src = img.src
})
originalImage = cloneImage(img)
updateResults()
}
async function applyRandomCrop() {
const dataId = window.testIds[getInputValue()]
const pts = await (await fetch(`${dataId}.json`)).json()
const img = cloneImage(originalImage)
const { croppedImage, shiftedPts } = randomCrop(img, pts)
img.src = croppedImage.toDataURL()
$(`#result${0}`).get(0).removeChild($(`#inputImg${0}`).get(0))
$(`#result${0}`).get(0).appendChild(img)
window.nets.slice(1).forEach((_, i) => {
const im = cloneImage(img)
im.id = `#inputImg${i + 1}`
$(`#result${i + 1}`).get(0).removeChild($(`#inputImg${i + 1}`).get(0))
$(`#result${i + 1}`).get(0).appendChild(im)
})
const { width, height } = croppedImage
img.onload = () => updateResults(
new faceapi.FaceLandmarks68(
shiftedPts.map(pt =>
new faceapi.Point(
pt.x / width,
pt.y / height
)
),
{ width, height }
)
)
}
async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer())
}
async function run() {
$('#imgByNr').keydown(onKeyDown)
faceapi.loadFaceLandmarkModel('./')
//window.trainIds = (await fetch('/face_landmarks_train_ids').then(res => res.json()))
//window.testIds = (await fetch('/face_landmarks_test_ids').then(res => res.json()))
window.testIds = (await fetch('/strong_sideways_test').then(res => res.json()))
window.nets = []
window.nets.push(faceapi)
const net = new faceapi.FaceLandmark68TinyNet()
await net.load('./')
window.nets.push(net)
modelCheckpoints.forEach(async checkpoint => {
const net = new faceapi.FaceLandmark68TinyNet()
const weights = await loadNetWeights(checkpoint)
await net.load(weights)
window.nets.push(net)
})
$('#loader').hide()
}
$(document).ready(() => run())
</script>
</body>
</html>
\ No newline at end of file
...@@ -2,6 +2,7 @@ require('./faceLandmarks/.env') ...@@ -2,6 +2,7 @@ require('./faceLandmarks/.env')
const express = require('express') const express = require('express')
const path = require('path') const path = require('path')
const fs = require('fs')
const app = express() const app = express()
...@@ -14,9 +15,72 @@ app.use(express.static(path.join(__dirname, '../../weights'))) ...@@ -14,9 +15,72 @@ app.use(express.static(path.join(__dirname, '../../weights')))
app.use(express.static(path.join(__dirname, '../../dist'))) app.use(express.static(path.join(__dirname, '../../dist')))
const trainDataPath = path.resolve(process.env.TRAIN_DATA_PATH) const trainDataPath = path.resolve(process.env.TRAIN_DATA_PATH)
app.use(express.static(trainDataPath))
app.get('/', (req, res) => res.redirect('/face_landmarks')) const pngPath = path.join(trainDataPath, 'png')
app.get('/face_landmarks', (req, res) => res.sendFile(path.join(publicDir, 'train.html'))) const jpgPath = path.join(trainDataPath, 'jpg')
const groundTruthPath = path.join(trainDataPath, 'pts')
app.listen(3000, () => console.log('Listening on port 3000!')) app.use(express.static(jpgPath))
\ No newline at end of file app.use(express.static(groundTruthPath))
const trainIds = JSON.parse(fs.readFileSync(path.join(publicDir, './trainData.json')))
const trainIdsSet = new Set(trainIds)
const testFileIds = fs.readdirSync(groundTruthPath)
.map(file => file.replace('.json', ''))
.filter(file => !trainIdsSet.has(file))
app.get('/face_landmarks_train_ids', (req, res) => res.status(202).send(trainIds))
app.get('/face_landmarks_test_ids', (req, res) => res.status(202).send(testFileIds))
const poseAnchors = JSON.parse(fs.readFileSync(path.join(trainDataPath, './pose-anchors.json')))
const sidewaysXTrain = []
const strongSidewaysXTrain = []
const strongSidewaysYTrain = []
const strongSidewaysTrain = []
const sidewaysXTest = []
const strongSidewaysXTest = []
const strongSidewaysYTest = []
const strongSidewaysTest = []
poseAnchors.forEach((pt, idx) => {
const id = `${idx}`
if (Math.abs(0.5 - pt.x) > 0.15) {
(trainIdsSet.has(id) ? sidewaysXTrain : sidewaysXTest).push(id)
}
if (Math.abs(0.5 - pt.x) > 0.2) {
(trainIdsSet.has(id) ? strongSidewaysXTrain : strongSidewaysXTest).push(id)
}
if (Math.abs(0.44 - pt.y) > 0.1) {
(trainIdsSet.has(id) ? strongSidewaysYTrain : strongSidewaysYTest).push(id)
}
if (Math.abs(0.5 - pt.x) > 0.2 || Math.abs(0.44 - pt.y) > 0.1) {
(trainIdsSet.has(id) ? strongSidewaysTrain : strongSidewaysTest).push(id)
}
})
console.log(sidewaysXTrain.length)
console.log(strongSidewaysXTrain.length)
console.log(strongSidewaysYTrain.length)
console.log(strongSidewaysTrain.length)
console.log(sidewaysXTest.length)
console.log(strongSidewaysXTest.length)
console.log(strongSidewaysYTest.length)
console.log(strongSidewaysTrain.length)
app.get('/sideways_x_train', (req, res) => res.status(202).send(sidewaysXTrain))
app.get('/strong_sideways_x_train', (req, res) => res.status(202).send(strongSidewaysXTrain))
app.get('/strong_sideways_y_train', (req, res) => res.status(202).send(strongSidewaysYTrain))
app.get('/strong_sideways_train', (req, res) => res.status(202).send(strongSidewaysTrain))
app.get('/sideways_x_test', (req, res) => res.status(202).send(sidewaysXTest))
app.get('/strong_sideways_x_test', (req, res) => res.status(202).send(strongSidewaysXTest))
app.get('/strong_sideways_y_test', (req, res) => res.status(202).send(strongSidewaysYTest))
app.get('/strong_sideways_test', (req, res) => res.status(202).send(strongSidewaysTest))
app.get('/', (req, res) => res.redirect('/train'))
app.get('/train', (req, res) => res.sendFile(path.join(publicDir, 'train.html')))
app.get('/verify', (req, res) => res.sendFile(path.join(publicDir, 'verify.html')))
app.get('/test', (req, res) => res.sendFile(path.join(publicDir, 'test.html')))
app.listen(8000, () => console.log('Listening on port 8000!'))
\ No newline at end of file
require('./tinyYolov2/.env')
const express = require('express')
const path = require('path')
const fs = require('fs')
const app = express()
const publicDir = path.join(__dirname, './tinyYolov2')
app.use(express.static(publicDir))
app.use(express.static(path.join(__dirname, './shared')))
app.use(express.static(path.join(__dirname, './node_modules/file-saver')))
app.use(express.static(path.join(__dirname, '../../examples/public')))
app.use(express.static(path.join(__dirname, '../../weights')))
app.use(express.static(path.join(__dirname, '../../dist')))
const trainDataPath = path.resolve(process.env.TRAIN_DATA_PATH)
const testDataPath = path.resolve(process.env.TEST_DATA_PATH)
const imagesPath = path.join(trainDataPath, './final_images')
const detectionsPath = path.join(trainDataPath, './final_detections')
app.use(express.static(imagesPath))
app.use(express.static(detectionsPath))
app.use(express.static(testDataPath))
const detectionFilenames = fs.readdirSync(detectionsPath)
const detectionFilenamesMultibox = JSON.parse(fs.readFileSync(path.join(__dirname, './tinyYolov2/multibox.json')))
app.use(express.static(trainDataPath))
app.get('/detection_filenames', (req, res) => res.status(202).send(detectionFilenames))
app.get('/detection_filenames_multibox', (req, res) => res.status(202).send(detectionFilenamesMultibox))
app.get('/', (req, res) => res.sendFile(path.join(publicDir, 'train.html')))
app.get('/verify', (req, res) => res.sendFile(path.join(publicDir, 'verify.html')))
app.get('/test', (req, res) => res.sendFile(path.join(publicDir, 'test.html')))
app.listen(3000, () => console.log('Listening on port 3000!'))
\ No newline at end of file
const log = (str, ...args) => console.log(`[${[(new Date()).toTimeString().substr(0, 8)]}] ${str || ''}`, ...args)
async function promiseSequential(promises) {
const curr = promises[0]
if (!curr) {
return
}
await curr()
return promiseSequential(promises.slice(1))
}
// https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array
function shuffle(a) {
var j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
}
function saveWeights(net, filename = 'train_tmp') {
const binaryWeights = new Float32Array(
net.getParamList()
.map(({ tensor }) => Array.from(tensor.dataSync()))
.reduce((flat, arr) => flat.concat(arr))
)
saveAs(new Blob([binaryWeights]), filename)
}
function imageToSquare(img) {
const scale = 608 / Math.max(img.height, img.width)
const width = scale * img.width
const height = scale * img.height
const canvas1 = faceapi.createCanvasFromMedia(img)
const targetCanvas = faceapi.createCanvas({ width: 608, height: 608 })
targetCanvas.getContext('2d').putImageData(canvas1.getContext('2d').getImageData(0, 0, width, height), 0, 0)
return targetCanvas
}
function getPaddingsAndReshapedSize(img, inputSize) {
const [h, w] = [img.height, img.width]
const maxDim = Math.max(h, w)
const f = inputSize / maxDim
const reshapedImgDims = {
height: Math.floor(h * f),
width: Math.floor(w * f)
}
const paddings = new faceapi.Point(
maxDim / img.width,
maxDim / img.height
)
return { paddings, reshapedImgDims }
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.12.0"> </script>
<script src="FileSaver.js"></script>
</head>
<body>
<script>
const glorotNormal = tf.initializers.glorotNormal()
saveWeights()
function initSeparableConvWeights(inChannels, outChannels) {
return {
depthwiseFilter: glorotNormal.apply([3, 3, inChannels, 1]),
pointwiseFilter: glorotNormal.apply([1, 1, inChannels, outChannels]),
bias: tf.zeros([outChannels])
}
}
function initTinyYolov2SeparableWeights() {
const conv0 = initSeparableConvWeights(3, 16)
const conv1 = initSeparableConvWeights(16, 32)
const conv2 = initSeparableConvWeights(32, 64)
const conv3 = initSeparableConvWeights(64, 128)
const conv4 = initSeparableConvWeights(128, 256)
const conv5 = initSeparableConvWeights(256, 512)
const conv6 = initSeparableConvWeights(512, 1024)
const conv7 = initSeparableConvWeights(1024, 1024)
const conv8 = {
filters: glorotNormal.apply([1, 1, 1024, 25]),
bias: tf.zeros([25])
}
return {
conv0,
conv1,
conv2,
conv3,
conv4,
conv5,
conv6,
conv7,
conv8
}
}
function saveWeights() {
const w = initTinyYolov2SeparableWeights()
const binaryWeights = new Float32Array(
Array(8).fill(0)
.map((_, i) => w[`conv${i}`])
.map(ps => [ps.depthwiseFilter, ps.pointwiseFilter, ps.bias])
.reduce((flat, arr) => flat.concat(arr))
.concat([w.conv8.filters, w.conv8.bias])
.map(tensor => console.log(tensor) || Array.from(tensor.dataSync()))
.reduce((flat, arr) => flat.concat(arr))
)
saveAs(new Blob([binaryWeights]), 'foo.weights')
}
</script>
</body>
</html>
\ No newline at end of file
const CELL_SIZE = 32
const getNumCells = inputSize => inputSize / CELL_SIZE
const inverseSigmoid = x => Math.log(x / (1 - x))
function getAnchors() {
return window.net.anchors
}
function squaredSumOverMask(lossTensors, mask) {
return tf.tidy(() => tf.sum(tf.square(tf.mul(mask, lossTensors))))
}
function assignBoxesToAnchors(groundTruthBoxes, reshapedImgDims) {
const inputSize = Math.max(reshapedImgDims.width, reshapedImgDims.height)
const numCells = getNumCells(inputSize)
return groundTruthBoxes.map(box => {
const { left, top, width, height } = box.rescale(reshapedImgDims)
const ctX = left + (width / 2)
const ctY = top + (height / 2)
const col = Math.floor((ctX / inputSize) * numCells)
const row = Math.floor((ctY / inputSize) * numCells)
const anchorsByIou = getAnchors().map((anchor, idx) => ({
idx,
iou: faceapi.iou(
new faceapi.BoundingBox(0, 0, anchor.x * CELL_SIZE, anchor.y * CELL_SIZE),
new faceapi.BoundingBox(0, 0, width, height)
)
})).sort((a1, a2) => a2.iou - a1.iou)
const anchor = anchorsByIou[0].idx
return { row, col, anchor, box }
})
}
function getGroundTruthMask(groundTruthBoxes, inputSize) {
const numCells = getNumCells(inputSize)
const mask = tf.zeros([numCells, numCells, 25])
const buf = mask.buffer()
groundTruthBoxes.forEach(({ row, col, anchor }) => {
const anchorOffset = anchor * 5
for (let i = 0; i < 5; i++) {
buf.set(1, row, col, anchorOffset + i)
}
})
return mask
}
function getCoordAndScoreMasks(inputSize) {
const numCells = getNumCells(inputSize)
const coordMask = tf.zeros([numCells, numCells, 25])
const scoreMask = tf.zeros([numCells, numCells, 25])
const coordBuf = coordMask.buffer()
const scoreBuf = scoreMask.buffer()
for (let row = 0; row < numCells; row++) {
for (let col = 0; col < numCells; col++) {
for (let anchor = 0; anchor < 5; anchor++) {
const anchorOffset = 5 * anchor
for (let i = 0; i < 4; i++) {
coordBuf.set(1, row, col, anchorOffset + i)
}
scoreBuf.set(1, row, col, anchorOffset + 4)
}
}
}
return { coordMask, scoreMask }
}
function computeBoxAdjustments(groundTruthBoxes, reshapedImgDims) {
const inputSize = Math.max(reshapedImgDims.width, reshapedImgDims.height)
const numCells = getNumCells(inputSize)
const adjustments = tf.zeros([numCells, numCells, 25])
const buf = adjustments.buffer()
groundTruthBoxes.forEach(({ row, col, anchor, box }) => {
const { left, top, right, bottom, width, height } = box.rescale(reshapedImgDims)
const centerX = (left + right) / 2
const centerY = (top + bottom) / 2
//const dCenterX = centerX - (col * CELL_SIZE + (CELL_SIZE / 2))
//const dCenterY = centerY - (row * CELL_SIZE + (CELL_SIZE / 2))
const dCenterX = centerX - (col * CELL_SIZE)
const dCenterY = centerY - (row * CELL_SIZE)
const dx = inverseSigmoid(dCenterX / CELL_SIZE)
const dy = inverseSigmoid(dCenterY / CELL_SIZE)
//const dx = dCenterX / CELL_SIZE
//const dy = dCenterY / CELL_SIZE
const dw = Math.log((width / CELL_SIZE) / getAnchors()[anchor].x)
const dh = Math.log((height / CELL_SIZE) / getAnchors()[anchor].y)
const anchorOffset = anchor * 5
buf.set(dx, row, col, anchorOffset + 0)
buf.set(dy, row, col, anchorOffset + 1)
buf.set(dw, row, col, anchorOffset + 2)
buf.set(dh, row, col, anchorOffset + 3)
})
return adjustments
}
function computeIous(predBoxes, groundTruthBoxes, reshapedImgDims) {
const inputSize = Math.max(reshapedImgDims.width, reshapedImgDims.height)
const numCells = getNumCells(inputSize)
const isSameAnchor = p1 => p2 =>
p1.row === p2.row
&& p1.col === p2.col
&& p1.anchor === p2.anchor
const ious = tf.zeros([numCells, numCells, 25])
const buf = ious.buffer()
groundTruthBoxes.forEach(({ row, col, anchor, box }) => {
const predBox = predBoxes.find(isSameAnchor({ row, col, anchor }))
if (!predBox) {
console.log(groundTruthBoxes)
console.log(predBoxes)
throw new Error(`no output box found for: row ${row}, col ${col}, anchor ${anchor}`)
}
const iou = faceapi.iou(
box.rescale(reshapedImgDims),
predBox.box.rescale(reshapedImgDims)
)
if (window.debug) {
console.log('ground thruth box:', box.rescale(reshapedImgDims).toRect())
console.log('predicted box:', predBox.box.rescale(reshapedImgDims).toRect())
console.log('predicted score:', predBox.score)
console.log('iou:', iou)
}
const anchorOffset = anchor * 5
buf.set(iou, row, col, anchorOffset + 4)
})
return ious
}
window.computeNoObjectLoss = function(outTensor, mask) {
return tf.tidy(() => {
const lossTensor = tf.sigmoid(outTensor)
return squaredSumOverMask(lossTensor, mask)
})
}
function computeObjectLoss(outTensor, groundTruthBoxes, reshapedImgDims, paddings, mask) {
return tf.tidy(() => {
const predBoxes = window.net.postProcess(
outTensor,
{ paddings }
)
const ious = computeIous(
predBoxes,
groundTruthBoxes,
reshapedImgDims
)
const lossTensor = tf.sub(ious, tf.sigmoid(outTensor))
return squaredSumOverMask(lossTensor, mask)
})
}
function computeCoordLoss(groundTruthBoxes, outTensor, reshapedImgDims, mask, paddings) {
return tf.tidy(() => {
const boxAdjustments = computeBoxAdjustments(
groundTruthBoxes,
reshapedImgDims
)
if (window.debug) {
const indToPos = []
const numCells = outTensor.shape[1]
for (let row = 0; row < numCells; row++) {
for (let col = 0; col < numCells; col++) {
for (let anchor = 0; anchor < 25; anchor++) {
indToPos.push({ row, col, anchor: parseInt(anchor / 5) })
}
}
}
const indices = Array.from(mask.dataSync()).map((val, ind) => ({ val, ind })).filter(v => v.val !== 0).map(v => v.ind)
const gt = Array.from(boxAdjustments.dataSync())
const out = Array.from(outTensor.dataSync())
const comp = indices.map(i => (
{
pos: indToPos[i],
gt: gt[i],
out: out[i]
}
))
console.log(comp.map(c => `gt: ${c.gt}, out: ${c.out}`))
const getBbox = (which) => {
const { row, col, anchor } = comp[0].pos
const ctX = ((col + faceapi.sigmoid(comp[0][which])) / numCells) * paddings.x
const ctY = ((row + faceapi.sigmoid(comp[1][which])) / numCells) * paddings.y
const width = ((Math.exp(comp[2][which]) * getAnchors()[anchor].x) / numCells) * paddings.x
const height = ((Math.exp(comp[3][which]) * getAnchors()[anchor].y) / numCells) * paddings.y
const x = (ctX - (width / 2))
const y = (ctY - (height / 2))
return new faceapi.BoundingBox(x, y, x + width, y + height)
}
const outRect = getBbox('out').rescale(reshapedImgDims).toRect()
const gtRect = getBbox('gt').rescale(reshapedImgDims).toRect()
console.log('out', outRect)
console.log('gtRect', gtRect)
}
const lossTensor = tf.sub(boxAdjustments, outTensor)
return squaredSumOverMask(lossTensor, mask)
})
}
function computeLoss(outTensor, groundTruth, reshapedImgDims, paddings) {
const inputSize = Math.max(reshapedImgDims.width, reshapedImgDims.height)
if (!inputSize) {
throw new Error(`invalid inputSize: ${inputSize}`)
}
let groundTruthBoxes = assignBoxesToAnchors(
groundTruth
.map(({ x, y, width, height }) => new faceapi.Rect(x, y, width, height))
.map(rect => rect.toBoundingBox()),
reshapedImgDims
)
const groundTruthMask = getGroundTruthMask(groundTruthBoxes, inputSize)
const { coordMask, scoreMask } = getCoordAndScoreMasks(inputSize)
const noObjectLossMask = tf.tidy(() => tf.mul(scoreMask, tf.sub(tf.scalar(1), groundTruthMask)))
const objectLossMask = tf.tidy(() => tf.mul(scoreMask, groundTruthMask))
const coordLossMask = tf.tidy(() => tf.mul(coordMask, groundTruthMask))
const noObjectLoss = tf.tidy(() =>
tf.mul(
tf.scalar(noObjectScale),
computeNoObjectLoss(outTensor, noObjectLossMask)
)
)
const objectLoss = tf.tidy(() =>
tf.mul(
tf.scalar(objectScale),
computeObjectLoss(outTensor, groundTruthBoxes, reshapedImgDims, paddings, objectLossMask)
)
)
const coordLoss = tf.tidy(() =>
tf.mul(
tf.scalar(coordScale),
computeCoordLoss(groundTruthBoxes, outTensor, reshapedImgDims, coordLossMask, paddings)
)
)
const totalLoss = tf.tidy(() => noObjectLoss.add(objectLoss).add(coordLoss))
return {
noObjectLoss,
objectLoss,
coordLoss,
totalLoss
}
}
\ No newline at end of file
import * as _tf from '@tensorflow/tfjs-core';
const faceapi = require('../../../dist/face-api.js')
const tf: typeof _tf = faceapi.tf
require('./loss')
window['faceapi'] = faceapi
window['tf'] = tf
const anchors = [
new faceapi.Point(1.603231, 2.094468),
new faceapi.Point(6.041143, 7.080126),
new faceapi.Point(2.882459, 3.518061),
new faceapi.Point(4.266906, 5.178857),
new faceapi.Point(9.041765, 10.66308)
]
window['net'] = {
getAnchors() {
return anchors
}
}
describe('loss', () => {
describe('computeNoObjectLoss', () => {
const computeNoObjectLoss = window['computeNoObjectLoss']
it('should only compute loss over scores, 1x1 grid', () => tf.tidy(() => {
const outTensor = tf.zeros([1, 1, 1, 25])
const loss = tf.sum(computeNoObjectLoss(outTensor)).dataSync()[0]
expect(loss).toEqual(0.5 * 0.5 * 5)
}))
it('should only compute loss over scores, 13x13 grid', () => tf.tidy(() => {
const outTensor = tf.zeros([1, 13, 13, 25])
const loss = tf.sum(computeNoObjectLoss(outTensor)).dataSync()[0]
expect(loss).toEqual(0.5 * 0.5 * 5 * 13 * 13)
}))
it('should only compute loss over scores, 13x13 grid, batchSize: 10', () => tf.tidy(() => {
const outTensor = tf.zeros([10, 13, 13, 25])
const loss = tf.sum(computeNoObjectLoss(outTensor)).dataSync()[0]
expect(loss).toEqual(0.5 * 0.5 * 5 * 13 * 13 * 10)
}))
})
})
\ No newline at end of file
["6ff8b403d437ec9210aed005f4b7d5de.jpeg.json","701a7573675975ffa17b5958d994da04.jpeg.json","70305901c9438c83241ded45a614a05a.jpeg.json","703dbe721760333b14f219b071af0258.jpeg.json","70644f2d900f3e3df8a094514378c8dc.jpeg.json","707c483fbfa6e10ea272052dbac91fbf.jpeg.json","70a226ff7b913d022f432a6262c432d5.jpeg.json","7103f3c81d7b9b5f47607f5517aa3210.jpeg.json","71040961f9178cb641ee2bda77daa1f1.jpeg.json","7153af94856dc09d65ca940b407ae437.jpeg.json","715f772a6a2306fd040598065dbe80e8.jpeg.json","71ec70fdebfe01fc82c055135e73e5aa.jpeg.json","71f38f7c6fd203dd193725a6575eb84f.jpeg.json","72058ea0b2f0ea32f6c1534dd0f4259a.jpeg.json","73203f4c115da50ef1bc411a2a043d22.jpeg.json","748311686961c891f4afbd8a373c02e6.jpeg.json","7531f8ba7c212c6ac13bdb1d6fc2a19f.jpeg.json","75349eb64fdd5d0fbf005df5c7594f07.jpeg.json","753681fbf3d3b2808b8c889f215feb5c.jpeg.json","755cb42ce7f1df46a1b893ea5d26cf26.jpeg.json","75f27276f628e427a2bf80aa7f9792e3.jpeg.json","75f2ace4925a2a749d6942885766f036.jpeg.json","76016b537ef69992905dad5c40289e21.jpeg.json","7624165ba78d723987fd7734ba6af201.png.json","76490fbac3548a57ce43fed12b29d5c6.jpeg.json","770ce887b2dc0c854d100ce8f11d30f7.png.json","77e3e59d693d7071abc68c4428802bbf.jpeg.json","7803ba050aa011496bb49f45ae395617.jpeg.json","787fd9830b9534d4603c704921312dc5.jpeg.json","78aa83fb0a9cd9148258af5201be89ec.jpeg.json","78b8ccc845900d7795b47d08618a0cd6.jpeg.json","78d5b85760cc495385c47f9ae486b37a.jpeg.json","78f3dad6dd4ace4ce16c1520c0c79a1a.jpeg.json","7941a02b69f84c5fe9faaa94510b2860.jpeg.json","798fd92d7074c29049fc772bdcb371ab.jpeg.json","79a1d77dd1380dbd15299ed72dfde5de.jpeg.json","79b0035db4d98d20736f354de4b7afc9.jpeg.json","79d6ffadb928a9aef20f1d3cd68810d6.jpeg.json","79f2078ec3fa4131062ec733c8d3ca10.jpeg.json","7a364f7e14fe9106787773b4bcb5cfc0.jpeg.json","7a6b1f70f5bd8e18f18c0c17a92848d3.jpeg.json","7b68ecb287ac3fdc8af59ed8d543848f.png.json","7b741584c0f339e91ecb7413c306d084.jpeg.json","7b7996d595b2df7ef2fd1a5502bb83cc.jpg.json","7b90dc94a75f26bd402735b26f45d017.jpeg.json","7bdc2b31b0632aff9922497b144972b3.png.json","7bdf2d802946995231b14274b2405ba7.jpeg.json","7c4c6e1c4cd562c7fc692fb3740dd1fb.jpg.json","7c5ce28f52c8befd9f86e6781a02b8c0.png.json","7cca72949282cc482fbb57b6230a92a7.png.json","7d3b0acd6672f0e36bd70153e53a07a8.jpeg.json","7d3d5a022ad59550f3ef259236e826bd.jpeg.json","7d514ff53e1150d4c1744aac4a2009ff.jpeg.json","7d52f80060a528bb7b2544c04baa65fb.jpeg.json","7d78e2881e3a2f454ce2cf822ca3e6af.jpeg.json","7e0ddfcc80d573770cd39f5853ec8daa.jpeg.json","7e27f9f4225299a55f786ab6d8eed544.jpeg.json","7e6296f2e01c6211eb7c5ba74ac3f4f7.jpeg.json","7e68577d4a092b66b009fe16f89b93ee.jpeg.json","7e9247a43ee9469014d72cfb7c6d23b9.jpeg.json","7eb014c79aa03c18f11a58ea58612c19.jpeg.json","7f9b22a160f257d7b4d3a80c09bb38ac.jpeg.json","806a1e4b1ed5a58f6b541fddad7f6a21.jpeg.json","8071807fba2c90fd85714cb40048c71f.jpeg.json","80787772d05ff9f8e58fc3e276df2c8f.jpeg.json","80a32fbe55da9c650fad4211badb09dc.jpeg.json","80ec0adfb26fc2d9e702382685560682.jpeg.json","8144caa0d00ba83634057866be21bb43.jpeg.json","818da784c33a36dade6ddbb4acb62a96.jpeg.json","81b0e7e46da0e150b295ee7a6d3e1d4f.jpeg.json","81e4ba2ee066ff09ce661cd1d9750ef2.jpeg.json","8202cf2736f818a34875d753d326b2e9.jpeg.json","820e93571774600596cbf7d152c12bb5.jpeg.json","828dbd12d2ed9d0ab59c39e0cf52405d.jpeg.json","82d4d7b29cdf9d02883002309f0a2b51.jpeg.json","831b1a75dbf1f605ac934cac47f2f6d8.jpeg.json","834bf7852cb1b14d1e1a3ba2a218bcda.jpeg.json","83631e520695495b92607c43168db581.jpeg.json","83c35f6fdf42ab14105836ede211a16c.jpeg.json","83e41860c05866c0e3918652417f8e45.jpeg.json","83fa5b54a47101cf0f63190eb24e1cd1.jpeg.json","841e1d2566656bded15d5e88c57c030b.jpeg.json","8441ce1f48fb9f42c1dc1965cc8c321f.JPEG.json","8475b7301e3b286871edd56452fd8e3f.jpeg.json","84914d767fffb2ba4fbeb2e04bf59d0b.jpeg.json","8494de96ec43cec4d3d2c33db672f403.jpeg.json","849ba4e71a31608b8dfa3407f50bf9a5.jpeg.json","854b6eb006aedb7c6ce72bdf5a078d51.jpeg.json","85838401549e4a1f22555627f44cc955.jpeg.json","859df8e5277a1c70815df5a5ca9ecffb.jpeg.json","85f59c64d02330962fbcfa26d7abfef7.jpeg.json","86040943da555695cf999b4ab9d03bb0.jpeg.json","866f7191af186f97a1b8c9623d73a82b.jpeg.json","86854f0064b17b918f90bc57a1980437.jpeg.json","87b431be307a704329f32ee4a63ff10d.jpeg.json","87ca351b10e672fbb1b3dde77e5525e4.jpeg.json","87df1901f592db9f071adb357b3c2d42.jpeg.json","884326038ea0d8ab9a9aa7847a87b73b.jpeg.json","886ad98d2d123c4192efdc0a00df4635.jpeg.json","888a1119743c85ff9120870f1c10fa53.jpeg.json","88badd516a416a5dd1528759d81dfa2f.jpeg.json","892e21ec3aa8c1ed1765a431b2b68482.jpeg.json","8976d9fc2c5b2ababda1406d043bdb74.jpeg.json","8a4e32e64a7dd08e29804623d4b96547.jpeg.json","8a59d79dae544c9046fd12f476c97165.jpeg.json","8b4bd50543e08290446d32a2a12f6ffe.jpeg.json","8b56fb59e1e1fd22a2df2013dd438c35.jpeg.json","8ba2afe0a859ca8156b8310badca4c9f.jpeg.json","8bb8ab40226c6368f143bf29c1bbd5a6.jpeg.json","8bdd8a6919661ea6cc729a31ab0955d5.png.json","8c3b0c73ce253840febcd51f3b8e8ce6.jpeg.json","8c5f38f5ab076909e00b447248d00687.jpeg.json","8c9496dc6cef4a0983fb3973b55b02a3.jpeg.json","8cb75fbf0624fd5cd086a49b1be8b2bf.jpeg.json","8cbe52a94b0701078098008e8f95c6ed.jpeg.json","8ce2b5a1afefdcbdd3462813a55090df.jpeg.json","8d0a85407a1eb4ed4162030d482275f9.jpeg.json","8da7129adbc4cb11a0387d3542fcf615.jpeg.json","8db1f21462137313d55f1a162ae1dd76.jpeg.json","8ddba860f36f70d82496d0f9d0b88bf1.jpeg.json","8df2cacbafde9eb289e229ea80f156f9.jpeg.json","8e2a21e0bf7fcb73adebe3dc760aac3b.jpeg.json","8ec4f340ff26d4d3685d55a9e51664a2.jpeg.json","8ee0a6ee1d87e42c377ff2b96c282f02.jpeg.json","8ee56a12c1f8929e0af9ed466e798381.jpeg.json","8f06cd4ff26dec9e56ccd07220446075.jpeg.json","8f0e47302b522ed20a7520c5b4a78f7c.jpeg.json","8f618dfa969081acd3012b296e2201bb.jpeg.json","8f9b1e321b35dba0d6942180979cb66d.jpg.json","8fb501f2238f9eb0d2d1026758cd15fe.jpeg.json","901c453cd81a877ace73eb0ad746a2ed.jpeg.json","9032a049b9745b29b5435ed253a49ff5.jpeg.json","90a2d791c096a54f78f0b8a04bbe8da2.jpeg.json","90a7881307bd13eb4bc9be47aeb20f28.jpeg.json","90c4545574be9de286262b7eff20adac.jpeg.json","911f4132ffbd482d9eb7f85f66b2ecee.jpeg.json","912f85ed5abd31fb7adb24c2bd8859d6.jpeg.json","91b1be0ae205888ac0b77ea6320e2ca1.jpeg.json","91d9f5ab78276a70f0972e3d8a15f745.jpeg.json","91db4d6a662b759a72d48707ba43e34b.jpeg.json","91ea2f5359cc19280d96c101bfe22759.jpeg.json","921a572517b61ec2d8096256b22fbeb2.jpeg.json","9236e75942f85a5a014e93aac73dfeff.jpeg.json","927810f080a7df039fb429fc1034c146.jpeg.json","92d8769ab1ac7919e46086e267349ece.jpeg.json","93f5fa9beb051efc61b931463b3362a6.jpeg.json","941afb668b7b5432667fc0cccfbfb641.png.json","941c9453c25b97cf56b81daf0fa3e2f1.jpeg.json","943cb181858d995be5225d1419e50c5c.jpeg.json","9441715282a08469d681a97ccd4b233f.png.json","947ddefa87430eaf0c40729d0fc7410f.jpeg.json","95355a69b7b39c60259719705cea9900.jpeg.json","9558b20aaeb1b1c833e20ccdf413fbf7.jpeg.json","957cad737d5e939581f3e8aa027233e1.jpeg.json","95c563f18662d1987996cdb2ead4624d.jpeg.json","95f5e3159aaebf5831ef0409cf274075.jpeg.json","9639be2d1eff02ec7162a823e669c44f.png.json","963aef3fff65546cb9803cfaf889ca34.jpeg.json","96440c01c37a3a594dd42ef21431f3a9.jpeg.json","96450269ec8e68b0108e8b5b4d145c55.jpeg.json","965c8a1ffbe3fc9951ee1ef5e34c6d37.jpeg.json","96ab72a688f57d0de88c71ef0bb6cfe7.jpeg.json","97738f79dad5424d6e1a440a3a461532.jpeg.json","97b8f38e9cf3adbf6657b25ce1a7335c.jpeg.json","9813582798fab064746a1b94a0f38f24.jpeg.json","98ccbfbe45bc1bcfbc3f40ec39a5f41f.jpeg.json","98f688756e56414609e0e27d39701c56.jpeg.json","99366e7ee9249b7dff69856eea8bd9a1.jpeg.json","993834e21792dfc43bfa9dd6bac01013.jpeg.json","997c7bd5d32ec45ea86a13b11444a59e.jpeg.json","99e22017232b6de400542df046a784ce.jpeg.json","99efc5d7227e94e8fa5db14657440d93.jpeg.json","9a41ce7c515fca5f0e3d852553bf7401.jpeg.json","9a586f9a33601864bd887e6a3fc15306.jpeg.json","9a9adfe19d6c60cf14406ee7cc4ba8b9.jpeg.json","9af6d1a0b444caef74043671fecc5a2d.jpeg.json","9b01e5e1794f2049e77f62be7d61fefb.jpeg.json","9b09ec48138fcc10bb6546e561550205.jpeg.json","9b71abcaaf16fb9544330871c1997c61.jpeg.json","9b72b7e9c7a2eba1225a2b94bf6c075a.jpeg.json","9b74584ba01059866029f10e04a28d86.jpeg.json","9b9f0306056eba83ee63fe13309d6416.jpeg.json","9bc01accf2bd998927e35eac49fbe701.jpeg.json","9bc603c6f4135cd85aadfb0ce280cf2e.jpeg.json","9c11c8da895be5e51f6f376bd565a19f.jpeg.json","9c1fa32f357d296e353e8eaa1c0de5d5.jpeg.json","9c417c45c7c2f840657b66c5c6445e1f.jpeg.json","9cfe50b051a74f0d34d47b2c2fc289ff.jpeg.json","9d833f506e39a9b256b7895ca4f4a12e.jpeg.json","9e6788e49029872d6af65628e47dfb42.jpeg.json","9e753701f51917034425ec42089227d1.jpeg.json","9e94808c0f99648e03ca95de2db13aaf.jpeg.json","9ed3aeff287805318f9158e973a84d80.jpeg.json","9eec8bf302a0b554a97ec051326e207d.jpeg.json","9f0354346e5f10f3a9cfa52298f988af.jpeg.json","9f470ac5164bde63ef3be45e9710e55f.jpeg.json","9f814896cba97063fe41f14519f3e3a9.jpeg.json","9fa8ba16bdb2e9866dc88edb3d6d4dbe.jpeg.json","9fb879fc041e93f09f4d4220f54ac496.jpeg.json","9fc6ace3668c28d8843ce3faeb967a81.jpeg.json","a11192978ca202719cd557e43ed49985.jpeg.json","a2985d563eeb38e0cd9853d9874d2186.jpeg.json","a2a3ec05647e4708b65b988b72831a99.jpeg.json","a2c345b97444160ca424f69ab2967424.jpeg.json","a2e6f5eb92c42c6c081375193e4016c0.jpeg.json","a3078d34dedd5f24001b5e0c629e53d1.jpeg.json","a36349468a0f3303e247c3bf1a3624f9.jpeg.json","a36cc42841175f14579ff80d9c21c7f8.jpeg.json","a375ca2f02bce027332edc0b59412819.jpeg.json","a43687a677c502eda3825a8e22504865.jpeg.json","a4a36933aa80f535d0d26beef6d534df.jpeg.json","a4fdc6f8ff15a377a7bbef763ce056b4.jpeg.json","a50e6b1510a72ee28a09f8f89201a515.jpeg.json","a51cf5fbcebabdb1b9b108349ef4c6e5.png.json","a56531bd2a7405fa41ec2c89a7740772.jpeg.json","a56e1286a9516e4c2a1ef113ab6b7f9c.jpeg.json","a6284ea25b31c6d9744e30e430ba22bb.jpeg.json","a64bbd93b3c31558166c74267c4ac73e.jpeg.json","a701b6c44062f5392e9375b3a70211a1.jpeg.json","a7582f2944bf8b2dbffb613d5e81911e.png.json","a768268f66f7ef309cb5adce6932e916.jpeg.json","a84850991d22f6dbcdac714225d2ed5d.jpeg.json","a9168a323a9861c5d44a27ca12cdcba3.jpeg.json","a99c7bbde59c831006c02ba047b3895b.png.json","a9a3c66d69397aabc475dea5b3d46fd7.jpeg.json","a9c70118fa87e20076152f8df3303e0b.jpeg.json","a9d60d9edf124b2a6e0371a9d4b1105a.jpeg.json","aa21c7f303b2b5e161883704901ca315.jpeg.json","aa248053b9f210e59cebe829e3c9b7d5.jpeg.json","aad7d6917fb9297e3647719963062f23.jpeg.json","ab10e5cb60fe670021c78c78adbab5df.jpeg.json","ab613048422816fdf3c15608065a29a1.jpeg.json","aba73d12fbe9f8e1b5477a197e07dfb3.jpeg.json","abdd45bcbb22860d36069b0019247198.jpeg.json","ac1743e4e6991795d887cdf34e7f68da.jpeg.json","ac294fcb10136cc069ca76fea18b1012.jpeg.json","acd9f38ee4702a6539408c69f593e091.png.json","acee560d56cf1e1c3be2fe5cbce85311.jpeg.json","acf27a1a77b1c64826d23fdd8b314de4.jpeg.json","ad8afd075c3ba5a3ffccb866223f5edb.jpeg.json","adcfa03f6ca674ae5c597dc120e76a89.jpeg.json","ae35ae8e2be13b5b45595c274b1cf7eb.jpeg.json","ae452077f9971d9921a7afb3922048cd.jpeg.json","ae6df2a0f14877bc5461abc3340e50c8.jpeg.json","ae6fc6f56c8727760e79d8c2b68036f2.jpeg.json","ae6fe194927e756df7fe631522f1c610.jpeg.json","aef1eeb4fd79ccf182859a3246c1d8d6.jpeg.json","af201f933bac670b2dc2350e86cee5c1.jpeg.json","af3f7fde9d22f417cdc0df4753e1cb50.jpeg.json","af7a777c97af1ad15984c4a5301ebf33.jpeg.json","afe3e53779e69ca42aab7f1791a5da39.jpeg.json","afe5047af1f74252f2c9c60992b91056.jpeg.json","b009a59ff072a43ea399a6e5ec517abf.jpeg.json","b099b4fb9dbe29bfa7c7b6be1940b614.jpeg.json","b0d92bfe4af109347d4463c46239d39e.jpeg.json","b1507b84f010a6a9422dfc64fb543487.jpeg.json","b16ed70eed5fb43186894fe692e7d475.jpeg.json","b29f34f5ae0a0809fcfa977b5ad9da87.jpeg.json","b2b7b90e86c5825806925d455e5bb216.jpeg.json","b34a507a4e4cfb5ebeb22839e3720d9f.jpeg.json","b369335407124257410680bc75a29766.jpeg.json","b3732c175eb8b6f9cd1ae9045166e16f.jpeg.json","b3e5dfed31c4e7bc8effbacf61ff82c8.jpeg.json","b40755769b8c56c01dd68b0dea7845cc.jpeg.json","b42335a9da636e4ad3cc00d3cc4ba05d.jpeg.json","b426df0cc4acc34ec5af159e8d42efd0.jpeg.json","b43941538c97ed7b14e4b5247f791e41.jpeg.json","b449be070cb8539436be650255bda41f.jpeg.json","b4a8395a54e094ee1250860e34c2ec07.jpeg.json","b685e3383780ffeacb8e6297cf0ae4b3.jpeg.json","b685e5f2be5e93d34e8041c66c1f1e03.jpeg.json","b6d011b0bb1a62abaab18b468449f784.jpeg.json","b6d427bdcb4ccd66b43f2bcfeea2192b.jpeg.json","b6ee014f5d623c6cca88732ac6d41c7c.jpeg.json","b71b3c9a493a1f63adb497cbf6cbfb2a.jpeg.json","b7355aa0b18badfff95d5fd5af6b8268.jpeg.json","b7616310f7f218dd57906e6e0cfcafa5.jpeg.json","b77a8535c138319408a0c757fffb1d79.jpeg.json","b8688ea021519777f61f124a419fdb29.jpeg.json","b86b50c9f0ce0b64431c4081a2f573dd.jpeg.json","b8ae48811862229e89f88794a2f02ddc.jpeg.json","b8cf36cfc7f9ae5a48714e475b25f164.jpeg.json","b945150c2ec585f3d7bd0a9102f8540d.jpeg.json","b95079e842b08ff260630efe2fe7e141.jpeg.json","b99f523d94bb7df35b7d028fdf94e988.jpeg.json","b9ccf86f5471af4d6428f873d8d958a8.jpeg.json","b9dc2bea6f9241ebeb5fc7e7ff8c04c6.png.json","ba1071f184a84439df9f6114b6e70472.jpeg.json","bae4b514eed98090d7e9aa81e0b03de2.jpeg.json","bb41fb0ae0d6f9a036a56def9924f172.jpeg.json","bb8648702ffaba9723ce164f24ca16d2.jpeg.json","bb8e78ad2c05150f55ce85704c4023e5.jpeg.json","bb8ec58ee700b811a205f577450160f1.jpeg.json","bbbef3b7c7943694c0969e11c35179ad.jpeg.json","bc390abe92c933d67427607a90c5d8e0.jpeg.json","bc3c5a9d4543ed7f5795582a632b20ce.jpeg.json","bc437be6d3ee896e081a752a7f7dd9f1.jpeg.json","bc6a12c985b8ee2d74d4c9bfb9918791.jpeg.json","bcee2f70904deff9fbfa47186c382dab.jpeg.json","bd78ced66d4e9939f7c4dca321c5cc64.jpeg.json","bd88325d1e6c52f87981f7b88d9f4efe.jpeg.json","bd92ceabd72484a556792c0efefc9071.jpeg.json","bde5e4571fee9681d6e7f2e089b5e4c5.jpeg.json","be26e439c5348cbcf8802fb66ea10ade.jpeg.json","be5c23545eb93575e7060f01f7ddcf88.jpeg.json","bf98d5ffa7eece6b3e72683e29c184f7.jpeg.json","bff24593f4c61fd4cfe25a63f019822c.jpeg.json","bffbb92b56845b871a727abb6e0cc9b7.jpeg.json","c03a255bb791b438134bcc211bcfc229.jpeg.json","c064de930c0c79937e528b0142a30da6.JPEG.json","c097e31ab213847094af00c163c27223.jpeg.json","c0bc62c518d17b377e60b8c6c046b862.jpeg.json","c1505ff920b8712d5d221e9ffe70709b.jpeg.json","c17e47a687117911eaaab9eca760c4eb.jpeg.json","c17fab603ec0c74155b1636c4418696d.jpeg.json","c1b15b94b3dfb7c964cc0027c8258ee7.jpeg.json","c1eb726692d48e56505f69d1d5e8f93e.jpeg.json","c238aea63cbddfbd121c944efc62af6b.jpeg.json","c25288d3b882fc081156387819203347.jpeg.json","c2e97bd83465c1013fb449aae9c139ba.jpeg.json","c311ca4d933f354d9a1b7e1a0ce5a158.jpeg.json","c37fcd546f12a1027b3aaa98ca49b567.jpeg.json","c4709b17ce7ae094ef8d148567aafc12.jpeg.json","c4dd7333fd768946314126dbaa209044.jpeg.json","c5428858d364875f454de16ea20209ef.jpeg.json","c54ae5db16c59a7689c3181b794776c5.jpeg.json","c57c739aa8fdffc25b3df14cb5e8cdf2.jpeg.json","c5c9c9f3078b29003c3eed19a9043419.jpeg.json","c6aca7bf440fd6f83df844419739ff34.jpeg.json","c6bf4ff2724a16eba0c61405e8739b03.jpeg.json","c6f987545f6ac44a7fae4b0d90c1cc22.jpeg.json","c763aefb9189781ee2fb49768f06e2e8.jpeg.json","c7988e0dbf6876b324824d8d58103fba.jpeg.json","c7a5775839e1c03994860dc36bac13f4.jpeg.json","c7ff714283d234f96601bcfd20c3ec03.jpeg.json","c839ef18856b89f21e79652fc322b936.jpeg.json","c862eaddd35a6123ce4eb3af530525b3.jpeg.json","c8702d40118aeae6ae328fe2b90937e0.jpeg.json","c8cc592e53418976b7d906ea53a20bcd.jpeg.json","c92dc36627b6ef845c0430b314baa1b5.jpeg.json","c9452a4f57b646744407a8d6f01e5e59.jpeg.json","c94ff10637406f0aa3c904f9913a7fbd.jpeg.json","c957f1c5f4873e6c3654e0e859ca2887.jpeg.json","c9af4e35587383ea25a9bebb1fc7b90e.jpeg.json","c9d7d4a01962409f8660829649a266aa.jpeg.json","c9fc3d14fe098f8a60e83ecf05072f8b.jpeg.json","cae0f91f1011966b709c653763e689cd.jpeg.json","cb579a43c90f5ac0c067523dc3a4d439.jpeg.json","cbaa4ba0460b5b82aafeed1065407d55.jpeg.json","cbbdd74c5e5ae3ee1ca65d78e1aae440.jpeg.json","cc312bc2f7d212f7ac16d680a08d132f.jpeg.json","cc3f325ba4ac7e580a93a7a21df57ddd.png.json","ccdc1da1cca2d2037f4fef94fc043853.jpeg.json","ccdc67e21001c5e546dcac8449598539.jpeg.json","cce9be1ce40f81e773a4e881ffc86470.jpeg.json","ccf563a0e70c62e3208854ef7c5b827f.jpeg.json","cd2d43a111837b154397252ae669f3fb.jpeg.json","ce382ad55ec548e681cf4a04a59a5580.jpeg.json","ce3863ca96e401fbba5adf438d98756f.jpeg.json","ce5b568520be24a630d7d6a7f9e4bf80.jpeg.json","ce65a1854e71ea6c4bb7416088713ebd.png.json","ce7318afda1c720cfa8808d4a4b512f0.jpeg.json","ce8110205ea28a8e18aaea50ac566fc7.jpeg.json","ce8519acff3b6c7877ffd964384ba7bc.jpeg.json","d03f62e47e2cc64f827d8dc3c9a56882.jpeg.json","d08e0703342cadb0cb4aa2251afd601b.jpeg.json","d0b07855a47678b6a8d7ce4552771c7c.jpeg.json","d0c5622bc33161aa3721cd7ded296c9d.jpeg.json","d0f3c17be64e044984b83b9e4aee4e3e.jpeg.json","d114f36c25a348f912acc7aed1f05d23.jpeg.json","d133c00b5989c9b453dc30b78cd9552d.jpeg.json","d148850ed7307574db17f9d965a2bb90.jpeg.json","d16a63cf6ba1a70d910a4c6689952a7e.jpeg.json","d18f1718fdc29baf281eac12b3a18b3e.jpeg.json","d1b4fdd0fe09e829944f8dad6efb0f4c.png.json","d1b72e014226de6f9b9f344e02860830.jpeg.json","d2130727129386cab1ed53a87a56e239.jpeg.json","d2e4e8808a62c404f38498faf3b86abb.jpeg.json","d310d9ddb154fbeeece93e5e85940bbd.jpeg.json","d334da1da9ff6e2975664b8005e265db.jpeg.json","d3ad82ac8936c1a68a3da54f7bd4217b.jpeg.json","d3c482aa1c424068f5e4244022b0ee49.jpeg.json","d3edfe6bab836691ec79f363a5c796f3.jpeg.json","d3f7d6da6e9ef8fd6dbbeff7b05afeaa.jpeg.json","d401f57bc3aff32e363b3168dfa86e62.jpeg.json","d44d401b12427837b14c734be51e0aee.jpeg.json","d467ab7b3d8beff8d51024d3f6ccf6c3.jpeg.json","d49ec827d0b340989f64be6fc6f996f4.jpeg.json","d4bc6f134ef7d2e087be9cf908c2a2e6.jpeg.json","d576adbe497d0859636e9362565cccb0.jpeg.json","d585bcb7110dbbd3d2c448fbd56e1de4.jpeg.json","d5af05d60ec4ecc0bfb110f58b175ab0.jpeg.json","d6330df9b0f62cc9a66b5bfc8bbc9949.jpeg.json","d657eb1e8aadf3c89cba389a6c118ab3.jpeg.json","d6913dd3374d981053042e99699aecde.jpeg.json","d69600cc178936838fb7fdb1118e7537.jpeg.json","d6a7b438e7dae8b357012f006a5853b9.jpeg.json","d78df2ab61897b99f3a08831ec348e45.jpeg.json","d7aa773a4dffafe161c0e30de3379324.jpeg.json","d802cfd3bbbc3276258ba689c6999a32.jpeg.json","d81e16b7e522222cd27a51b483c11800.jpeg.json","d87a53001c3cc399b76a638f731d2cc7.jpeg.json","d8ed135ab9d50e136394a2256a6c9d46.jpeg.json","d8f8f0507a48b76ef59060bfa9b2cf47.jpeg.json","d915c4b42c400f9bbb7ed5a713375e86.jpeg.json","d931f074be18d83bc76fad43dc165081.jpeg.json","d9573cbf00349536974d9ded8223e4c9.jpeg.json","d9c2b4df4f06e4715feff4f50a073a03.jpeg.json","d9e725406bee40d5acd6c8042031855e.jpeg.json","da14aaf7d6c01bc230a5d2c82d1c9500.jpeg.json","da233e2bad57b401c494a4f62883e34a.jpeg.json","da896edbf57d593dd39247b5a5965ca5.jpeg.json","daa446ba29c460532f352814f4026685.jpeg.json","db200ab7ac7cf86e7cdf86da6347edc5.jpeg.json","db3266e571b66e3ce100c0033710cf1e.jpeg.json","db61ad9e74d0f52204608b894dccd498.jpeg.json","db656485be6ccaa7163c9561ce8d4db0.png.json","dbbee0ed60f3d78f7f3d40c4e9f04b05.jpeg.json","dbcce7ec562b55974af45addcfcdb291.jpeg.json","dc1e1228d473d40b6e41712a5db17366.jpeg.json","dc2c97305ce4686d135c86fa48c204f0.jpeg.json","dc6f2a9dd6fc900c8b1e17c33eb9e2b1.jpeg.json","dd2a8c3d9757912e71a7b89466b0dbf9.jpeg.json","dd3c8b67685c6a59b876a3e01d393d13.jpeg.json","dd562c58de783991b44578cfcee09ee1.jpeg.json","dd5658d1decea90a9af7a37d1031f8c6.jpeg.json","ddc0ed9c03c92a1d89064132f8be0403.jpeg.json","ddef41bc01129efd1f84bb3555bca104.jpeg.json","dec14706cfee632134eb72fd3821db6b.jpeg.json","df0c46342a07183d8bc8c73fc7a8807c.jpeg.json","df2264b8511a4ceff8b708982e4cf4c7.jpeg.json","df529f96a1a27b98a412aa6507c967fa.jpeg.json","dfb5016b199c5c7a68af6fd54dff8a5a.jpeg.json","e049759552aadbb681faeea21a386ddc.jpeg.json","e065afa7e8354ff5d2fa6b8fc2e7133e.jpeg.json","e08cbdd522c38bc9622d4ac7055f2f13.jpeg.json","e09f9a594a5f082e1b5320dcc200f73a.jpeg.json","e0b13dae2ee6176ea0997a69c73edb6b.jpeg.json","e0edee1feb02e46a737d785731660606.jpeg.json","e1627a23c077a873c8b3feaa0865abe4.png.json","e1819aae68143e22365034525c067dc8.jpeg.json","e225981599523f56c082cef434378250.jpeg.json","e26b4a319348af33c3eccaae584fb273.jpeg.json","e2cbf82cd42642f86a79ad520cc1ff4c.jpeg.json","e33ebc36feb28144cc11e13a3d554625.jpeg.json","e352f13f8d13bb47833807e01f1ed4ed.jpeg.json","e3961ea3be6f5072723fa8a9b481ba35.jpeg.json","e39ec5e9d0761cb2f42b6fd951a8cdb1.jpeg.json","e3a62394af2093967325d9c48fe96e97.jpeg.json","e3e614d08c544a28ecb766e5dd2b0285.jpeg.json","e43ab595eb777ac412a9234a6fb289cb.jpeg.json","e45c0e2626ef36f047157c73dd8e043c.jpeg.json","e45cebee744e770e06250863575a61be.jpeg.json","e4fbf2a865ef90de7ba12b8a20b16ed3.jpeg.json","e506d47988a469e048f0d30c87a77269.jpeg.json","e51fef4ae33be241c98139b9e5473b0d.jpeg.json","e5578d64e93286b225f8889e57243537.jpeg.json","e5bedd47dcfbe72338ed8887326dfba0.jpeg.json","e5e536c80dd4645521d4b116a323d23b.jpeg.json","e6197415a8e971b2929c42c77eb935f0.jpeg.json","e64a0a52ca7510f358a66c826dd87703.jpeg.json","e66835e2dc2976f5f89c7b431a25b18a.jpg.json","e674947be8a06387107332df62f0f9bd.jpeg.json","e675b5f02a6ef6f054d915aa40b0376f.jpeg.json","e6a2e3f440439eea3d71efae67282fcc.jpeg.json","e6bcf7cf0fa9159a0cda27a29142de91.jpeg.json","e726e0061a9c28d232ad4a82075f9163.jpeg.json","e76165960c6acd2e981542adb051cb88.jpeg.json","e77a77e463d77b7fdd5493ba34e07ae1.jpeg.json","e786698b884afd2b4d2083f54752d13f.png.json","e78dba436cc506b491635587931efa45.jpeg.json","e7a000d9dbc511424ff1f4dba6c0d66e.jpeg.json","e7bdb14f10d4d024113702a7ca8661ee.jpeg.json","e81b86a127345c34837a0fd6faad4bb3.jpeg.json","e82a9debbbd86a16d08c7bb89d1868a7.jpeg.json","e89220fdf4a509c52ae62ec57ebeb8d4.jpeg.json","e9181e87b3fedf0371ab1d22f9e98cd7.jpeg.json","e9266d87658a25bcd0ee1a6000bf7c3c.jpeg.json","e94e1762fbdfee04b1b882cda844175e.jpeg.json","e95cc2a5919596c9411f30a5aa0533f2.jpeg.json","e970e9e2ad1ecffb03ea07bb847d244e.jpeg.json","e9b245358b4b5e1cd1dede8623db322f.jpeg.json","e9cc8329e12b6150a29bb24f7ff7baa7.jpeg.json","e9d8805725a216a4e90bc68abffee7f7.jpg.json","ea1c9a1224b1ebf21bfbcb183041967e.jpeg.json","ea5164f1625fac7fc25785146105026e.jpeg.json","ea5255de62e00538f7a5552d95ffacd2.jpeg.json","ea848887a2d74fe043f81f56b88a2d12.jpeg.json","eaad3399669a44432d88bcefb80dddea.jpeg.json","eab3e34e6ca878e5b16dac1d1e1a6dfc.jpeg.json","eb8c79d310d7054d37890d2290b85bf0.jpeg.json","ebc8c16eb8ebefa11516f6d9b2990edd.jpeg.json","ec23245f92b5a1bff7db98bac02a6cb4.jpeg.json","ece1ca7a319c25f3cfeec23fa97d2fc3.jpeg.json","ed243bb698dc4b19ce281a770034f9a5.jpeg.json","ed4f181c6c8863f2ded095700bc2c7c3.jpeg.json","ed6409e995cff02a1f5a30a024326aef.jpeg.json","edb4ec033232cc5560d21d1a91028ac6.jpeg.json","edb5618bb7c3ce70faa9cea170958efc.jpeg.json","ede4e9ca14dbb92cdaf95576103ac6d1.jpeg.json","ee08415d8550a2d87364de650a07ecd9.jpeg.json","ee6399df5a80d9cc28c7b13cec0abd1f.jpeg.json","ee819fb1e536987c8c566f62791ef8d9.jpeg.json","eea87fd1a1ddc013631567ad9bd670f8.jpeg.json","eecb407083fd3cf5c704f6b0d86ca8c8.png.json","ef3737f8de72aeb3e967b8344be0f5f0.jpeg.json","f0223dc36cfb60e12b176050796453ba.jpeg.json","f0c7529b755b21c1c7f775413203e49a.jpeg.json","f0e8b78f1f18daf76dd4fc9d04211720.jpeg.json","f11090e810ba8d57814a572d78d40b56.jpeg.json","f17b12b2416962a2d668c8899ff2f0b7.jpeg.json","f18e6c5d2c65873cf38884f41489bc0e.jpeg.json","f18f8f3e6d3d18bc98b0818ee43b32d8.jpeg.json","f1d0d1ebe22add8e060e73cf30048f54.jpeg.json","f1f48ffb9b03fdfadb469dd198aba7ff.jpeg.json","f256210bf91062ad03c442bd67e8ae5c.jpeg.json","f264ae3144218ac7e01b5c9f8c8ead0d.jpeg.json","f2cc05d2d2d65f4fd123fc30197d94d3.jpeg.json","f2db964b5fc68cef96db0381a3b3ba77.jpeg.json","f2edefb4489da7af9e3db508a4cdad48.jpeg.json","f338b2e04ed7dd590a786fa7a0466b3c.jpeg.json","f344d8a79230d6eaa5a79d6fe937807e.jpeg.json","f3460f571c21ba0024630a81f7bfd646.jpeg.json","f3c689b454a7ec1c0d4a656e5c73627f.jpeg.json","f4137bf001ab4487db97aa7f8c02f02c.jpeg.json","f485d8be1fa45e835d1180975a32cf0f.jpeg.json","f4d42e49993c2ea60788c5080b8bfd60.jpeg.json","f4f73b8baef2980de373858ae764234b.jpeg.json","f550e853f302025076d54ebd00ce5be9.jpg.json","f5604bfa425414845c84cbdc5f374d01.jpeg.json","f5abe1eac1b1a79fa194e6a6afec45fc.jpeg.json","f5d8576e4c189d11d057a6412dc81171.jpeg.json","f652d7303b131cd7531f1b13e8aca173.jpeg.json","f68d939fda43324776d14209edcee17c.jpeg.json","f690d9f88c5b7ec8a044254ff7949f6e.jpeg.json","f692676863cc9c9e570d6295e4af2399.png.json","f69f28589b5054d716184203b82fba8a.png.json","f69f3b6b7b6677a83f4baacf5ce7e143.jpeg.json","f6e5b7b6a9c9f9ccfeee2a074a0c1203.jpeg.json","f743c161a800793704d38252219216ce.jpeg.json","f792a9a7c74112b35edf7047f40f1790.jpeg.json","f7a32d5c00985cee6a728fd80f51955a.jpeg.json","f7c008134f887ca820e1c8d689038f2c.jpeg.json","f7d9c395b3900300c075fdf81f2312d1.jpeg.json","f7e8ea6ced92054dea82faebbe3fa243.jpeg.json","f80a0c5b5232ead4c9b22a01c17e2564.jpeg.json","f8326f743b6358281f3e0d6660e7f0ef.jpeg.json","f8f8c7d3d03bf90018b2666bf213077b.jpeg.json","f96bd76914f6f1ae7531f29c42a57eef.jpeg.json","fa26736dbe66222fdded414327ada3a3.jpeg.json","fa614d794e63113c22a47b5565423f9c.jpeg.json","fad737b5bd14428d8df30c7ac075b522.jpeg.json","fb17166c127c726b7a4d83f39860e307.jpeg.json","fb33c6ab0b31786fda22e2dfcca19533.jpeg.json","fb6e4592d5509d6a7e4d8b2809a883cd.jpeg.json","fb9423fb2527a12f984ed02a6b4fa0f0.jpeg.json","fbab704d149fca7dd1dd0d51ca8d48f2.jpeg.json","fbae3e2b20b46023ee996ca28ba376b5.jpeg.json","fbb044dbb5d3a328c9cd3f7bbd643561.jpeg.json","fbb1def90cf2d501e46d65e3a28c2c06.jpeg.json","fbbcddc555e1c1fcca7b2e427642fc43.png.json","fbd3b2c5b3c8ba8942a4e6c8bc32bf12.jpeg.json","fc0b12f2f06e054d3261db3932601265.jpeg.json","fc6bc2eb21708b3c8d645f709ef63927.jpeg.json","fc94c5366b7bdb40160f71f6d59ed727.jpeg.json","fd859c72c1a38cb382494882b1c502cb.jpeg.json","fda53bb004e548379f0296b9dc9769f1.jpeg.json","fe95e2167be353aac3ce9dae1c522784.jpeg.json","feb176b3f242f9f05b4fd137044be85a.jpeg.json","fecf9c5d753e1c29ce4dbad456290265.jpeg.json","ff548c8e48c6f3a687e6f406a7d0e47a.jpeg.json","ff71eb172eba1019e7cb579625089669.jpeg.json","ffb4e6b33755f68cc0ca9b93aa3af654.jpeg.json","ffe6f1ab63d429fa0e090ae78fb79301.jpeg.json","6ff8b403d437ec9210aed005f4b7d5de.jpeg.json","701a7573675975ffa17b5958d994da04.jpeg.json","70305901c9438c83241ded45a614a05a.jpeg.json","703dbe721760333b14f219b071af0258.jpeg.json","70644f2d900f3e3df8a094514378c8dc.jpeg.json","707c483fbfa6e10ea272052dbac91fbf.jpeg.json","70a226ff7b913d022f432a6262c432d5.jpeg.json","7103f3c81d7b9b5f47607f5517aa3210.jpeg.json","71040961f9178cb641ee2bda77daa1f1.jpeg.json","7153af94856dc09d65ca940b407ae437.jpeg.json","715f772a6a2306fd040598065dbe80e8.jpeg.json","71ec70fdebfe01fc82c055135e73e5aa.jpeg.json","71f38f7c6fd203dd193725a6575eb84f.jpeg.json","72058ea0b2f0ea32f6c1534dd0f4259a.jpeg.json","73203f4c115da50ef1bc411a2a043d22.jpeg.json","748311686961c891f4afbd8a373c02e6.jpeg.json","7531f8ba7c212c6ac13bdb1d6fc2a19f.jpeg.json","75349eb64fdd5d0fbf005df5c7594f07.jpeg.json","753681fbf3d3b2808b8c889f215feb5c.jpeg.json","755cb42ce7f1df46a1b893ea5d26cf26.jpeg.json","75f27276f628e427a2bf80aa7f9792e3.jpeg.json","75f2ace4925a2a749d6942885766f036.jpeg.json","76016b537ef69992905dad5c40289e21.jpeg.json","7624165ba78d723987fd7734ba6af201.png.json","76490fbac3548a57ce43fed12b29d5c6.jpeg.json","770ce887b2dc0c854d100ce8f11d30f7.png.json","77e3e59d693d7071abc68c4428802bbf.jpeg.json","7803ba050aa011496bb49f45ae395617.jpeg.json","787fd9830b9534d4603c704921312dc5.jpeg.json","78aa83fb0a9cd9148258af5201be89ec.jpeg.json","78b8ccc845900d7795b47d08618a0cd6.jpeg.json","78d5b85760cc495385c47f9ae486b37a.jpeg.json","78f3dad6dd4ace4ce16c1520c0c79a1a.jpeg.json","7941a02b69f84c5fe9faaa94510b2860.jpeg.json","798fd92d7074c29049fc772bdcb371ab.jpeg.json","79a1d77dd1380dbd15299ed72dfde5de.jpeg.json","79b0035db4d98d20736f354de4b7afc9.jpeg.json","79d6ffadb928a9aef20f1d3cd68810d6.jpeg.json","79f2078ec3fa4131062ec733c8d3ca10.jpeg.json","7a364f7e14fe9106787773b4bcb5cfc0.jpeg.json","7a6b1f70f5bd8e18f18c0c17a92848d3.jpeg.json","7b68ecb287ac3fdc8af59ed8d543848f.png.json","7b741584c0f339e91ecb7413c306d084.jpeg.json","7b7996d595b2df7ef2fd1a5502bb83cc.jpg.json","7b90dc94a75f26bd402735b26f45d017.jpeg.json","7bdc2b31b0632aff9922497b144972b3.png.json","7bdf2d802946995231b14274b2405ba7.jpeg.json","7c4c6e1c4cd562c7fc692fb3740dd1fb.jpg.json","7c5ce28f52c8befd9f86e6781a02b8c0.png.json","7cca72949282cc482fbb57b6230a92a7.png.json","7d3b0acd6672f0e36bd70153e53a07a8.jpeg.json","7d3d5a022ad59550f3ef259236e826bd.jpeg.json","7d514ff53e1150d4c1744aac4a2009ff.jpeg.json","7d52f80060a528bb7b2544c04baa65fb.jpeg.json","7d78e2881e3a2f454ce2cf822ca3e6af.jpeg.json","7e0ddfcc80d573770cd39f5853ec8daa.jpeg.json","7e27f9f4225299a55f786ab6d8eed544.jpeg.json","7e6296f2e01c6211eb7c5ba74ac3f4f7.jpeg.json","7e68577d4a092b66b009fe16f89b93ee.jpeg.json","7e9247a43ee9469014d72cfb7c6d23b9.jpeg.json","7eb014c79aa03c18f11a58ea58612c19.jpeg.json","7f9b22a160f257d7b4d3a80c09bb38ac.jpeg.json","806a1e4b1ed5a58f6b541fddad7f6a21.jpeg.json","8071807fba2c90fd85714cb40048c71f.jpeg.json","80787772d05ff9f8e58fc3e276df2c8f.jpeg.json","80a32fbe55da9c650fad4211badb09dc.jpeg.json","80ec0adfb26fc2d9e702382685560682.jpeg.json","8144caa0d00ba83634057866be21bb43.jpeg.json","818da784c33a36dade6ddbb4acb62a96.jpeg.json","81b0e7e46da0e150b295ee7a6d3e1d4f.jpeg.json","81e4ba2ee066ff09ce661cd1d9750ef2.jpeg.json","8202cf2736f818a34875d753d326b2e9.jpeg.json","820e93571774600596cbf7d152c12bb5.jpeg.json","828dbd12d2ed9d0ab59c39e0cf52405d.jpeg.json","82d4d7b29cdf9d02883002309f0a2b51.jpeg.json","831b1a75dbf1f605ac934cac47f2f6d8.jpeg.json","834bf7852cb1b14d1e1a3ba2a218bcda.jpeg.json","83631e520695495b92607c43168db581.jpeg.json","83c35f6fdf42ab14105836ede211a16c.jpeg.json","83e41860c05866c0e3918652417f8e45.jpeg.json","83fa5b54a47101cf0f63190eb24e1cd1.jpeg.json","841e1d2566656bded15d5e88c57c030b.jpeg.json","8441ce1f48fb9f42c1dc1965cc8c321f.JPEG.json","8475b7301e3b286871edd56452fd8e3f.jpeg.json","84914d767fffb2ba4fbeb2e04bf59d0b.jpeg.json","8494de96ec43cec4d3d2c33db672f403.jpeg.json","849ba4e71a31608b8dfa3407f50bf9a5.jpeg.json","854b6eb006aedb7c6ce72bdf5a078d51.jpeg.json","85838401549e4a1f22555627f44cc955.jpeg.json","859df8e5277a1c70815df5a5ca9ecffb.jpeg.json","85f59c64d02330962fbcfa26d7abfef7.jpeg.json","86040943da555695cf999b4ab9d03bb0.jpeg.json","866f7191af186f97a1b8c9623d73a82b.jpeg.json","86854f0064b17b918f90bc57a1980437.jpeg.json","87b431be307a704329f32ee4a63ff10d.jpeg.json","87ca351b10e672fbb1b3dde77e5525e4.jpeg.json","87df1901f592db9f071adb357b3c2d42.jpeg.json","884326038ea0d8ab9a9aa7847a87b73b.jpeg.json","886ad98d2d123c4192efdc0a00df4635.jpeg.json","888a1119743c85ff9120870f1c10fa53.jpeg.json","88badd516a416a5dd1528759d81dfa2f.jpeg.json","892e21ec3aa8c1ed1765a431b2b68482.jpeg.json","8976d9fc2c5b2ababda1406d043bdb74.jpeg.json","8a4e32e64a7dd08e29804623d4b96547.jpeg.json","8a59d79dae544c9046fd12f476c97165.jpeg.json","8b4bd50543e08290446d32a2a12f6ffe.jpeg.json","8b56fb59e1e1fd22a2df2013dd438c35.jpeg.json","8ba2afe0a859ca8156b8310badca4c9f.jpeg.json","8bb8ab40226c6368f143bf29c1bbd5a6.jpeg.json","8bdd8a6919661ea6cc729a31ab0955d5.png.json","8c3b0c73ce253840febcd51f3b8e8ce6.jpeg.json","8c5f38f5ab076909e00b447248d00687.jpeg.json","8c9496dc6cef4a0983fb3973b55b02a3.jpeg.json","8cb75fbf0624fd5cd086a49b1be8b2bf.jpeg.json","8cbe52a94b0701078098008e8f95c6ed.jpeg.json","8ce2b5a1afefdcbdd3462813a55090df.jpeg.json","8d0a85407a1eb4ed4162030d482275f9.jpeg.json","8da7129adbc4cb11a0387d3542fcf615.jpeg.json","8db1f21462137313d55f1a162ae1dd76.jpeg.json","8ddba860f36f70d82496d0f9d0b88bf1.jpeg.json","8df2cacbafde9eb289e229ea80f156f9.jpeg.json","8e2a21e0bf7fcb73adebe3dc760aac3b.jpeg.json","8ec4f340ff26d4d3685d55a9e51664a2.jpeg.json","8ee0a6ee1d87e42c377ff2b96c282f02.jpeg.json","8ee56a12c1f8929e0af9ed466e798381.jpeg.json","8f06cd4ff26dec9e56ccd07220446075.jpeg.json","8f0e47302b522ed20a7520c5b4a78f7c.jpeg.json","8f618dfa969081acd3012b296e2201bb.jpeg.json","8f9b1e321b35dba0d6942180979cb66d.jpg.json","8fb501f2238f9eb0d2d1026758cd15fe.jpeg.json","901c453cd81a877ace73eb0ad746a2ed.jpeg.json","9032a049b9745b29b5435ed253a49ff5.jpeg.json","90a2d791c096a54f78f0b8a04bbe8da2.jpeg.json","90a7881307bd13eb4bc9be47aeb20f28.jpeg.json","90c4545574be9de286262b7eff20adac.jpeg.json","911f4132ffbd482d9eb7f85f66b2ecee.jpeg.json","912f85ed5abd31fb7adb24c2bd8859d6.jpeg.json","91b1be0ae205888ac0b77ea6320e2ca1.jpeg.json","91d9f5ab78276a70f0972e3d8a15f745.jpeg.json","91db4d6a662b759a72d48707ba43e34b.jpeg.json","91ea2f5359cc19280d96c101bfe22759.jpeg.json","921a572517b61ec2d8096256b22fbeb2.jpeg.json","9236e75942f85a5a014e93aac73dfeff.jpeg.json","927810f080a7df039fb429fc1034c146.jpeg.json","92d8769ab1ac7919e46086e267349ece.jpeg.json","93f5fa9beb051efc61b931463b3362a6.jpeg.json","941afb668b7b5432667fc0cccfbfb641.png.json","941c9453c25b97cf56b81daf0fa3e2f1.jpeg.json","943cb181858d995be5225d1419e50c5c.jpeg.json","9441715282a08469d681a97ccd4b233f.png.json","947ddefa87430eaf0c40729d0fc7410f.jpeg.json","95355a69b7b39c60259719705cea9900.jpeg.json","9558b20aaeb1b1c833e20ccdf413fbf7.jpeg.json","957cad737d5e939581f3e8aa027233e1.jpeg.json","95c563f18662d1987996cdb2ead4624d.jpeg.json","95f5e3159aaebf5831ef0409cf274075.jpeg.json","9639be2d1eff02ec7162a823e669c44f.png.json","963aef3fff65546cb9803cfaf889ca34.jpeg.json","96440c01c37a3a594dd42ef21431f3a9.jpeg.json","96450269ec8e68b0108e8b5b4d145c55.jpeg.json","965c8a1ffbe3fc9951ee1ef5e34c6d37.jpeg.json","96ab72a688f57d0de88c71ef0bb6cfe7.jpeg.json","97738f79dad5424d6e1a440a3a461532.jpeg.json","97b8f38e9cf3adbf6657b25ce1a7335c.jpeg.json","9813582798fab064746a1b94a0f38f24.jpeg.json","98ccbfbe45bc1bcfbc3f40ec39a5f41f.jpeg.json","98f688756e56414609e0e27d39701c56.jpeg.json","99366e7ee9249b7dff69856eea8bd9a1.jpeg.json","993834e21792dfc43bfa9dd6bac01013.jpeg.json","997c7bd5d32ec45ea86a13b11444a59e.jpeg.json","99e22017232b6de400542df046a784ce.jpeg.json","99efc5d7227e94e8fa5db14657440d93.jpeg.json","9a41ce7c515fca5f0e3d852553bf7401.jpeg.json","9a586f9a33601864bd887e6a3fc15306.jpeg.json","9a9adfe19d6c60cf14406ee7cc4ba8b9.jpeg.json","9af6d1a0b444caef74043671fecc5a2d.jpeg.json","9b01e5e1794f2049e77f62be7d61fefb.jpeg.json","9b09ec48138fcc10bb6546e561550205.jpeg.json","9b71abcaaf16fb9544330871c1997c61.jpeg.json","9b72b7e9c7a2eba1225a2b94bf6c075a.jpeg.json","9b74584ba01059866029f10e04a28d86.jpeg.json","9b9f0306056eba83ee63fe13309d6416.jpeg.json","9bc01accf2bd998927e35eac49fbe701.jpeg.json","9bc603c6f4135cd85aadfb0ce280cf2e.jpeg.json","9c11c8da895be5e51f6f376bd565a19f.jpeg.json","9c1fa32f357d296e353e8eaa1c0de5d5.jpeg.json","9c417c45c7c2f840657b66c5c6445e1f.jpeg.json","9cfe50b051a74f0d34d47b2c2fc289ff.jpeg.json","9d833f506e39a9b256b7895ca4f4a12e.jpeg.json","9e6788e49029872d6af65628e47dfb42.jpeg.json","9e753701f51917034425ec42089227d1.jpeg.json","9e94808c0f99648e03ca95de2db13aaf.jpeg.json","9ed3aeff287805318f9158e973a84d80.jpeg.json","9eec8bf302a0b554a97ec051326e207d.jpeg.json","9f0354346e5f10f3a9cfa52298f988af.jpeg.json","9f470ac5164bde63ef3be45e9710e55f.jpeg.json","9f814896cba97063fe41f14519f3e3a9.jpeg.json","9fa8ba16bdb2e9866dc88edb3d6d4dbe.jpeg.json","9fb879fc041e93f09f4d4220f54ac496.jpeg.json","9fc6ace3668c28d8843ce3faeb967a81.jpeg.json","a11192978ca202719cd557e43ed49985.jpeg.json","a2985d563eeb38e0cd9853d9874d2186.jpeg.json","a2a3ec05647e4708b65b988b72831a99.jpeg.json","a2c345b97444160ca424f69ab2967424.jpeg.json","a2e6f5eb92c42c6c081375193e4016c0.jpeg.json","a3078d34dedd5f24001b5e0c629e53d1.jpeg.json","a36349468a0f3303e247c3bf1a3624f9.jpeg.json","a36cc42841175f14579ff80d9c21c7f8.jpeg.json","a375ca2f02bce027332edc0b59412819.jpeg.json","a43687a677c502eda3825a8e22504865.jpeg.json","a4a36933aa80f535d0d26beef6d534df.jpeg.json","a4fdc6f8ff15a377a7bbef763ce056b4.jpeg.json","a50e6b1510a72ee28a09f8f89201a515.jpeg.json","a51cf5fbcebabdb1b9b108349ef4c6e5.png.json","a56531bd2a7405fa41ec2c89a7740772.jpeg.json","a56e1286a9516e4c2a1ef113ab6b7f9c.jpeg.json","a6284ea25b31c6d9744e30e430ba22bb.jpeg.json","a64bbd93b3c31558166c74267c4ac73e.jpeg.json","a701b6c44062f5392e9375b3a70211a1.jpeg.json","a7582f2944bf8b2dbffb613d5e81911e.png.json","a768268f66f7ef309cb5adce6932e916.jpeg.json","a84850991d22f6dbcdac714225d2ed5d.jpeg.json","a9168a323a9861c5d44a27ca12cdcba3.jpeg.json","a99c7bbde59c831006c02ba047b3895b.png.json","a9a3c66d69397aabc475dea5b3d46fd7.jpeg.json","a9c70118fa87e20076152f8df3303e0b.jpeg.json","a9d60d9edf124b2a6e0371a9d4b1105a.jpeg.json","aa21c7f303b2b5e161883704901ca315.jpeg.json","aa248053b9f210e59cebe829e3c9b7d5.jpeg.json","aad7d6917fb9297e3647719963062f23.jpeg.json","ab10e5cb60fe670021c78c78adbab5df.jpeg.json","ab613048422816fdf3c15608065a29a1.jpeg.json","aba73d12fbe9f8e1b5477a197e07dfb3.jpeg.json","abdd45bcbb22860d36069b0019247198.jpeg.json","ac1743e4e6991795d887cdf34e7f68da.jpeg.json","ac294fcb10136cc069ca76fea18b1012.jpeg.json","acd9f38ee4702a6539408c69f593e091.png.json","acee560d56cf1e1c3be2fe5cbce85311.jpeg.json","acf27a1a77b1c64826d23fdd8b314de4.jpeg.json","ad8afd075c3ba5a3ffccb866223f5edb.jpeg.json","adcfa03f6ca674ae5c597dc120e76a89.jpeg.json","ae35ae8e2be13b5b45595c274b1cf7eb.jpeg.json","ae452077f9971d9921a7afb3922048cd.jpeg.json","ae6df2a0f14877bc5461abc3340e50c8.jpeg.json","ae6fc6f56c8727760e79d8c2b68036f2.jpeg.json","ae6fe194927e756df7fe631522f1c610.jpeg.json","aef1eeb4fd79ccf182859a3246c1d8d6.jpeg.json","af201f933bac670b2dc2350e86cee5c1.jpeg.json","af3f7fde9d22f417cdc0df4753e1cb50.jpeg.json","af7a777c97af1ad15984c4a5301ebf33.jpeg.json","afe3e53779e69ca42aab7f1791a5da39.jpeg.json","afe5047af1f74252f2c9c60992b91056.jpeg.json","b009a59ff072a43ea399a6e5ec517abf.jpeg.json","b099b4fb9dbe29bfa7c7b6be1940b614.jpeg.json","b0d92bfe4af109347d4463c46239d39e.jpeg.json","b1507b84f010a6a9422dfc64fb543487.jpeg.json","b16ed70eed5fb43186894fe692e7d475.jpeg.json","b29f34f5ae0a0809fcfa977b5ad9da87.jpeg.json","b2b7b90e86c5825806925d455e5bb216.jpeg.json","b34a507a4e4cfb5ebeb22839e3720d9f.jpeg.json","b369335407124257410680bc75a29766.jpeg.json","b3732c175eb8b6f9cd1ae9045166e16f.jpeg.json","b3e5dfed31c4e7bc8effbacf61ff82c8.jpeg.json","b40755769b8c56c01dd68b0dea7845cc.jpeg.json","b42335a9da636e4ad3cc00d3cc4ba05d.jpeg.json","b426df0cc4acc34ec5af159e8d42efd0.jpeg.json","b43941538c97ed7b14e4b5247f791e41.jpeg.json","b449be070cb8539436be650255bda41f.jpeg.json","b4a8395a54e094ee1250860e34c2ec07.jpeg.json","b685e3383780ffeacb8e6297cf0ae4b3.jpeg.json","b685e5f2be5e93d34e8041c66c1f1e03.jpeg.json","b6d011b0bb1a62abaab18b468449f784.jpeg.json","b6d427bdcb4ccd66b43f2bcfeea2192b.jpeg.json","b6ee014f5d623c6cca88732ac6d41c7c.jpeg.json","b71b3c9a493a1f63adb497cbf6cbfb2a.jpeg.json","b7355aa0b18badfff95d5fd5af6b8268.jpeg.json","b7616310f7f218dd57906e6e0cfcafa5.jpeg.json","b77a8535c138319408a0c757fffb1d79.jpeg.json","b8688ea021519777f61f124a419fdb29.jpeg.json","b86b50c9f0ce0b64431c4081a2f573dd.jpeg.json","b8ae48811862229e89f88794a2f02ddc.jpeg.json","b8cf36cfc7f9ae5a48714e475b25f164.jpeg.json","b945150c2ec585f3d7bd0a9102f8540d.jpeg.json","b95079e842b08ff260630efe2fe7e141.jpeg.json","b99f523d94bb7df35b7d028fdf94e988.jpeg.json","b9ccf86f5471af4d6428f873d8d958a8.jpeg.json","b9dc2bea6f9241ebeb5fc7e7ff8c04c6.png.json","ba1071f184a84439df9f6114b6e70472.jpeg.json","bae4b514eed98090d7e9aa81e0b03de2.jpeg.json","bb41fb0ae0d6f9a036a56def9924f172.jpeg.json","bb8648702ffaba9723ce164f24ca16d2.jpeg.json","bb8e78ad2c05150f55ce85704c4023e5.jpeg.json","bb8ec58ee700b811a205f577450160f1.jpeg.json","bbbef3b7c7943694c0969e11c35179ad.jpeg.json","bc390abe92c933d67427607a90c5d8e0.jpeg.json","bc3c5a9d4543ed7f5795582a632b20ce.jpeg.json","bc437be6d3ee896e081a752a7f7dd9f1.jpeg.json","bc6a12c985b8ee2d74d4c9bfb9918791.jpeg.json","bcee2f70904deff9fbfa47186c382dab.jpeg.json","bd78ced66d4e9939f7c4dca321c5cc64.jpeg.json","bd88325d1e6c52f87981f7b88d9f4efe.jpeg.json","bd92ceabd72484a556792c0efefc9071.jpeg.json","bde5e4571fee9681d6e7f2e089b5e4c5.jpeg.json","be26e439c5348cbcf8802fb66ea10ade.jpeg.json","be5c23545eb93575e7060f01f7ddcf88.jpeg.json","bf98d5ffa7eece6b3e72683e29c184f7.jpeg.json","bff24593f4c61fd4cfe25a63f019822c.jpeg.json","bffbb92b56845b871a727abb6e0cc9b7.jpeg.json","c03a255bb791b438134bcc211bcfc229.jpeg.json","c064de930c0c79937e528b0142a30da6.JPEG.json","c097e31ab213847094af00c163c27223.jpeg.json","c0bc62c518d17b377e60b8c6c046b862.jpeg.json","c1505ff920b8712d5d221e9ffe70709b.jpeg.json","c17e47a687117911eaaab9eca760c4eb.jpeg.json","c17fab603ec0c74155b1636c4418696d.jpeg.json","c1b15b94b3dfb7c964cc0027c8258ee7.jpeg.json","c1eb726692d48e56505f69d1d5e8f93e.jpeg.json","c238aea63cbddfbd121c944efc62af6b.jpeg.json","c25288d3b882fc081156387819203347.jpeg.json","c2e97bd83465c1013fb449aae9c139ba.jpeg.json","c311ca4d933f354d9a1b7e1a0ce5a158.jpeg.json","c37fcd546f12a1027b3aaa98ca49b567.jpeg.json","c4709b17ce7ae094ef8d148567aafc12.jpeg.json","c4dd7333fd768946314126dbaa209044.jpeg.json","c5428858d364875f454de16ea20209ef.jpeg.json","c54ae5db16c59a7689c3181b794776c5.jpeg.json","c57c739aa8fdffc25b3df14cb5e8cdf2.jpeg.json","c5c9c9f3078b29003c3eed19a9043419.jpeg.json","c6aca7bf440fd6f83df844419739ff34.jpeg.json","c6bf4ff2724a16eba0c61405e8739b03.jpeg.json","c6f987545f6ac44a7fae4b0d90c1cc22.jpeg.json","c763aefb9189781ee2fb49768f06e2e8.jpeg.json","c7988e0dbf6876b324824d8d58103fba.jpeg.json","c7a5775839e1c03994860dc36bac13f4.jpeg.json","c7ff714283d234f96601bcfd20c3ec03.jpeg.json","c839ef18856b89f21e79652fc322b936.jpeg.json","c862eaddd35a6123ce4eb3af530525b3.jpeg.json","c8702d40118aeae6ae328fe2b90937e0.jpeg.json","c8cc592e53418976b7d906ea53a20bcd.jpeg.json","c92dc36627b6ef845c0430b314baa1b5.jpeg.json","c9452a4f57b646744407a8d6f01e5e59.jpeg.json","c94ff10637406f0aa3c904f9913a7fbd.jpeg.json","c957f1c5f4873e6c3654e0e859ca2887.jpeg.json","c9af4e35587383ea25a9bebb1fc7b90e.jpeg.json","c9d7d4a01962409f8660829649a266aa.jpeg.json","c9fc3d14fe098f8a60e83ecf05072f8b.jpeg.json","cae0f91f1011966b709c653763e689cd.jpeg.json","cb579a43c90f5ac0c067523dc3a4d439.jpeg.json","cbaa4ba0460b5b82aafeed1065407d55.jpeg.json","cbbdd74c5e5ae3ee1ca65d78e1aae440.jpeg.json","cc312bc2f7d212f7ac16d680a08d132f.jpeg.json","cc3f325ba4ac7e580a93a7a21df57ddd.png.json","ccdc1da1cca2d2037f4fef94fc043853.jpeg.json","ccdc67e21001c5e546dcac8449598539.jpeg.json","cce9be1ce40f81e773a4e881ffc86470.jpeg.json","ccf563a0e70c62e3208854ef7c5b827f.jpeg.json","cd2d43a111837b154397252ae669f3fb.jpeg.json","ce382ad55ec548e681cf4a04a59a5580.jpeg.json","ce3863ca96e401fbba5adf438d98756f.jpeg.json","ce5b568520be24a630d7d6a7f9e4bf80.jpeg.json","ce65a1854e71ea6c4bb7416088713ebd.png.json","ce7318afda1c720cfa8808d4a4b512f0.jpeg.json","ce8110205ea28a8e18aaea50ac566fc7.jpeg.json","ce8519acff3b6c7877ffd964384ba7bc.jpeg.json","d03f62e47e2cc64f827d8dc3c9a56882.jpeg.json","d08e0703342cadb0cb4aa2251afd601b.jpeg.json","d0b07855a47678b6a8d7ce4552771c7c.jpeg.json","d0c5622bc33161aa3721cd7ded296c9d.jpeg.json","d0f3c17be64e044984b83b9e4aee4e3e.jpeg.json","d114f36c25a348f912acc7aed1f05d23.jpeg.json","d133c00b5989c9b453dc30b78cd9552d.jpeg.json","d148850ed7307574db17f9d965a2bb90.jpeg.json","d16a63cf6ba1a70d910a4c6689952a7e.jpeg.json","d18f1718fdc29baf281eac12b3a18b3e.jpeg.json","d1b4fdd0fe09e829944f8dad6efb0f4c.png.json","d1b72e014226de6f9b9f344e02860830.jpeg.json","d2130727129386cab1ed53a87a56e239.jpeg.json","d2e4e8808a62c404f38498faf3b86abb.jpeg.json","d310d9ddb154fbeeece93e5e85940bbd.jpeg.json","d334da1da9ff6e2975664b8005e265db.jpeg.json","d3ad82ac8936c1a68a3da54f7bd4217b.jpeg.json","d3c482aa1c424068f5e4244022b0ee49.jpeg.json","d3edfe6bab836691ec79f363a5c796f3.jpeg.json","d3f7d6da6e9ef8fd6dbbeff7b05afeaa.jpeg.json","d401f57bc3aff32e363b3168dfa86e62.jpeg.json","d44d401b12427837b14c734be51e0aee.jpeg.json","d467ab7b3d8beff8d51024d3f6ccf6c3.jpeg.json","d49ec827d0b340989f64be6fc6f996f4.jpeg.json","d4bc6f134ef7d2e087be9cf908c2a2e6.jpeg.json","d576adbe497d0859636e9362565cccb0.jpeg.json","d585bcb7110dbbd3d2c448fbd56e1de4.jpeg.json","d5af05d60ec4ecc0bfb110f58b175ab0.jpeg.json","d6330df9b0f62cc9a66b5bfc8bbc9949.jpeg.json","d657eb1e8aadf3c89cba389a6c118ab3.jpeg.json","d6913dd3374d981053042e99699aecde.jpeg.json","d69600cc178936838fb7fdb1118e7537.jpeg.json","d6a7b438e7dae8b357012f006a5853b9.jpeg.json","d78df2ab61897b99f3a08831ec348e45.jpeg.json","d7aa773a4dffafe161c0e30de3379324.jpeg.json","d802cfd3bbbc3276258ba689c6999a32.jpeg.json","d81e16b7e522222cd27a51b483c11800.jpeg.json","d87a53001c3cc399b76a638f731d2cc7.jpeg.json","d8ed135ab9d50e136394a2256a6c9d46.jpeg.json","d8f8f0507a48b76ef59060bfa9b2cf47.jpeg.json","d915c4b42c400f9bbb7ed5a713375e86.jpeg.json","d931f074be18d83bc76fad43dc165081.jpeg.json","d9573cbf00349536974d9ded8223e4c9.jpeg.json","d9c2b4df4f06e4715feff4f50a073a03.jpeg.json","d9e725406bee40d5acd6c8042031855e.jpeg.json","da14aaf7d6c01bc230a5d2c82d1c9500.jpeg.json","da233e2bad57b401c494a4f62883e34a.jpeg.json","da896edbf57d593dd39247b5a5965ca5.jpeg.json","daa446ba29c460532f352814f4026685.jpeg.json","db200ab7ac7cf86e7cdf86da6347edc5.jpeg.json","db3266e571b66e3ce100c0033710cf1e.jpeg.json","db61ad9e74d0f52204608b894dccd498.jpeg.json","db656485be6ccaa7163c9561ce8d4db0.png.json","dbbee0ed60f3d78f7f3d40c4e9f04b05.jpeg.json","dbcce7ec562b55974af45addcfcdb291.jpeg.json","dc1e1228d473d40b6e41712a5db17366.jpeg.json","dc2c97305ce4686d135c86fa48c204f0.jpeg.json","dc6f2a9dd6fc900c8b1e17c33eb9e2b1.jpeg.json","dd2a8c3d9757912e71a7b89466b0dbf9.jpeg.json","dd3c8b67685c6a59b876a3e01d393d13.jpeg.json","dd562c58de783991b44578cfcee09ee1.jpeg.json","dd5658d1decea90a9af7a37d1031f8c6.jpeg.json","ddc0ed9c03c92a1d89064132f8be0403.jpeg.json","ddef41bc01129efd1f84bb3555bca104.jpeg.json","dec14706cfee632134eb72fd3821db6b.jpeg.json","df0c46342a07183d8bc8c73fc7a8807c.jpeg.json","df2264b8511a4ceff8b708982e4cf4c7.jpeg.json","df529f96a1a27b98a412aa6507c967fa.jpeg.json","dfb5016b199c5c7a68af6fd54dff8a5a.jpeg.json","e049759552aadbb681faeea21a386ddc.jpeg.json","e065afa7e8354ff5d2fa6b8fc2e7133e.jpeg.json","e08cbdd522c38bc9622d4ac7055f2f13.jpeg.json","e09f9a594a5f082e1b5320dcc200f73a.jpeg.json","e0b13dae2ee6176ea0997a69c73edb6b.jpeg.json","e0edee1feb02e46a737d785731660606.jpeg.json","e1627a23c077a873c8b3feaa0865abe4.png.json","e1819aae68143e22365034525c067dc8.jpeg.json","e225981599523f56c082cef434378250.jpeg.json","e26b4a319348af33c3eccaae584fb273.jpeg.json","e2cbf82cd42642f86a79ad520cc1ff4c.jpeg.json","e33ebc36feb28144cc11e13a3d554625.jpeg.json","e352f13f8d13bb47833807e01f1ed4ed.jpeg.json","e3961ea3be6f5072723fa8a9b481ba35.jpeg.json","e39ec5e9d0761cb2f42b6fd951a8cdb1.jpeg.json","e3a62394af2093967325d9c48fe96e97.jpeg.json","e3e614d08c544a28ecb766e5dd2b0285.jpeg.json","e43ab595eb777ac412a9234a6fb289cb.jpeg.json","e45c0e2626ef36f047157c73dd8e043c.jpeg.json","e45cebee744e770e06250863575a61be.jpeg.json","e4fbf2a865ef90de7ba12b8a20b16ed3.jpeg.json","e506d47988a469e048f0d30c87a77269.jpeg.json","e51fef4ae33be241c98139b9e5473b0d.jpeg.json","e5578d64e93286b225f8889e57243537.jpeg.json","e5bedd47dcfbe72338ed8887326dfba0.jpeg.json","e5e536c80dd4645521d4b116a323d23b.jpeg.json","e6197415a8e971b2929c42c77eb935f0.jpeg.json","e64a0a52ca7510f358a66c826dd87703.jpeg.json","e66835e2dc2976f5f89c7b431a25b18a.jpg.json","e674947be8a06387107332df62f0f9bd.jpeg.json","e675b5f02a6ef6f054d915aa40b0376f.jpeg.json","e6a2e3f440439eea3d71efae67282fcc.jpeg.json","e6bcf7cf0fa9159a0cda27a29142de91.jpeg.json","e726e0061a9c28d232ad4a82075f9163.jpeg.json","e76165960c6acd2e981542adb051cb88.jpeg.json","e77a77e463d77b7fdd5493ba34e07ae1.jpeg.json","e786698b884afd2b4d2083f54752d13f.png.json","e78dba436cc506b491635587931efa45.jpeg.json","e7a000d9dbc511424ff1f4dba6c0d66e.jpeg.json","e7bdb14f10d4d024113702a7ca8661ee.jpeg.json","e81b86a127345c34837a0fd6faad4bb3.jpeg.json","e82a9debbbd86a16d08c7bb89d1868a7.jpeg.json","e89220fdf4a509c52ae62ec57ebeb8d4.jpeg.json","e9181e87b3fedf0371ab1d22f9e98cd7.jpeg.json","e9266d87658a25bcd0ee1a6000bf7c3c.jpeg.json","e94e1762fbdfee04b1b882cda844175e.jpeg.json","e95cc2a5919596c9411f30a5aa0533f2.jpeg.json","e970e9e2ad1ecffb03ea07bb847d244e.jpeg.json","e9b245358b4b5e1cd1dede8623db322f.jpeg.json","e9cc8329e12b6150a29bb24f7ff7baa7.jpeg.json","e9d8805725a216a4e90bc68abffee7f7.jpg.json","ea1c9a1224b1ebf21bfbcb183041967e.jpeg.json","ea5164f1625fac7fc25785146105026e.jpeg.json","ea5255de62e00538f7a5552d95ffacd2.jpeg.json","ea848887a2d74fe043f81f56b88a2d12.jpeg.json","eaad3399669a44432d88bcefb80dddea.jpeg.json","eab3e34e6ca878e5b16dac1d1e1a6dfc.jpeg.json","eb8c79d310d7054d37890d2290b85bf0.jpeg.json","ebc8c16eb8ebefa11516f6d9b2990edd.jpeg.json","ec23245f92b5a1bff7db98bac02a6cb4.jpeg.json","ece1ca7a319c25f3cfeec23fa97d2fc3.jpeg.json","ed243bb698dc4b19ce281a770034f9a5.jpeg.json","ed4f181c6c8863f2ded095700bc2c7c3.jpeg.json","ed6409e995cff02a1f5a30a024326aef.jpeg.json","edb4ec033232cc5560d21d1a91028ac6.jpeg.json","edb5618bb7c3ce70faa9cea170958efc.jpeg.json","ede4e9ca14dbb92cdaf95576103ac6d1.jpeg.json","ee08415d8550a2d87364de650a07ecd9.jpeg.json","ee6399df5a80d9cc28c7b13cec0abd1f.jpeg.json","ee819fb1e536987c8c566f62791ef8d9.jpeg.json","eea87fd1a1ddc013631567ad9bd670f8.jpeg.json","eecb407083fd3cf5c704f6b0d86ca8c8.png.json","ef3737f8de72aeb3e967b8344be0f5f0.jpeg.json","f0223dc36cfb60e12b176050796453ba.jpeg.json","f0c7529b755b21c1c7f775413203e49a.jpeg.json","f0e8b78f1f18daf76dd4fc9d04211720.jpeg.json","f11090e810ba8d57814a572d78d40b56.jpeg.json","f17b12b2416962a2d668c8899ff2f0b7.jpeg.json","f18e6c5d2c65873cf38884f41489bc0e.jpeg.json","f18f8f3e6d3d18bc98b0818ee43b32d8.jpeg.json","f1d0d1ebe22add8e060e73cf30048f54.jpeg.json","f1f48ffb9b03fdfadb469dd198aba7ff.jpeg.json","f256210bf91062ad03c442bd67e8ae5c.jpeg.json","f264ae3144218ac7e01b5c9f8c8ead0d.jpeg.json","f2cc05d2d2d65f4fd123fc30197d94d3.jpeg.json","f2db964b5fc68cef96db0381a3b3ba77.jpeg.json","f2edefb4489da7af9e3db508a4cdad48.jpeg.json","f338b2e04ed7dd590a786fa7a0466b3c.jpeg.json","f344d8a79230d6eaa5a79d6fe937807e.jpeg.json","f3460f571c21ba0024630a81f7bfd646.jpeg.json","f3c689b454a7ec1c0d4a656e5c73627f.jpeg.json","f4137bf001ab4487db97aa7f8c02f02c.jpeg.json","f485d8be1fa45e835d1180975a32cf0f.jpeg.json","f4d42e49993c2ea60788c5080b8bfd60.jpeg.json","f4f73b8baef2980de373858ae764234b.jpeg.json","f550e853f302025076d54ebd00ce5be9.jpg.json","f5604bfa425414845c84cbdc5f374d01.jpeg.json","f5abe1eac1b1a79fa194e6a6afec45fc.jpeg.json","f5d8576e4c189d11d057a6412dc81171.jpeg.json","f652d7303b131cd7531f1b13e8aca173.jpeg.json","f68d939fda43324776d14209edcee17c.jpeg.json","f690d9f88c5b7ec8a044254ff7949f6e.jpeg.json","f692676863cc9c9e570d6295e4af2399.png.json","f69f28589b5054d716184203b82fba8a.png.json","f69f3b6b7b6677a83f4baacf5ce7e143.jpeg.json","f6e5b7b6a9c9f9ccfeee2a074a0c1203.jpeg.json","f743c161a800793704d38252219216ce.jpeg.json","f792a9a7c74112b35edf7047f40f1790.jpeg.json","f7a32d5c00985cee6a728fd80f51955a.jpeg.json","f7c008134f887ca820e1c8d689038f2c.jpeg.json","f7d9c395b3900300c075fdf81f2312d1.jpeg.json","f7e8ea6ced92054dea82faebbe3fa243.jpeg.json","f80a0c5b5232ead4c9b22a01c17e2564.jpeg.json","f8326f743b6358281f3e0d6660e7f0ef.jpeg.json","f8f8c7d3d03bf90018b2666bf213077b.jpeg.json","f96bd76914f6f1ae7531f29c42a57eef.jpeg.json","fa26736dbe66222fdded414327ada3a3.jpeg.json","fa614d794e63113c22a47b5565423f9c.jpeg.json","fad737b5bd14428d8df30c7ac075b522.jpeg.json","fb17166c127c726b7a4d83f39860e307.jpeg.json","fb33c6ab0b31786fda22e2dfcca19533.jpeg.json","fb6e4592d5509d6a7e4d8b2809a883cd.jpeg.json","fb9423fb2527a12f984ed02a6b4fa0f0.jpeg.json","fbab704d149fca7dd1dd0d51ca8d48f2.jpeg.json","fbae3e2b20b46023ee996ca28ba376b5.jpeg.json","fbb044dbb5d3a328c9cd3f7bbd643561.jpeg.json","fbb1def90cf2d501e46d65e3a28c2c06.jpeg.json","fbbcddc555e1c1fcca7b2e427642fc43.png.json","fbd3b2c5b3c8ba8942a4e6c8bc32bf12.jpeg.json","fc0b12f2f06e054d3261db3932601265.jpeg.json","fc6bc2eb21708b3c8d645f709ef63927.jpeg.json","fc94c5366b7bdb40160f71f6d59ed727.jpeg.json","fd859c72c1a38cb382494882b1c502cb.jpeg.json","fda53bb004e548379f0296b9dc9769f1.jpeg.json","fe95e2167be353aac3ce9dae1c522784.jpeg.json","feb176b3f242f9f05b4fd137044be85a.jpeg.json","fecf9c5d753e1c29ce4dbad456290265.jpeg.json","ff548c8e48c6f3a687e6f406a7d0e47a.jpeg.json","ff71eb172eba1019e7cb579625089669.jpeg.json","ffb4e6b33755f68cc0ca9b93aa3af654.jpeg.json","ffe6f1ab63d429fa0e090ae78fb79301.jpeg.json"]
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script src="face-api.js"></script>
<script src="commons.js"></script>
<script src="trainUtils.js"></script>
<script src="loss.js"></script>
<script src="FileSaver.js"></script>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
</head>
<body>
<script>
window.tf = faceapi.tf
// hyper parameters
window.objectScale = 5
window.noObjectScale = 1
window.coordScale = 1
const weightsUrl = `/tmp/tmp__224_35060__320_41188__416_31050__608_16520.weights`
//const inputSizes = [160, 224, 320, 416]
const inputSizes = [512, 608]
async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer())
}
async function fetchFddbJson() {
return fetch('/fddb-detections.json').then(res => res.json())
}
async function run() {
window.fddbJson = await fetchFddbJson()
const weights = await loadNetWeights(weightsUrl)
window.net = new faceapi.TinyYolov2(true)
await window.net.load(weights)
await promiseSequential(inputSizes.map(inputSize => async () => {
await promiseSequential(window.fddbJson.map(({ filePath, rects }) => () => {
return test(filePath, rects, inputSize)
}))
const losses = Object.keys(window.lossMap[inputSize]).map(k => window.lossMap[inputSize][k])
const totalLoss = losses
.map(l => l.totalLoss)
.reduce((sum, l) => sum + l)
const avgLoss = totalLoss / losses.length
log(`totalLoss (${inputSize}): ${totalLoss}`)
log(`avgLoss (${inputSize}): ${avgLoss}`)
window.losses = window.losses || {}
window.losses[inputSize] = { totalLoss, avgLoss }
}))
console.log(window.losses)
}
async function test(fileUri, rects, inputSize) {
const img = await faceapi.bufferToImage(await fetchImage(fileUri))
const groundTruthBoxes = rects
.map(({ x, y, width, height }) => new faceapi.Rect(x, y, width, height))
.map(rect => rect.clipAtImageBorders(img.width, img.height))
.map(({ x, y, width, height }) => ({
x: x / img.width,
y: y / img.height,
width: width / img.width,
height: height / img.height,
}))
const { reshapedImgDims, paddings } = getPaddingsAndReshapedSize(img, inputSize)
const squareImg = imageToSquare(img)
const netInput = (await faceapi.toNetInput(squareImg)).managed()
const losses = tf.tidy(() => {
const outTensor = window.net.forwardInput(netInput, inputSize)
const {
noObjectLoss,
objectLoss,
coordLoss,
totalLoss
} = computeLoss(
outTensor,
groundTruthBoxes,
reshapedImgDims,
paddings
)
const losses = {
totalLoss: totalLoss.dataSync()[0],
noObjectLoss: noObjectLoss.dataSync()[0],
objectLoss: objectLoss.dataSync()[0],
coordLoss: coordLoss.dataSync()[0]
}
return losses
})
log(`${fileUri}:`)
log(`ground truth boxes: ${groundTruthBoxes.length}`)
log(`noObjectLoss: ${losses.noObjectLoss}`)
log(`objectLoss: ${losses.objectLoss}`)
log(`coordLoss: ${losses.coordLoss}`)
log(`totalLoss: ${losses.totalLoss}`)
if (Object.keys(losses).map(k => losses[k]).some(loss => isNaN(loss) || loss === Infinity)) {
console.log(groundTruthBoxes)
console.log(img)
console.log(losses)
throw new Error('corrupted loss value')
}
window.lossMap = window.lossMap || {}
window.lossMap[inputSize] = window.lossMap[inputSize] || {}
window.lossMap[inputSize][fileUri] = losses
}
$(document).ready(function() {
run()
})
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<script src="face-api.js"></script>
<script src="commons.js"></script>
<script src="FileSaver.js"></script>
<script src="trainUtils.js"></script>
<script src="train.js"></script>
<script src="loss.js"></script>
</head>
<body>
<script>
tf = faceapi.tf
const startIdx160 = 26600
const startIdx224 = 61660
const startIdx320 = 67788
const startIdx416 = 57650
const startIdx608 = 16520
//const weightsUrl = `/tmp/tmp__160_${startIdx160}__224_${startIdx224}__320_${startIdx320}__416_${startIdx416}__608_${startIdx608}.weights`
const weightsUrl = `/tmp/tmp_multiscale_count_8700.weights`
const fromEpoch = 0
const trainOnlyMultibox = false
const trainSizes = [416, 512, 608]
//const trainSizes = [608]
window.debug = false
window.logTrainSteps = true
window.count = 0
// hyper parameters
window.objectScale = 5
window.noObjectScale = 1
window.coordScale = 1
const rescaleEveryNthBatch = 100
window.saveEveryNthDataIdx = trainSizes.length * rescaleEveryNthBatch
window.trainSteps = 4000
//window.optimizer = tf.train.sgd(0.001)
window.optimizer = tf.train.adam(0.001, 0.9, 0.999, 1e-8)
// all samples
//const dataStartIdx = 8000
const dataStartIdx = 0
const numTrainSamples = Infinity
async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer())
}
async function fetchDetectionFilenames() {
return fetch('/detection_filenames').then(res => res.json())
}
async function fetchDetectionFilenamesMultibox() {
return fetch('/detection_filenames_multibox').then(res => res.json())
}
async function run() {
const weights = await loadNetWeights(weightsUrl)
window.net = new faceapi.TinyYolov2(true)
window.net.load(weights)
window.net.variable()
const fetchDetectionsFn = trainOnlyMultibox
? fetchDetectionFilenamesMultibox
: fetchDetectionFilenames
window.detectionFilenames = (await fetchDetectionsFn()).slice(dataStartIdx, dataStartIdx + numTrainSamples)
window.lossMap = {}
console.log('ready')
}
function logLossChange(lossType) {
const { currentLoss, prevLoss, detectionFilenames } = window
log(`${lossType} : ${faceapi.round(currentLoss[lossType])} (avg: ${faceapi.round(currentLoss[lossType] / detectionFilenames.length)}) (delta: ${currentLoss[lossType] - prevLoss[lossType]})`)
}
function onBatchProcessed(dataIdx, inputSize) {
window.count++
const idx = (dataIdx + 1) + (window.epoch * window.detectionFilenames.length)
console.log('dataIdx', dataIdx)
if ((window.count % saveEveryNthDataIdx) === 0) {
saveWeights(window.net, `tmp_multiscale_count_${window.count}.weights`)
}
}
function _onBatchProcessed(dataIdx, inputSize) {
const idx = (dataIdx + 1) + (window.epoch * window.detectionFilenames.length)
console.log('idx', idx)
if ((idx % saveEveryNthDataIdx) === 0) {
saveWeights(window.net, `tmp__224_${startIdx224 + (inputSize === 224 ? idx : 0)}__320_${startIdx320 + (inputSize === 320 ? idx : 0)}__416_${startIdx416 + (inputSize === 416 ? idx : 0)}__608_${startIdx608 + (inputSize === 608 ? idx : 0)}.weights`)
}
}
async function train() {
const batchSize = 1
for (let i = fromEpoch; i < trainSteps; i++) {
window.epoch = i
log('step', i)
let ts2 = Date.now()
const batchCreators = createBatchCreators(shuffle(window.detectionFilenames), batchSize)
await trainStep(batchCreators, trainSizes, rescaleEveryNthBatch, onBatchProcessed)
ts2 = Date.now() - ts2
}
ts = Date.now() - ts
log()
log('--------------------')
log()
log('step %s done (%s ms)', i, ts)
window.prevLoss = window.currentLoss
window.currentLoss = Object.keys(lossMap)
.map(filename => lossMap[filename])
.reduce((accumulatedLosses, losses) =>
Object.keys(losses)
.map(key => ({
[key]: (accumulatedLosses[key] || 0) + losses[key]
}))
.reduce((map, curr) => ({ ...map, ...curr }), {}),
{}
)
if (window.prevLoss) {
logLossChange('noObjectLoss')
logLossChange('objectLoss')
logLossChange('coordLoss')
logLossChange('totalLoss')
}
log()
log('--------------------')
log()
}
run()
</script>
</body>
</html>
\ No newline at end of file
const batchIdx = 0
function minimize(groundTruthBoxes, batchInput, inputSize, batch, { reshapedImgDims, paddings }) {
const filename = batch.filenames[batchIdx]
const { dataIdx } = batch
return optimizer.minimize(() => {
const outTensor = window.net.forwardInput(batchInput, inputSize)
const {
noObjectLoss,
objectLoss,
coordLoss,
totalLoss
} = computeLoss(
outTensor,
groundTruthBoxes,
reshapedImgDims,
paddings
)
const losses = {
totalLoss: totalLoss.dataSync()[0],
noObjectLoss: noObjectLoss.dataSync()[0],
objectLoss: objectLoss.dataSync()[0],
coordLoss: coordLoss.dataSync()[0]
}
const lossKey = `${filename}_${inputSize}`
if (window.logTrainSteps) {
log(`ground truth boxes: ${groundTruthBoxes.length}`)
log(`noObjectLoss[${dataIdx}]: ${losses.noObjectLoss}`)
log(`objectLoss[${dataIdx}]: ${losses.objectLoss}`)
log(`coordLoss[${dataIdx}]: ${losses.coordLoss}`)
log(`totalLoss[${dataIdx}]: ${losses.totalLoss}`)
if (window.lossMap[lossKey]) {
log(`loss change: ${losses.totalLoss - window.lossMap[lossKey].totalLoss}`)
}
}
window.lossMap[lossKey] = losses
return totalLoss
}, true)
}
async function trainStep(batchCreators, inputSizes, rescaleEveryNthBatch, onBatchProcessed = () => {}) {
async function step(currentBatchCreators) {
if (!currentBatchCreators.batchCreators.length) {
return
}
await promiseSequential(inputSizes.map(inputSize => async () => {
await promiseSequential(currentBatchCreators.batchCreators.map(batchCreator => async () => {
const batch = await batchCreator()
const { imgs, groundTruthBoxes, filenames, dataIdx } = batch
const img = imgs[0]
const { reshapedImgDims, paddings } = getPaddingsAndReshapedSize(img, inputSize)
const squareImg = imageToSquare(img)
const batchInput = await faceapi.toNetInput(squareImg)
const [imgHeight, imgWidth] = batchInput.inputs[batchIdx].shape
// skip groundTruthBoxes, which are too tiny
const scaleFactor = inputSize / Math.max(imgHeight, imgWidth)
const filteredGroundTruthBoxes = groundTruthBoxes[batchIdx].filter(({ x, y, width, height }) => {
const box = (new faceapi.Rect(x, y, width, height))
.toBoundingBox()
.rescale({ height: imgHeight, width: imgWidth })
.rescale(scaleFactor)
const isTooTiny = box.width < 40 || box.height < 40
if (isTooTiny && window.debug) {
log(`skipping box for input size ${inputSize}: (${Math.floor(box.width)} x ${Math.floor(box.height)})`)
}
return !isTooTiny
})
if (!filteredGroundTruthBoxes.length) {
if (window.debug) {
log(`no boxes for input size ${inputSize}, ${groundTruthBoxes[batchIdx].length} boxes were too small`)
}
batchInput.dispose()
onBatchProcessed(dataIdx, inputSize)
return
}
let ts = Date.now()
const loss = minimize(filteredGroundTruthBoxes, batchInput, inputSize, batch, { reshapedImgDims, paddings })
ts = Date.now() - ts
if (window.logTrainSteps) {
log(`trainStep time for dataIdx ${dataIdx} (${inputSize}): ${ts} ms`)
}
loss.dispose()
batchInput.dispose()
onBatchProcessed(dataIdx, inputSize)
await tf.nextFrame()
}))
}))
await step(currentBatchCreators.next(rescaleEveryNthBatch))
}
await step(batchCreators.next(rescaleEveryNthBatch))
}
async function fetchGroundTruthBoxesForFile(file) {
const boxes = await fetch(file).then(res => res.json())
return {
file,
boxes
}
}
function createBatchCreators(detectionFilenames, batchSize) {
if (batchSize < 1) {
throw new Error('invalid batch size: ' + batchSize)
}
const batches = []
const pushToBatch = (remaining) => {
if (remaining.length) {
batches.push(remaining.slice(0, batchSize))
pushToBatch(remaining.slice(batchSize))
}
return batches
}
pushToBatch(detectionFilenames)
const batchCreators = batches.map((filenamesForBatch, dataIdx) => async () => {
const groundTruthBoxes = (await Promise.all(filenamesForBatch.map(fetchGroundTruthBoxesForFile)))
.map(({ boxes }) => boxes)
const imgs = await Promise.all(filenamesForBatch.map(
async file => await faceapi.bufferToImage(await fetchImage(file.replace('.json', '')))
))
return {
imgs,
groundTruthBoxes,
filenames: filenamesForBatch,
dataIdx
}
})
let idx = 0
function next(n) {
const nextBatchCreators = batchCreators.slice(idx, idx + n)
idx += n
return {
batchCreators: nextBatchCreators,
next
}
}
return {
data: batchCreators,
next
}
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script src="face-api.js"></script>
<script src="commons.js"></script>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
</head>
<body>
<div id="navbar"></div>
<div class="center-content page-container">
<div class="progress" id="loader">
<div class="indeterminate"></div>
</div>
<div style="position: relative" class="margin">
<img id="inputImg" src="" style="max-width: 800px;" />
<canvas id="overlay" />
</div>
<div class="row side-by-side">
<div id="selectList"></div>
<div class="row">
<label for="imgUrlInput">Get image from URL:</label>
<input id="imgUrlInput" type="text" class="bold">
</div>
<button
class="waves-effect waves-light btn"
onclick="loadImageFromUrl()"
>
Ok
</button>
</div>
<div class="row side-by-side">
<div class="row input-field" style="margin-right: 20px;">
<select id="sizeType">
<option value="128">128 x 128</option>
<option value="160">160 x 160</option>
<option value="224">224 x 224</option>
<option value="320">320 x 320</option>
<option value="416">416 x 416</option>
<option value="608">608 x 608</option>
</select>
<label>Input Size</label>
</div>
<div class="row">
<label for="scoreThreshold">Score Threshold:</label>
<input disabled value="0.5" id="scoreThreshold" type="text" class="bold">
</div>
<button
class="waves-effect waves-light btn"
onclick="onDecreaseThreshold()"
>
<i class="material-icons left">-</i>
</button>
<button
class="waves-effect waves-light btn"
onclick="onIncreaseThreshold()"
>
<i class="material-icons left">+</i>
</button>
</div>
<div class="row">
<label for="imgByNr">Enter image NR: </label>
<input id="imgByNr" type="text" class="bold">
</div>
</div>
<script>
let scoreThreshold = 0.5
let sizeType = 608
function onKeyDown(e) {
e.target.value = (
parseInt(e.target.value) + (e.keyCode === 38 ? 1 : (e.keyCode === 40 ? -1 : 0))
) || e.target.value || 0
const imgUri = window.imgs[e.target.value]
console.log(imgUri)
onSelectionChanged(imgUri)
}
function onIncreaseThreshold() {
scoreThreshold = Math.min(faceapi.round(scoreThreshold + 0.1), 1.0)
$('#scoreThreshold').val(scoreThreshold)
updateResults()
}
function onDecreaseThreshold() {
scoreThreshold = Math.max(faceapi.round(scoreThreshold - 0.1), 0.1)
$('#scoreThreshold').val(scoreThreshold)
updateResults()
}
function onSizeTypeChanged(e, c) {
sizeType = e.target.value
$('#sizeType').val(sizeType)
updateResults()
}
async function loadImageFromUrl(url) {
const img = await requestExternalImage($('#imgUrlInput').val())
$('#inputImg').get(0).src = img.src
updateResults()
}
async function updateResults() {
const inputImgEl = $('#inputImg').get(0)
const { width, height } = inputImgEl
const canvas = $('#overlay').get(0)
canvas.width = width
canvas.height = height
const forwardParams = {
inputSize: parseInt(sizeType),
scoreThreshold
}
const detections = await window.net.locateFaces(inputImgEl, forwardParams)
faceapi.drawDetection('overlay', detections.map(det => det.forSize(width, height)))
}
async function onSelectionChanged(uri) {
const imgBuf = await fetchImage(uri)
$(`#inputImg`).get(0).src = (await faceapi.bufferToImage(imgBuf)).src
updateResults()
}
async function loadNetWeights(uri) {
return new Float32Array(await (await fetch(uri)).arrayBuffer())
}
async function fetchDetectionFilenames() {
return fetch('/detection_filenames').then(res => res.json())
}
async function run() {
$('#imgByNr').keydown(onKeyDown)
const startIdx224 = 35060
const startIdx320 = 41188
const startIdx416 = 31050
const startIdx608 = 16520
//const weightsUrl = `/tmp/tmp__224_${startIdx224}__320_${startIdx320}__416_${startIdx416}__608_${startIdx608}.weights`
const weightsUrl = `/tmp/tmp_multiscale_count_4200.weights`
const weights = await loadNetWeights(weightsUrl)
window.net = new faceapi.TinyYolov2(true)
await window.net.load(weights)
window.imgs = (await fetchDetectionFilenames()).map(f => f.replace('.json', ''))
$('#loader').hide()
onSelectionChanged($('#selectList select').val())
}
$(document).ready(function() {
renderNavBar('#navbar', 'tiny_yolov2_face_detection')
renderImageSelectList(
'#selectList',
async (uri) => {
await onSelectionChanged(uri)
},
'bbt1.jpg'
)
const sizeTypeSelect = $('#sizeType')
sizeTypeSelect.val(sizeType)
sizeTypeSelect.on('change', onSizeTypeChanged)
sizeTypeSelect.material_select()
run()
})
</script>
</body>
</html>
\ No newline at end of file
[{"weights":[{"name":"dense0/conv0/filters","shape":[3,3,3,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.007822331026488659,"min":-0.8761010749667297}},{"name":"dense0/conv0/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0061304877786075365,"min":-0.8460073134478401}},{"name":"dense0/conv1/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006931193669637044,"min":-0.928779951731364}},{"name":"dense0/conv1/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01667951135074391,"min":-2.201695498298196}},{"name":"dense0/conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00381035027550716,"min":-0.7658804053769392}},{"name":"dense0/conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006376917455710617,"min":-0.9437837834451713}},{"name":"dense0/conv2/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013428742745343376,"min":-1.2757305608076208}},{"name":"dense0/conv2/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003760007666606529,"min":-0.6091212419902577}},{"name":"dense1/conv0/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003171338637669881,"min":-0.38373197515805557}},{"name":"dense1/conv0/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014757957645491059,"min":-1.8447447056863824}},{"name":"dense1/conv0/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0032720065584369734,"min":-0.369736741103378}},{"name":"dense1/conv1/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006221360318800983,"min":-0.6096933112424963}},{"name":"dense1/conv1/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.011515324723486807,"min":-1.6236607860116399}},{"name":"dense1/conv1/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002638959300284292,"min":-0.316675116034115}},{"name":"dense1/conv2/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005790043110940971,"min":-0.7237553888676214}},{"name":"dense1/conv2/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013147306442260743,"min":-1.6960025310516358}},{"name":"dense1/conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0036364357845455994,"min":-0.5163738814054751}},{"name":"dense2/conv0/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0025823556909374164,"min":-0.3279591727490519}},{"name":"dense2/conv0/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014951597475538066,"min":-1.8689496844422582}},{"name":"dense2/conv0/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0034451217043633556,"min":-0.561554837811227}},{"name":"dense2/conv1/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00580287587408926,"min":-0.7775853671279609}},{"name":"dense2/conv1/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.020558691024780272,"min":-2.2203386306762694}},{"name":"dense2/conv1/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003580525459027758,"min":-0.47262936059166405}},{"name":"dense2/conv2/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.007787894267661899,"min":-0.996850466260723}},{"name":"dense2/conv2/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0294371997608858,"min":-3.6207755705889535}},{"name":"dense2/conv2/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0037439118413364184,"min":-0.40434247886433317}},{"name":"fc/weights","shape":[128,136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013749843485215131,"min":-1.8424790270188276}},{"name":"fc/bias","shape":[136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002984718394045736,"min":0.0885973796248436}}],"paths":["face_landmark_68_tiny_model-shard1"]}]
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment