Unverified Commit 1c56acb1 by justadudewhohacks Committed by GitHub

Merge pull request #93 from justadudewhohacks/fixes-and-performance-improvements

fixed landmark postprocessing + resize input canvases instead of tensors to net input size, which is much more performant
parents f512f8b3 8b1d5771
......@@ -84,12 +84,9 @@
const detections = await faceapi.locateFaces(input, minConfidence)
faceapi.drawDetection('overlay', detections.map(det => det.forSize(width, height)))
const faceImages = await faceapi.extractFaces(input.inputs[0], detections)
const faceImages = await faceapi.extractFaces(inputImgEl, detections)
$('#facesContainer').empty()
faceImages.forEach(canvas => $('#facesContainer').append(canvas))
// free memory for input tensors
input.dispose()
}
async function onSelectionChanged(uri) {
......
......@@ -89,11 +89,8 @@
const input = await faceapi.toNetInput(inputImgEl)
const locations = await faceapi.locateFaces(input, minConfidence)
const faceTensors = (await faceapi.extractFaceTensors(input, locations))
let landmarksByFace = await Promise.all(faceTensors.map(t => faceapi.detectLandmarks(t)))
// free memory for face image tensors after we computed their descriptors
faceTensors.forEach(t => t.dispose())
const faces = await faceapi.extractFaces(input, locations)
let landmarksByFace = await Promise.all(faces.map(face => faceapi.detectLandmarks(face)))
// shift and scale the face landmarks to the face image position in the canvas
landmarksByFace = landmarksByFace.map((landmarks, i) => {
......@@ -103,9 +100,6 @@
faceapi.drawLandmarks(canvas, landmarksByFace, { lineWidth: drawLines ? 2 : 4, drawLines, color: 'red' })
faceapi.drawDetection('overlay', locations.map(det => det.forSize(width, height)))
// free memory for input tensors
input.dispose()
}
async function run() {
......
......@@ -86,26 +86,17 @@
}
async function locateAndAlignFacesWithMtcnn(inputImgEl) {
const input = await faceapi.toNetInput(
inputImgEl,
// dispose input manually
false,
// keep canvases (required for mtcnn)
true
)
const input = await faceapi.toNetInput(inputImgEl)
const results = await faceapi.mtcnn(input, { minFaceSize: 100 })
const unalignedFaceImages = await faceapi.extractFaces(input.inputs[0], results.map(res => res.faceDetection))
const unalignedFaceImages = await faceapi.extractFaces(input.getInput(0), results.map(res => res.faceDetection))
const alignedFaceBoxes = results
.filter(res => res.faceDetection.score > minConfidence)
.map(res => res.faceLandmarks.align())
const alignedFaceImages = await faceapi.extractFaces(input.inputs[0], alignedFaceBoxes)
// free memory for input tensors
input.dispose()
const alignedFaceImages = await faceapi.extractFaces(input.getInput(0), alignedFaceBoxes)
return {
unalignedFaceImages,
......@@ -118,7 +109,7 @@
const locations = await faceapi.locateFaces(input, minConfidence)
const unalignedFaceImages = await faceapi.extractFaces(input.inputs[0], locations)
const unalignedFaceImages = await faceapi.extractFaces(input.getInput(0), locations)
// detect landmarks and get the aligned face image bounding boxes
const alignedFaceBoxes = await Promise.all(unalignedFaceImages.map(
......@@ -127,10 +118,7 @@
return faceLandmarks.align(locations[i])
}
))
const alignedFaceImages = await faceapi.extractFaces(input.inputs[0], alignedFaceBoxes)
// free memory for input tensors
input.dispose()
const alignedFaceImages = await faceapi.extractFaces(input.getInput(0), alignedFaceBoxes)
return {
unalignedFaceImages,
......
......@@ -5838,18 +5838,20 @@
"dev": true
},
"tfjs-image-recognition-base": {
"version": "git+https://github.com/justadudewhohacks/tfjs-image-recognition-base.git#2f2072f883dd098bc539e2e89a61878720e400a1",
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/tfjs-image-recognition-base/-/tfjs-image-recognition-base-0.1.0.tgz",
"integrity": "sha512-rgbDz+96qwDaH3dUbyMAfCc+ptlTH9jV0M8ucuCfSmxTKwvQ7alAOI5EblubSaKq0y+ioNRoK2eBkkfIenPOSQ==",
"requires": {
"@tensorflow/tfjs-core": "0.12.14"
}
},
"tfjs-tiny-yolov2": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/tfjs-tiny-yolov2/-/tfjs-tiny-yolov2-0.0.2.tgz",
"integrity": "sha512-NtiPErN2tIP9EkZA7rGjW5wF7iN4JAjI0LwoC1HvMfd4oPpmqwFSbXxwDvFbZY3a5mPUyW6E4/AqoXjSg4w3yA==",
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/tfjs-tiny-yolov2/-/tfjs-tiny-yolov2-0.1.0.tgz",
"integrity": "sha512-UZyvukF61XExoGAGvvCTYKALe6mz0ZNUczgfW4P+qE6mVbPzIFHiY1yweSwc9u4LZm6FstQf5EgiBFgXoFoxgw==",
"requires": {
"@tensorflow/tfjs-core": "0.12.14",
"tfjs-image-recognition-base": "git+https://github.com/justadudewhohacks/tfjs-image-recognition-base.git#2f2072f883dd098bc539e2e89a61878720e400a1"
"tfjs-image-recognition-base": "0.1.0"
}
},
"through2": {
......@@ -5983,8 +5985,7 @@
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
},
"tty-browserify": {
"version": "0.0.0",
......
......@@ -22,8 +22,8 @@
"license": "MIT",
"dependencies": {
"@tensorflow/tfjs-core": "^0.12.14",
"tfjs-image-recognition-base": "^0.0.0",
"tfjs-tiny-yolov2": "0.0.2",
"tfjs-image-recognition-base": "0.1.0",
"tfjs-tiny-yolov2": "0.1.0",
"tslib": "^1.9.3"
},
"devDependencies": {
......
......@@ -5,9 +5,9 @@ import { TinyYolov2 } from '.';
import { FaceDetection } from './classes/FaceDetection';
import { FaceLandmarks68 } from './classes/FaceLandmarks68';
import { FullFaceDescription } from './classes/FullFaceDescription';
import { extractFaceTensors } from './dom';
import { extractFaces } from './dom';
import { FaceDetectionNet } from './faceDetectionNet/FaceDetectionNet';
import { FaceLandmarkNet } from './faceLandmarkNet/FaceLandmarkNet';
import { FaceLandmark68Net } from './faceLandmarkNet/FaceLandmark68Net';
import { FaceRecognitionNet } from './faceRecognitionNet/FaceRecognitionNet';
import { Mtcnn } from './mtcnn/Mtcnn';
import { MtcnnForwardParams } from './mtcnn/types';
......@@ -16,23 +16,21 @@ function computeDescriptorsFactory(
recognitionNet: FaceRecognitionNet
) {
return async function(input: TNetInput, alignedFaceBoxes: Rect[], useBatchProcessing: boolean) {
const alignedFaceTensors = await extractFaceTensors(input, alignedFaceBoxes)
const alignedFaceCanvases = await extractFaces(input, alignedFaceBoxes)
const descriptors = useBatchProcessing
? await recognitionNet.computeFaceDescriptor(alignedFaceTensors) as Float32Array[]
: await Promise.all(alignedFaceTensors.map(
faceTensor => recognitionNet.computeFaceDescriptor(faceTensor)
? await recognitionNet.computeFaceDescriptor(alignedFaceCanvases) as Float32Array[]
: await Promise.all(alignedFaceCanvases.map(
canvas => recognitionNet.computeFaceDescriptor(canvas)
)) as Float32Array[]
alignedFaceTensors.forEach(t => t.dispose())
return descriptors
}
}
function allFacesFactory(
detectFaces: (input: TNetInput) => Promise<FaceDetection[]>,
landmarkNet: FaceLandmarkNet,
landmarkNet: FaceLandmark68Net,
recognitionNet: FaceRecognitionNet
) {
const computeDescriptors = computeDescriptorsFactory(recognitionNet)
......@@ -43,15 +41,14 @@ function allFacesFactory(
): Promise<FullFaceDescription[]> {
const detections = await detectFaces(input)
const faceTensors = await extractFaceTensors(input, detections)
const faceCanvases = await extractFaces(input, detections)
const faceLandmarksByFace = useBatchProcessing
? await landmarkNet.detectLandmarks(faceTensors) as FaceLandmarks68[]
: await Promise.all(faceTensors.map(
faceTensor => landmarkNet.detectLandmarks(faceTensor)
? await landmarkNet.detectLandmarks(faceCanvases) as FaceLandmarks68[]
: await Promise.all(faceCanvases.map(
canvas => landmarkNet.detectLandmarks(canvas)
)) as FaceLandmarks68[]
faceTensors.forEach(t => t.dispose())
const alignedFaceBoxes = faceLandmarksByFace.map(
(landmarks, i) => landmarks.align(detections[i].getBox())
......@@ -74,7 +71,7 @@ function allFacesFactory(
export function allFacesSsdMobilenetv1Factory(
ssdMobilenetv1: FaceDetectionNet,
landmarkNet: FaceLandmarkNet,
landmarkNet: FaceLandmark68Net,
recognitionNet: FaceRecognitionNet
) {
return async function(
......@@ -90,7 +87,7 @@ export function allFacesSsdMobilenetv1Factory(
export function allFacesTinyYolov2Factory(
tinyYolov2: TinyYolov2,
landmarkNet: FaceLandmarkNet,
landmarkNet: FaceLandmark68Net,
recognitionNet: FaceRecognitionNet
) {
return async function(
......
import * as tf from '@tensorflow/tfjs-core';
import { Rect, TNetInput, toNetInput } from 'tfjs-image-recognition-base';
import { isTensor4D, Rect } from 'tfjs-image-recognition-base';
import { FaceDetection } from '../classes/FaceDetection';
......@@ -9,28 +9,21 @@ import { FaceDetection } from '../classes/FaceDetection';
* Using this method is faster then extracting a canvas for each face and
* converting them to tensors individually.
*
* @param input The image that face detection has been performed on.
* @param imageTensor The image tensor that face detection has been performed on.
* @param detections The face detection results or face bounding boxes for that image.
* @returns Tensors of the corresponding image region for each detected face.
*/
export async function extractFaceTensors(
input: TNetInput,
imageTensor: tf.Tensor3D | tf.Tensor4D,
detections: Array<FaceDetection | Rect>
): Promise<tf.Tensor4D[]> {
): Promise<tf.Tensor3D[]> {
const netInput = await toNetInput(input, true)
if (netInput.batchSize > 1) {
if (netInput.isManaged) {
netInput.dispose()
}
if (isTensor4D(imageTensor) && imageTensor.shape[0] > 1) {
throw new Error('extractFaceTensors - batchSize > 1 not supported')
}
return tf.tidy(() => {
const imgTensor = netInput.inputs[0].expandDims().toFloat() as tf.Tensor4D
const [imgHeight, imgWidth, numChannels] = imgTensor.shape.slice(1)
const [imgHeight, imgWidth, numChannels] = imageTensor.shape.slice(isTensor4D(imageTensor) ? 1 : 0)
const boxes = detections.map(
det => det instanceof FaceDetection
......@@ -40,12 +33,9 @@ export async function extractFaceTensors(
.map(box => box.clipAtImageBorders(imgWidth, imgHeight))
const faceTensors = boxes.map(({ x, y, width, height }) =>
tf.slice4d(imgTensor, [0, y, x, 0], [1, height, width, numChannels])
tf.slice3d(imageTensor.as3D(imgHeight, imgWidth, numChannels), [y, x, 0], [height, width, numChannels])
)
if (netInput.isManaged) {
netInput.dispose()
}
return faceTensors
})
}
\ No newline at end of file
......@@ -24,24 +24,19 @@ export async function extractFaces(
let canvas = input as HTMLCanvasElement
if (!(input instanceof HTMLCanvasElement)) {
const netInput = await toNetInput(input, true)
const netInput = await toNetInput(input)
if (netInput.batchSize > 1) {
if (netInput.isManaged) {
netInput.dispose()
}
throw new Error('extractFaces - batchSize > 1 not supported')
}
canvas = await imageTensorToCanvas(netInput.inputs[0])
if (netInput.isManaged) {
netInput.dispose()
}
const tensorOrCanvas = netInput.getInput(0)
canvas = tensorOrCanvas instanceof HTMLCanvasElement
? tensorOrCanvas
: await imageTensorToCanvas(tensorOrCanvas)
}
const ctx = getContext2dOrThrow(canvas)
const boxes = detections.map(
det => det instanceof FaceDetection
? det.forSize(canvas.width, canvas.height).getBox().floor()
......
......@@ -25,7 +25,7 @@ export class FaceDetectionNet extends NeuralNetwork<NetParams> {
}
return tf.tidy(() => {
const batchTensor = input.toBatchTensor(512, false)
const batchTensor = input.toBatchTensor(512, false).toFloat()
const x = tf.sub(tf.mul(batchTensor, tf.scalar(0.007843137718737125)), tf.scalar(1)) as tf.Tensor4D
const features = mobileNetV1(x, params.mobilenetv1)
......@@ -40,7 +40,7 @@ export class FaceDetectionNet extends NeuralNetwork<NetParams> {
}
public async forward(input: TNetInput) {
return this.forwardInput(await toNetInput(input, true))
return this.forwardInput(await toNetInput(input))
}
public async locateFaces(
......@@ -49,7 +49,7 @@ export class FaceDetectionNet extends NeuralNetwork<NetParams> {
maxResults: number = 100
): Promise<FaceDetection[]> {
const netInput = await toNetInput(input, true)
const netInput = await toNetInput(input)
const {
boxes: _boxes,
......@@ -77,18 +77,21 @@ export class FaceDetectionNet extends NeuralNetwork<NetParams> {
minConfidence
)
const paddings = netInput.getRelativePaddings(0)
const reshapedDims = netInput.getReshapedInputDimensions(0)
const inputSize = netInput.inputSize as number
const padX = inputSize / reshapedDims.width
const padY = inputSize / reshapedDims.height
const results = indices
.map(idx => {
const [top, bottom] = [
Math.max(0, boxes.get(idx, 0)),
Math.min(1.0, boxes.get(idx, 2))
].map(val => val * paddings.y)
].map(val => val * padY)
const [left, right] = [
Math.max(0, boxes.get(idx, 1)),
Math.min(1.0, boxes.get(idx, 3))
].map(val => val * paddings.x)
].map(val => val * padX)
return new FaceDetection(
scoresData[idx],
new Rect(
......
import * as tf from '@tensorflow/tfjs-core';
import { isEven, NetInput, NeuralNetwork, Point, TNetInput, toNetInput } from 'tfjs-image-recognition-base';
import { NetInput } from 'tfjs-image-recognition-base';
import { convLayer, ConvParams } from 'tfjs-tiny-yolov2';
import { FaceLandmarks68 } from '../classes/FaceLandmarks68';
import { extractParams } from './extractParams';
import { FaceLandmark68NetBase } from './FaceLandmark68NetBase';
import { fullyConnectedLayer } from './fullyConnectedLayer';
import { loadQuantizedParams } from './loadQuantizedParams';
import { NetParams } from './types';
......@@ -16,22 +16,22 @@ function maxPool(x: tf.Tensor4D, strides: [number, number] = [2, 2]): tf.Tensor4
return tf.maxPool(x, [2, 2], strides, 'valid')
}
export class FaceLandmarkNet extends NeuralNetwork<NetParams> {
export class FaceLandmark68Net extends FaceLandmark68NetBase<NetParams> {
constructor() {
super('FaceLandmarkNet')
super('FaceLandmark68Net')
}
public forwardInput(input: NetInput): tf.Tensor2D {
public runNet(input: NetInput): tf.Tensor2D {
const { params } = this
if (!params) {
throw new Error('FaceLandmarkNet - load model before inference')
throw new Error('FaceLandmark68Net - load model before inference')
}
return tf.tidy(() => {
const batchTensor = input.toBatchTensor(128, true)
const batchTensor = input.toBatchTensor(128, true).toFloat() as tf.Tensor4D
let out = conv(batchTensor, params.conv0)
out = maxPool(out)
......@@ -46,77 +46,11 @@ export class FaceLandmarkNet extends NeuralNetwork<NetParams> {
out = maxPool(out, [1, 1])
out = conv(out, params.conv7)
const fc0 = tf.relu(fullyConnectedLayer(out.as2D(out.shape[0], -1), params.fc0))
const fc1 = fullyConnectedLayer(fc0, params.fc1)
const createInterleavedTensor = (fillX: number, fillY: number) =>
tf.stack([
tf.fill([68], fillX),
tf.fill([68], fillY)
], 1).as2D(1, 136).as1D()
/* shift coordinates back, to undo centered padding
x = ((x * widthAfterPadding) - shiftX) / width
y = ((y * heightAfterPadding) - shiftY) / height
*/
const landmarkTensors = fc1
.mul(tf.stack(Array.from(Array(input.batchSize), (_, batchIdx) =>
createInterleavedTensor(
input.getPaddings(batchIdx).x + input.getInputWidth(batchIdx),
input.getPaddings(batchIdx).y + input.getInputHeight(batchIdx)
)
)))
.sub(tf.stack(Array.from(Array(input.batchSize), (_, batchIdx) =>
createInterleavedTensor(
Math.floor(input.getPaddings(batchIdx).x / 2),
Math.floor(input.getPaddings(batchIdx).y / 2)
)
)))
.div(tf.stack(Array.from(Array(input.batchSize), (_, batchIdx) =>
createInterleavedTensor(
input.getInputWidth(batchIdx),
input.getInputHeight(batchIdx)
)
)))
return landmarkTensors as tf.Tensor2D
return fullyConnectedLayer(fc0, params.fc1)
})
}
public async forward(input: TNetInput): Promise<tf.Tensor2D> {
return this.forwardInput(await toNetInput(input, true))
}
public async detectLandmarks(input: TNetInput): Promise<FaceLandmarks68 | FaceLandmarks68[]> {
const netInput = await toNetInput(input, true)
const landmarkTensors = tf.tidy(
() => tf.unstack(this.forwardInput(netInput))
)
const landmarksForBatch = await Promise.all(landmarkTensors.map(
async (landmarkTensor, batchIdx) => {
const landmarksArray = Array.from(await landmarkTensor.data())
const xCoords = landmarksArray.filter((_, i) => isEven(i))
const yCoords = landmarksArray.filter((_, i) => !isEven(i))
return new FaceLandmarks68(
Array(68).fill(0).map((_, i) => new Point(xCoords[i], yCoords[i])),
{
height: netInput.getInputHeight(batchIdx),
width : netInput.getInputWidth(batchIdx),
}
)
}
))
landmarkTensors.forEach(t => t.dispose())
return netInput.isBatchInput
? landmarksForBatch
: landmarksForBatch[0]
}
protected loadQuantizedParams(uri: string | undefined) {
return loadQuantizedParams(uri)
}
......
import * as tf from '@tensorflow/tfjs-core';
import { isEven, NetInput, NeuralNetwork, Point, TNetInput, toNetInput, Dimensions } from 'tfjs-image-recognition-base';
import { FaceLandmarks68 } from '../classes/FaceLandmarks68';
export class FaceLandmark68NetBase<NetParams> extends NeuralNetwork<NetParams> {
// TODO: make super.name protected
private __name: string
constructor(_name: string) {
super(_name)
this.__name = _name
}
public runNet(_: NetInput): tf.Tensor2D {
throw new Error(`${this.__name} - runNet not implemented`)
}
public postProcess(output: tf.Tensor2D, inputSize: number, originalDimensions: Dimensions[]): tf.Tensor2D {
const inputDimensions = originalDimensions.map(({ width, height }) => {
const scale = inputSize / Math.max(height, width)
return {
width: width * scale,
height: height * scale
}
})
const batchSize = inputDimensions.length
return tf.tidy(() => {
const createInterleavedTensor = (fillX: number, fillY: number) =>
tf.stack([
tf.fill([68], fillX),
tf.fill([68], fillY)
], 1).as2D(1, 136).as1D()
const getPadding = (batchIdx: number, cond: (w: number, h: number) => boolean): number => {
const { width, height } = inputDimensions[batchIdx]
return cond(width, height) ? Math.abs(width - height) / 2 : 0
}
const getPaddingX = (batchIdx: number) => getPadding(batchIdx, (w, h) => w < h)
const getPaddingY = (batchIdx: number) => getPadding(batchIdx, (w, h) => h < w)
const landmarkTensors = output
.mul(tf.fill([batchSize, 136], inputSize))
.sub(tf.stack(Array.from(Array(batchSize), (_, batchIdx) =>
createInterleavedTensor(
getPaddingX(batchIdx),
getPaddingY(batchIdx)
)
)))
.div(tf.stack(Array.from(Array(batchSize), (_, batchIdx) =>
createInterleavedTensor(
inputDimensions[batchIdx].width,
inputDimensions[batchIdx].height
)
)))
return landmarkTensors as tf.Tensor2D
})
}
public forwardInput(input: NetInput): tf.Tensor2D {
return tf.tidy(() => {
const out = this.runNet(input)
return this.postProcess(
out,
input.inputSize as number,
input.inputDimensions.map(([height, width]) => ({ height, width }))
)
})
}
public async forward(input: TNetInput): Promise<tf.Tensor2D> {
return this.forwardInput(await toNetInput(input))
}
public async detectLandmarks(input: TNetInput): Promise<FaceLandmarks68 | FaceLandmarks68[]> {
const netInput = await toNetInput(input)
const landmarkTensors = tf.tidy(
() => tf.unstack(this.forwardInput(netInput))
)
const landmarksForBatch = await Promise.all(landmarkTensors.map(
async (landmarkTensor, batchIdx) => {
const landmarksArray = Array.from(await landmarkTensor.data())
const xCoords = landmarksArray.filter((_, i) => isEven(i))
const yCoords = landmarksArray.filter((_, i) => !isEven(i))
return new FaceLandmarks68(
Array(68).fill(0).map((_, i) => new Point(xCoords[i], yCoords[i])),
{
height: netInput.getInputHeight(batchIdx),
width : netInput.getInputWidth(batchIdx),
}
)
}
))
landmarkTensors.forEach(t => t.dispose())
return netInput.isBatchInput
? landmarksForBatch
: landmarksForBatch[0]
}
}
\ No newline at end of file
import { FaceLandmarkNet } from './FaceLandmarkNet';
import { FaceLandmark68Net } from './FaceLandmark68Net';
export * from './FaceLandmarkNet';
export * from './FaceLandmark68Net';
export class FaceLandmarkNet extends FaceLandmark68Net {}
export function createFaceLandmarkNet(weights: Float32Array) {
const net = new FaceLandmarkNet()
......
......@@ -23,7 +23,7 @@ export class FaceRecognitionNet extends NeuralNetwork<NetParams> {
}
return tf.tidy(() => {
const batchTensor = input.toBatchTensor(150, true)
const batchTensor = input.toBatchTensor(150, true).toFloat()
const meanRgb = [122.782, 117.001, 104.298]
const normalized = normalize(batchTensor, meanRgb).div(tf.scalar(256)) as tf.Tensor4D
......@@ -57,11 +57,11 @@ export class FaceRecognitionNet extends NeuralNetwork<NetParams> {
}
public async forward(input: TNetInput): Promise<tf.Tensor2D> {
return this.forwardInput(await toNetInput(input, true))
return this.forwardInput(await toNetInput(input))
}
public async computeFaceDescriptor(input: TNetInput): Promise<Float32Array|Float32Array[]> {
const netInput = await toNetInput(input, true)
const netInput = await toNetInput(input)
const faceDescriptorTensors = tf.tidy(
() => tf.unstack(this.forwardInput(netInput))
......
......@@ -7,14 +7,14 @@ import { FaceDetection } from './classes/FaceDetection';
import { FaceLandmarks68 } from './classes/FaceLandmarks68';
import { FullFaceDescription } from './classes/FullFaceDescription';
import { FaceDetectionNet } from './faceDetectionNet/FaceDetectionNet';
import { FaceLandmarkNet } from './faceLandmarkNet/FaceLandmarkNet';
import { FaceLandmark68Net } from './faceLandmarkNet/FaceLandmark68Net';
import { FaceRecognitionNet } from './faceRecognitionNet/FaceRecognitionNet';
import { Mtcnn } from './mtcnn/Mtcnn';
import { MtcnnForwardParams, MtcnnResult } from './mtcnn/types';
import { TinyYolov2 } from './tinyYolov2/TinyYolov2';
export const detectionNet = new FaceDetectionNet()
export const landmarkNet = new FaceLandmarkNet()
export const landmarkNet = new FaceLandmark68Net()
export const recognitionNet = new FaceRecognitionNet()
// nets need more specific names, to avoid ambiguity in future
......
......@@ -32,7 +32,6 @@ export class Mtcnn extends NeuralNetwork<NetParams> {
throw new Error('Mtcnn - load model before inference')
}
const inputTensor = input.inputs[0]
const inputCanvas = input.canvases[0]
if (!inputCanvas) {
......@@ -45,14 +44,13 @@ export class Mtcnn extends NeuralNetwork<NetParams> {
const imgTensor = tf.tidy(() =>
bgrToRgbTensor(
tf.expandDims(inputTensor).toFloat() as tf.Tensor4D
tf.expandDims(tf.fromPixels(inputCanvas)).toFloat() as tf.Tensor4D
)
)
const onReturn = (results: any) => {
// dispose tensors on return
imgTensor.dispose()
input.dispose()
stats.total = Date.now() - tsTotal
return results
}
......@@ -131,7 +129,7 @@ export class Mtcnn extends NeuralNetwork<NetParams> {
): Promise<MtcnnResult[]> {
return (
await this.forwardInput(
await toNetInput(input, true, true),
await toNetInput(input),
forwardParams
)
).results
......@@ -142,7 +140,7 @@ export class Mtcnn extends NeuralNetwork<NetParams> {
forwardParams: MtcnnForwardParams = {}
): Promise<{ results: MtcnnResult[], stats: any }> {
return this.forwardInput(
await toNetInput(input, true, true),
await toNetInput(input),
forwardParams
)
}
......
import { Box } from 'tfjs-image-recognition-base';
export class MtcnnBox extends Box<MtcnnBox> {
constructor(left: number, top: number, right: number, bottom: number) {
super({ left, top, right, bottom }, true)
}
}
\ No newline at end of file
import * as tf from '@tensorflow/tfjs-core';
import { Box, createCanvas, Dimensions, getContext2dOrThrow } from 'tfjs-image-recognition-base';
import { normalize } from './normalize';
import { BoundingBox, Dimensions, getContext2dOrThrow, createCanvas } from 'tfjs-image-recognition-base';
export async function extractImagePatches(
img: HTMLCanvasElement,
boxes: BoundingBox[],
boxes: Box[],
{ width, height }: Dimensions
): Promise<tf.Tensor4D[]> {
......
......@@ -3,6 +3,7 @@ import { BoundingBox, nonMaxSuppression, Point } from 'tfjs-image-recognition-ba
import { CELL_SIZE, CELL_STRIDE } from './config';
import { getSizesForScale } from './getSizesForScale';
import { MtcnnBox } from './MtcnnBox';
import { normalize } from './normalize';
import { PNet } from './PNet';
import { PNetParams } from './types';
......@@ -45,7 +46,7 @@ function extractBoundingBoxes(
const score = scoresTensor.get(idx.y, idx.x)
const region = new BoundingBox(
const region = new MtcnnBox(
regionsTensor.get(idx.y, idx.x, 0),
regionsTensor.get(idx.y, idx.x, 1),
regionsTensor.get(idx.y, idx.x, 2),
......
import * as tf from '@tensorflow/tfjs-core';
import { BoundingBox, nonMaxSuppression } from 'tfjs-image-recognition-base';
import { Box, nonMaxSuppression } from 'tfjs-image-recognition-base';
import { extractImagePatches } from './extractImagePatches';
import { MtcnnBox } from './MtcnnBox';
import { RNet } from './RNet';
import { RNetParams } from './types';
export async function stage2(
img: HTMLCanvasElement,
inputBoxes: BoundingBox[],
inputBoxes: Box[],
scoreThreshold: number,
params: RNetParams,
stats: any
......@@ -41,7 +42,7 @@ export async function stage2(
const filteredBoxes = indices.map(idx => inputBoxes[idx])
const filteredScores = indices.map(idx => scores[idx])
let finalBoxes: BoundingBox[] = []
let finalBoxes: Box[] = []
let finalScores: number[] = []
if (filteredBoxes.length > 0) {
......@@ -54,7 +55,7 @@ export async function stage2(
stats.stage2_nms = Date.now() - ts
const regions = indicesNms.map(idx =>
new BoundingBox(
new MtcnnBox(
rnetOuts[indices[idx]].regions.get(0, 0),
rnetOuts[indices[idx]].regions.get(0, 1),
rnetOuts[indices[idx]].regions.get(0, 2),
......
import * as tf from '@tensorflow/tfjs-core';
import { BoundingBox, nonMaxSuppression, Point } from 'tfjs-image-recognition-base';
import { BoundingBox, Box, nonMaxSuppression, Point } from 'tfjs-image-recognition-base';
import { extractImagePatches } from './extractImagePatches';
import { MtcnnBox } from './MtcnnBox';
import { ONet } from './ONet';
import { ONetParams } from './types';
......@@ -38,7 +39,7 @@ export async function stage3(
.filter(c => c.score > scoreThreshold)
.map(({ idx }) => idx)
const filteredRegions = indices.map(idx => new BoundingBox(
const filteredRegions = indices.map(idx => new MtcnnBox(
onetOuts[idx].regions.get(0, 0),
onetOuts[idx].regions.get(0, 1),
onetOuts[idx].regions.get(0, 2),
......@@ -48,7 +49,7 @@ export async function stage3(
.map((idx, i) => inputBoxes[idx].calibrate(filteredRegions[i]))
const filteredScores = indices.map(idx => scores[idx])
let finalBoxes: BoundingBox[] = []
let finalBoxes: Box[] = []
let finalScores: number[] = []
let points: Point[][] = []
......
[[-0.11301642656326294, 0.05311831831932068, 0.11299516260623932, 0.06697624176740646, -0.21439769864082336, 0.018440978601574898, -0.02246944047510624, -0.07858379185199738, 0.1299576610326767, -0.07627607882022858, 0.13028405606746674, -0.07381529361009598, -0.2666480243206024, 0.1386503130197525, -0.0318572111427784, 0.19745664298534393, -0.06815779209136963, -0.10036905854940414, -0.14179939031600952, -0.08129233121871948, 0.022137288004159927, 0.040264301002025604, 0.040526989847421646, 0.13235996663570404, -0.18518206477165222, -0.32066434621810913, -0.02661932446062565, -0.05834241956472397, 0.03743532672524452, -0.00874195247888565, -0.07306552678346634, 0.08665299415588379, -0.16994774341583252, -0.013914648443460464, 0.045591093599796295, 0.011259820312261581, -0.015375563874840736, -0.02478434517979622, 0.22394348680973053, 0.019354572519659996, -0.29177573323249817, 0.007424432784318924, 0.09004595875740051, 0.196985125541687, 0.17434047162532806, 0.07935666292905807, 0.10738393664360046, -0.08182166516780853, 0.09036535769701004, -0.35426175594329834, 0.004522169940173626, 0.14777712523937225, 0.03345843032002449, 0.019831661134958267, 0.05174245312809944, -0.21797265112400055, -0.0635240375995636, 0.1138913482427597, -0.14936882257461548, 0.13345786929130554, 0.09437544643878937, 0.013876287266612053, -0.05527258664369583, -0.09154847264289856, 0.19714784622192383, 0.12585902214050293, -0.1258784979581833, -0.14687272906303406, 0.15844950079917908, -0.22417423129081726, -0.009384687058627605, 0.16835945844650269, -0.0978832021355629, -0.23462620377540588, -0.2714555859565735, 0.013164130970835686, 0.5018162727355957, 0.15732169151306152, -0.17715050280094147, -0.05510217696428299, -0.028521616011857986, 0.016591474413871765, 0.06292184442281723, 0.06307955831289291, -0.05202113837003708, -0.04303480684757233, -0.06258511543273926, 0.04492054134607315, 0.2579847276210785, -0.030089672654867172, 0.034749776124954224, 0.2730059027671814, 0.10963069647550583, -0.018608057871460915, -0.03264498710632324, 0.17454616725444794, -0.14471100270748138, -0.05291951075196266, -0.15773987770080566, -0.00878031924366951, 0.013892430812120438, 0.03880078345537186, 0.06900060176849365, 0.07285363972187042, -0.1963559240102768, 0.1951112151145935, -0.1176108717918396, -0.0644276961684227, -0.05753452330827713, 0.0583438016474247, -0.13664516806602478, 0.008200451731681824, 0.18180623650550842, -0.19785352051258087, 0.08596935868263245, 0.18097352981567383, -0.02679232507944107, 0.0758017972111702, 0.04083810746669769, 0.06573939323425293, 0.028857335448265076, 0.02794247679412365, -0.1294119507074356, -0.12546013295650482, 0.09301090985536575, -0.18581253290176392, -0.00008967332541942596, 0.004743552301079035], [-0.09994355589151382, 0.10224375128746033, 0.05058574303984642, -0.007721252739429474, -0.017086468636989594, 0.07549440860748291, -0.035992272198200226, -0.05198703333735466, 0.21361662447452545, -0.12978722155094147, 0.240940660238266, 0.006456747651100159, -0.27226075530052185, -0.013018697500228882, -0.08444526791572571, 0.08976947516202927, -0.09226515889167786, -0.06434378027915955, -0.07935208082199097, -0.01867634989321232, 0.10694169998168945, 0.07422233372926712, 0.04553782194852829, -0.010077288374304771, -0.11393612623214722, -0.4091143012046814, -0.14219625294208527, -0.0598783977329731, -0.026971451938152313, -0.03787655383348465, 0.021513912826776505, -0.012842817232012749, -0.1919005811214447, -0.000048376619815826416, 0.014531563967466354, 0.06467536091804504, -0.07482472062110901, 0.012392744421958923, 0.1304527223110199, 0.014805854298174381, -0.13836951553821564, 0.02878289297223091, 0.033762313425540924, 0.28061288595199585, 0.10587205737829208, 0.0318145789206028, 0.00440259650349617, -0.11348624527454376, 0.036986637860536575, -0.26385852694511414, 0.1499217301607132, 0.14649057388305664, 0.11839955300092697, 0.10325036942958832, 0.17655009031295776, -0.09731947630643845, -0.03258107975125313, 0.14808624982833862, -0.15074148774147034, 0.10213347524404526, 0.06218596547842026, 0.06304526329040527, -0.019552389159798622, -0.10384533554315567, 0.13429109752178192, 0.14194521307945251, -0.16596026718616486, -0.19037191569805145, 0.04928822070360184, -0.1992926299571991, 0.017874792218208313, 0.1040906235575676, -0.08356981724500656, -0.2674674391746521, -0.26023608446121216, 0.12374836206436157, 0.42628446221351624, 0.20582066476345062, -0.14651867747306824, -0.026605937629938126, -0.057607077062129974, -0.06105601787567139, 0.06685034930706024, 0.00046340934932231903, -0.08286283910274506, 0.025767460465431213, -0.06461362540721893, 0.0723767876625061, 0.17591258883476257, -0.006310688331723213, -0.07715718448162079, 0.25646382570266724, -0.04985096678137779, 0.0062642693519592285, 0.00570443831384182, 0.046732913702726364, -0.10989770293235779, -0.003452849108725786, -0.1805979162454605, 0.024943644180893898, -0.07201632112264633, -0.10125844925642014, -0.04925733804702759, 0.021053487434983253, -0.13046781718730927, 0.1533353179693222, -0.01654619723558426, -0.06076410412788391, -0.014542249031364918, 0.05473938211798668, -0.26792800426483154, -0.009533338248729706, 0.1733490526676178, -0.29974472522735596, 0.20299524068832397, 0.1027199774980545, 0.08181609213352203, 0.16548770666122437, 0.1160709485411644, 0.08434373885393143, 0.01546089630573988, -0.02321481704711914, -0.11887650936841965, -0.07879133522510529, 0.04050300642848015, -0.018666662275791168, 0.02767147496342659, -0.0409514494240284], [-0.12437328696250916, 0.16371744871139526, 0.057088494300842285, -0.08345270156860352, -0.1361895501613617, -0.007239761762320995, -0.0027456339448690414, -0.0022191042080521584, 0.1767570823431015, -0.029083389788866043, 0.1353265941143036, -0.020310351625084877, -0.287823885679245, 0.03621964901685715, -0.1308278739452362, 0.1081429049372673, -0.23993228375911713, -0.10986220091581345, -0.05526617169380188, -0.16110815107822418, 0.0338473841547966, 0.1609116494655609, -0.031369976699352264, 0.04462018609046936, -0.12118063867092133, -0.26080891489982605, -0.005951732397079468, -0.1575908213853836, 0.0643172413110733, -0.1461418718099594, 0.07567547261714935, 0.107530377805233, -0.09414676576852798, -0.04453685134649277, 0.006743345409631729, 0.0339377298951149, -0.015679262578487396, -0.073453888297081, 0.21779049932956696, 0.05521396920084953, -0.1335650384426117, 0.010233767330646515, 0.0664336159825325, 0.430218368768692, 0.13693466782569885, -0.0009829048067331314, 0.0017257444560527802, -0.04437744617462158, 0.15366870164871216, -0.21540115773677826, 0.0167088583111763, 0.1910901665687561, 0.05646311119198799, 0.043141767382621765, 0.1210416778922081, -0.17660900950431824, 0.02089228481054306, 0.10505655407905579, -0.22187289595603943, 0.08246994763612747, 0.1212199255824089, 0.001875532791018486, 0.007675278931856155, -0.11065125465393066, 0.11531264334917068, -0.0005581285804510117, -0.08609281480312347, -0.12632550299167633, 0.11016186326742172, -0.18078474700450897, -0.1049041897058487, 0.1042267382144928, -0.10656270384788513, -0.11614145338535309, -0.22367611527442932, 0.02147454395890236, 0.3818414807319641, 0.19803355634212494, -0.04573599621653557, 0.03495173156261444, -0.02827727422118187, -0.07291625440120697, 0.09016091376543045, 0.022530820220708847, -0.16063527762889862, 0.008984813466668129, -0.06500966101884842, 0.06870897859334946, 0.1445840448141098, 0.07035897672176361, -0.09533844888210297, 0.16255708038806915, 0.03426520526409149, -0.07016268372535706, 0.020144084468483925, 0.0068038590252399445, -0.14746502041816711, 0.02669757604598999, -0.1343773752450943, -0.0007380247116088867, 0.036735646426677704, -0.16095727682113647, 0.003342229872941971, 0.09120983630418777, -0.18368342518806458, 0.12250301241874695, 0.014707496389746666, -0.011677264235913754, -0.09200786054134369, 0.054589901119470596, -0.17861320078372955, 0.08242300897836685, 0.2472871094942093, -0.2322787046432495, 0.17269502580165863, 0.16788843274116516, 0.047502290457487106, 0.1130535900592804, 0.10760728269815445, -0.010811767540872097, -0.047366999089717865, -0.04577387124300003, -0.12238828837871552, -0.1456669420003891, 0.06693950295448303, -0.025252800434827805, 0.12544143199920654, 0.0265400018543005], [0.025927569717168808, 0.15663708746433258, 0.058965474367141724, -0.028551187366247177, -0.12631410360336304, 0.022237226366996765, 0.01126374676823616, -0.1872863471508026, 0.1682029664516449, -0.002070157788693905, 0.15346597135066986, -0.18673506379127502, -0.278525710105896, 0.04948587715625763, -0.03619107976555824, 0.1413515955209732, -0.060395412147045135, -0.2583189606666565, -0.13808242976665497, -0.07600552588701248, 0.04279348626732826, -0.0666167140007019, 0.05459671467542648, 0.149562269449234, -0.23997187614440918, -0.3192921280860901, -0.06438620388507843, -0.06955230236053467, -0.016590990126132965, -0.13756340742111206, 0.016080018132925034, 0.10797879099845886, -0.1965179443359375, 0.0034376978874206543, 0.06646829843521118, 0.05213041231036186, -0.032798610627651215, -0.20469960570335388, 0.20994003117084503, 0.004714690148830414, -0.24007561802864075, -0.025683866813778877, 0.092221200466156, 0.29108813405036926, 0.21951010823249817, -0.012189175933599472, 0.11264907568693161, -0.060570698231458664, 0.03355713561177254, -0.25495976209640503, 0.05203044414520264, 0.2157290279865265, 0.04515017941594124, 0.07426977157592773, 0.045807648450136185, -0.12190262973308563, -0.033570460975170135, 0.16843782365322113, -0.1834886521100998, 0.09117748588323593, 0.15288689732551575, -0.06749839335680008, -0.08566926419734955, -0.19639848172664642, 0.2661381959915161, 0.18407370150089264, -0.1834886372089386, -0.16875605285167694, 0.12717457115650177, -0.18597885966300964, -0.11863835155963898, 0.05518629774451256, -0.18559770286083221, -0.16873560845851898, -0.28400951623916626, -0.014187514781951904, 0.45283856987953186, 0.17721779644489288, -0.2146177440881729, 0.011182520538568497, -0.09624088555574417, 0.10169383138418198, -0.035505618900060654, 0.1689070165157318, -0.0015639550983905792, 0.018879884853959084, -0.07630802690982819, 0.05508717522025108, 0.22385680675506592, -0.03961780667304993, -0.07674316316843033, 0.23865419626235962, -0.04872302711009979, -0.09690545499324799, -0.06860771775245667, 0.05698356404900551, -0.14728271961212158, 0.010753602720797062, -0.08920906484127045, -0.04629332199692726, -0.038761310279369354, 0.09933528304100037, 0.031854718923568726, 0.14741075038909912, -0.17927861213684082, 0.1629253327846527, -0.04969882220029831, 0.00284720491617918, -0.010596400126814842, -0.0009414497762918472, -0.046387046575546265, -0.0606590211391449, 0.14352722465991974, -0.3583222031593323, 0.06242058053612709, 0.1551140546798706, 0.052389003336429596, 0.04141424223780632, 0.01068437471985817, 0.10475203394889832, 0.11781509220600128, 0.034557536244392395, -0.16312718391418457, -0.06071166321635246, 0.024744128808379173, -0.029767779633402824, -0.00618806853890419, 0.03224271535873413], [-0.0962543934583664, -0.03510488569736481, -0.004949606955051422, 0.008284758776426315, -0.09557978063821793, -0.03148920461535454, -0.0035469243302941322, -0.03148280084133148, 0.15762394666671753, 0.018902912735939026, 0.20940224826335907, -0.0049227150157094, -0.23696963489055634, -0.006771348416805267, -0.053370341658592224, 0.06698818504810333, -0.13303788006305695, -0.08625766634941101, -0.08649979531764984, -0.10049654543399811, 0.06255536526441574, 0.11754642426967621, 0.02521955966949463, 0.08287493139505386, -0.13028612732887268, -0.29643961787223816, -0.11251382529735565, -0.1740148663520813, 0.02946341782808304, -0.14464221894741058, 0.003542322665452957, 0.15385982394218445, -0.14348679780960083, -0.08776889741420746, 0.06114386394619942, 0.044328175485134125, -0.028169168159365654, -0.019242623820900917, 0.1819373369216919, 0.12094853073358536, -0.11942562460899353, 0.013642464764416218, -0.00580492801964283, 0.4194585978984833, 0.17064036428928375, 0.03833707049489021, 0.023577354848384857, -0.0596885085105896, 0.10573002696037292, -0.3004778027534485, 0.13485471904277802, 0.162809818983078, 0.1528499722480774, 0.02323446422815323, 0.11265985667705536, -0.15413106977939606, 0.013475492596626282, 0.13343635201454163, -0.18546022474765778, 0.23613464832305908, 0.1127074807882309, -0.07481369376182556, -0.03090173937380314, -0.11048624664545059, 0.14306733012199402, 0.06088583171367645, -0.10998954623937607, -0.0625547245144844, 0.1647409200668335, -0.115761898458004, -0.07907391339540482, 0.11408128589391708, -0.17270313203334808, -0.2541918158531189, -0.265663743019104, 0.05659070611000061, 0.5032548904418945, 0.1484411507844925, -0.21916182339191437, -0.004813231527805328, -0.049820076674222946, -0.00413606408983469, 0.07484494149684906, 0.07051379978656769, -0.08695848286151886, -0.023705370724201202, -0.12480843812227249, 0.07716438174247742, 0.18726631999015808, 0.022125976160168648, -0.06583775579929352, 0.24017201364040375, -0.004352990537881851, -0.004003781825304031, 0.09011345356702805, 0.05776156485080719, -0.04413218796253204, -0.057881422340869904, -0.15213117003440857, -0.021110566332936287, -0.004055136814713478, -0.16205355525016785, -0.04073438048362732, 0.06350117921829224, -0.14136318862438202, 0.18425655364990234, 0.015558455139398575, -0.038499198853969574, -0.06519972532987595, -0.046765126287937164, -0.12038338929414749, 0.06919427961111069, 0.22361968457698822, -0.25389334559440613, 0.1741548627614975, 0.15434733033180237, 0.11286289244890213, 0.15635934472084045, -0.038396697491407394, 0.058131638914346695, -0.06190327927470207, -0.06887871026992798, -0.19518369436264038, -0.06716073304414749, 0.03750930726528168, -0.016213154420256615, -0.0019600093364715576, 0.03146345913410187], [-0.08438469469547272, 0.034433476626873016, 0.013390637934207916, -0.11032305657863617, -0.1721678525209427, -0.014739526435732841, 0.013159649446606636, -0.04383833706378937, 0.22945237159729004, -0.18055742979049683, 0.10724801570177078, -0.10486509650945663, -0.17328080534934998, 0.11445727944374084, -0.1428213119506836, 0.19179785251617432, -0.1129641979932785, -0.22180527448654175, -0.18176206946372986, -0.17263242602348328, 0.001636851578950882, 0.17328158020973206, -0.08476416766643524, 0.1016133576631546, -0.20712536573410034, -0.2548363208770752, -0.013701280578970909, -0.12087295949459076, 0.028648169711232185, -0.14270061254501343, 0.005408044904470444, 0.16749466955661774, -0.09364913403987885, 0.060463741421699524, 0.0917007252573967, 0.05608232319355011, -0.02542371302843094, -0.11898399889469147, 0.18762332201004028, 0.08870362490415573, -0.21150806546211243, -0.05339662358164787, 0.06960764527320862, 0.342587411403656, 0.24152815341949463, -0.055178068578243256, 0.006454486399888992, -0.12146948277950287, 0.1976870596408844, -0.29518774151802063, 0.03274869918823242, 0.12191275507211685, 0.030610322952270508, 0.14941972494125366, 0.09244397282600403, -0.18991874158382416, -0.0002718530595302582, 0.16477075219154358, -0.17079557478427887, 0.0982893779873848, 0.07169570028781891, -0.14121730625629425, -0.09334538877010345, -0.12710705399513245, 0.2545923888683319, 0.0946723222732544, -0.1666725128889084, -0.21636250615119934, 0.2331445813179016, -0.1981041133403778, -0.0874897837638855, 0.06129404529929161, -0.11788947135210037, -0.11157948523759842, -0.19723665714263916, 0.0467531718313694, 0.3331378996372223, 0.15040019154548645, -0.11666619032621384, 0.07890522480010986, -0.014858516864478588, 0.023824408650398254, -0.011289974674582481, 0.1570158302783966, -0.06161228567361832, -0.08446240425109863, -0.009107670746743679, 0.04418444260954857, 0.22349263727664948, -0.03572162613272667, -0.019860783591866493, 0.22091175615787506, -0.07901038229465485, -0.06882502138614655, -0.033531900495290756, 0.05211462080478668, -0.23550309240818024, 0.05773200839757919, -0.0063783712685108185, 0.00804441049695015, 0.0771300420165062, -0.01622505486011505, 0.031227124854922295, 0.15908673405647278, -0.12141232192516327, 0.24367079138755798, -0.15001045167446136, 0.03848479688167572, -0.008541088551282883, -0.055416084825992584, -0.03920590132474899, 0.006571357604116201, 0.17505057156085968, -0.2050420194864273, 0.187688946723938, 0.0819040909409523, 0.10374179482460022, 0.15251339972019196, 0.04850784316658974, 0.04916004836559296, 0.07320357859134674, 0.0493251234292984, -0.24662616848945618, -0.02634003758430481, 0.08344380557537079, -0.09143543243408203, -0.0650162473320961, 0.04578257352113724]]
\ No newline at end of file
[[-0.14404241740703583, 0.020420558750629425, 0.12159731984138489, 0.06673850864171982, -0.1845773458480835, -0.006459429860115051, -0.018922673538327217, -0.09011562168598175, 0.1014566421508789, -0.06833116710186005, 0.1313943713903427, -0.059500157833099365, -0.2429366409778595, 0.12969601154327393, -0.03123823180794716, 0.17403516173362732, -0.06976033747196198, -0.08903076499700546, -0.1670686900615692, -0.0905330702662468, 0.026567362248897552, 0.059487584978342056, 0.05232132971286774, 0.10820496082305908, -0.16574358940124512, -0.31992703676223755, -0.021159984171390533, -0.07648815214633942, 0.061189644038677216, 0.01882709003984928, -0.048110153526067734, 0.09231358021497726, -0.16308540105819702, -0.0308229923248291, 0.08153855800628662, 0.050749342888593674, 0.013089634478092194, 0.010833777487277985, 0.22493457794189453, -0.005780760198831558, -0.3017699122428894, 0.009801583364605904, 0.10426986962556839, 0.20322877168655396, 0.18211282789707184, 0.11280057579278946, 0.08282452076673508, -0.10828351974487305, 0.10574567317962646, -0.33380937576293945, 0.017940619960427284, 0.14281710982322693, 0.026437608525156975, 0.02825773134827614, 0.07432245463132858, -0.2295369654893875, -0.036365751177072525, 0.08971801400184631, -0.14061452448368073, 0.10517840832471848, 0.08703923225402832, 0.015264930203557014, -0.028071045875549316, -0.09756545722484589, 0.1787717193365097, 0.0928979441523552, -0.12441863119602203, -0.13970641791820526, 0.16826023161411285, -0.2287701666355133, -0.007024107500910759, 0.17693987488746643, -0.10933536291122437, -0.19572770595550537, -0.29248538613319397, -0.022097650915384293, 0.5018369555473328, 0.1315133422613144, -0.17158204317092896, -0.06210331246256828, -0.03497014939785004, 0.00930861197412014, 0.06882740557193756, 0.06423769891262054, -0.05281900614500046, -0.03944452479481697, -0.040059495717287064, 0.02342894673347473, 0.25209322571754456, -0.018084552139043808, 0.0385407917201519, 0.24800404906272888, 0.10142052918672562, -0.01649537868797779, -0.01963762380182743, 0.18608947098255157, -0.1553012877702713, -0.061059728264808655, -0.17497558891773224, -0.018999110907316208, 0.0031996723264455795, 0.03891829773783684, 0.08639538288116455, 0.0968272015452385, -0.2213348150253296, 0.19600607454776764, -0.12203031778335571, -0.05167662352323532, -0.03981113061308861, 0.05974903330206871, -0.10548581928014755, 0.006194081157445908, 0.18520109355449677, -0.18155987560749054, 0.10244937986135483, 0.20877017080783844, -0.029200386255979538, 0.0688735619187355, 0.035200126469135284, 0.061611589044332504, 0.0020254189148545265, 0.04999671131372452, -0.1587153673171997, -0.12486191838979721, 0.06645583361387253, -0.1836017519235611, -0.01840708777308464, 0.008367311209440231],
[-0.07981500774621964, 0.11756777763366699, 0.02936466969549656, -0.09633475542068481, -0.15267357230186462, -0.011278534308075905, -0.001300307922065258, 0.0360361747443676, 0.17416930198669434, -0.017560135573148727, 0.13196146488189697, -0.06830444186925888, -0.29480257630348206, -0.03122542053461075, -0.06372717022895813, 0.09800832718610764, -0.23692278563976288, -0.10906898230314255, -0.01810368150472641, -0.1275140941143036, 0.0789264440536499, 0.09011727571487427, -0.024859212338924408, 0.09766484051942825, -0.11434200406074524, -0.2884414494037628, -0.025204341858625412, -0.18114201724529266, 0.10241267085075378, -0.1564866155385971, 0.03878610581159592, 0.09216093271970749, -0.08629581332206726, -0.07612591236829758, -0.007761821150779724, 0.05982988327741623, -0.01618112437427044, -0.11326153576374054, 0.1909821480512619, 0.03072071261703968, -0.1123855710029602, 0.008064975962042809, 0.02313450537621975, 0.4029819071292877, 0.13979993760585785, 0.03689645603299141, 0.0571918711066246, 0.006454968824982643, 0.1722615510225296, -0.21643899381160736, 0.04860049486160278, 0.1964837908744812, 0.09936783462762833, 0.013555353507399559, 0.1356257051229477, -0.15182176232337952, 0.014821959659457207, 0.14483428001403809, -0.22364187240600586, 0.1065005213022232, 0.10592366009950638, 0.04265245050191879, 0.010688506066799164, -0.11790886521339417, 0.15378248691558838, 0.02181466668844223, -0.09279246628284454, -0.10258939862251282, 0.13707773387432098, -0.1889384388923645, -0.1254684329032898, 0.06016359105706215, -0.14417685568332672, -0.0884396955370903, -0.19703182578086853, 0.008006387390196323, 0.3649013340473175, 0.17656049132347107, -0.10456853359937668, 0.034014202654361725, -0.02204163931310177, -0.030664954334497452, 0.09979542344808578, 0.05958552658557892, -0.11041656881570816, -0.0012588966637849808, -0.09470727294683456, 0.029211319983005524, 0.1301136016845703, 0.02824041247367859, -0.05213681235909462, 0.16133788228034973, 0.06453216075897217, -0.06588482856750488, 0.03479577228426933, -0.04281643033027649, -0.11604544520378113, 0.017909755930304527, -0.10244867205619812, 0.0026457589119672775, 0.03951391205191612, -0.15675100684165955, -0.03322248160839081, 0.06447272002696991, -0.1749180257320404, 0.1186823770403862, 0.018416468054056168, 0.0057243346236646175, -0.0821719840168953, 0.04123109579086304, -0.1651352494955063, 0.0557333379983902, 0.22582757472991943, -0.2417983114719391, 0.14351877570152283, 0.17512904107570648, 0.0387265682220459, 0.09988535940647125, 0.04966028034687042, 0.030464978888630867, -0.008326411247253418, -0.05624760314822197, -0.1279410421848297, -0.1480567455291748, 0.05000598356127739, -0.020273571833968163, 0.06707905232906342, 0.01750762201845646],
[-0.0737464651465416, 0.10227955877780914, 0.045663461089134216, 0.002152875065803528, 0.009061860851943493, 0.041457511484622955, -0.0363280288875103, -0.029537897557020187, 0.20743197202682495, -0.08834147453308105, 0.24489930272102356, 0.032994892448186874, -0.2592104375362396, -0.020188704133033752, -0.08490309864282608, 0.0849633663892746, -0.11557746678590775, -0.06636104732751846, -0.09154438227415085, -0.050566721707582474, 0.09158705174922943, 0.10730121284723282, 0.07843941450119019, -0.004716010764241219, -0.10810066014528275, -0.39324918389320374, -0.1305016577243805, -0.08330740034580231, -0.0036739520728588104, -0.030765876173973083, 0.019488206133246422, 0.004923966247588396, -0.20298710465431213, -0.03460344672203064, 0.044474586844444275, 0.07522155344486237, -0.07436703890562057, 0.012282747775316238, 0.12453831732273102, 0.04521659389138222, -0.10746897011995316, 0.0348062664270401, 0.06490033864974976, 0.30782240629196167, 0.13561832904815674, 0.04975732043385506, -0.003293197602033615, -0.11471724510192871, 0.03620770573616028, -0.2719414532184601, 0.16933363676071167, 0.14766038954257965, 0.13480553030967712, 0.12729986011981964, 0.19226641952991486, -0.12410514801740646, -0.004798094276338816, 0.1369594782590866, -0.14226339757442474, 0.09994019567966461, 0.050312548875808716, 0.08937902748584747, -0.0024147313088178635, -0.08850325644016266, 0.13694575428962708, 0.1042422354221344, -0.15734383463859558, -0.1722884625196457, 0.01907198503613472, -0.1980213075876236, 0.008401932194828987, 0.1310276836156845, -0.08898252993822098, -0.2503238320350647, -0.2706325948238373, 0.1248704269528389, 0.40481770038604736, 0.20478859543800354, -0.12876024842262268, -0.006813257932662964, -0.04922744259238243, -0.06709268689155579, 0.04355831816792488, -0.003456573933362961, -0.11132106184959412, 0.04055509716272354, -0.08725453913211823, 0.06786619126796722, 0.15359823405742645, 0.004168705083429813, -0.08209235966205597, 0.2625846266746521, -0.06305798888206482, 0.018265152350068092, 0.02244562655687332, 0.05237526446580887, -0.1300857961177826, -0.004572143778204918, -0.20017029345035553, -0.012051284313201904, -0.061292197555303574, -0.09332993626594543, -0.06143403425812721, 0.015400122851133347, -0.13490766286849976, 0.11769682168960571, -0.028142036870121956, -0.06330903619527817, -0.018554333597421646, 0.03908921405673027, -0.25225508213043213, -0.016484694555401802, 0.17153753340244293, -0.2887243330478668, 0.19834962487220764, 0.11764566600322723, 0.0824105441570282, 0.16050225496292114, 0.12200618535280228, 0.06956330686807632, -0.007610949221998453, -0.029755599796772003, -0.11322794109582901, -0.06064249575138092, 0.032834120094776154, -0.017432063817977905, 0.02160019613802433, -0.0409822091460228],
[0.02198081463575363, 0.14465422928333282, 0.07101303339004517, -0.044889725744724274, -0.13926607370376587, 0.04856165871024132, 0.017496149986982346, -0.19866913557052612, 0.18785734474658966, -0.026991918683052063, 0.15308845043182373, -0.17339034378528595, -0.2501252293586731, 0.058495186269283295, -0.03581349924206734, 0.13019725680351257, -0.05490950495004654, -0.26040923595428467, -0.1364300549030304, -0.08451102674007416, 0.025452375411987305, -0.07919269055128098, 0.04486474394798279, 0.1439180076122284, -0.25699424743652344, -0.29463642835617065, -0.07614881545305252, -0.08073395490646362, 0.0010398533195257187, -0.13200102746486664, 0.019853388890624046, 0.10385788977146149, -0.21561194956302643, 0.0026465803384780884, 0.06799249351024628, 0.07691547274589539, -0.044076863676309586, -0.17641012370586395, 0.2100786417722702, 0.007020076736807823, -0.25046873092651367, -0.00371517613530159, 0.09093756973743439, 0.2862309515476227, 0.21583841741085052, -0.017651448026299477, 0.09234227985143661, -0.055896956473588943, 0.06638403236865997, -0.25630074739456177, 0.051851771771907806, 0.2166277915239334, 0.047334928065538406, 0.06653263419866562, 0.06419968605041504, -0.10982727259397507, -0.020135242491960526, 0.16812273859977722, -0.1735689789056778, 0.10705995559692383, 0.1650555431842804, -0.09183922410011292, -0.1027933657169342, -0.19209715723991394, 0.23922601342201233, 0.16908636689186096, -0.1708422303199768, -0.17500729858875275, 0.13317111134529114, -0.18402671813964844, -0.09851723909378052, 0.08667218685150146, -0.18973618745803833, -0.17125304043293, -0.28838276863098145, 0.005678129382431507, 0.4719759225845337, 0.18350759148597717, -0.21023567020893097, 0.018875613808631897, -0.06975957006216049, 0.11724178493022919, -0.021086065098643303, 0.17786994576454163, 0.00879625603556633, 0.0294604804366827, -0.060828305780887604, 0.05347011238336563, 0.21962711215019226, -0.04817062243819237, -0.05765945091843605, 0.23210160434246063, -0.050482627004384995, -0.08786634355783463, -0.06896024942398071, 0.04401649907231331, -0.14302408695220947, -0.020030740648508072, -0.11611396074295044, -0.04479845613241196, -0.05181102827191353, 0.10492114722728729, 0.02000310830771923, 0.13014227151870728, -0.1863778680562973, 0.15746904909610748, -0.054266855120658875, -0.0250262301415205, -0.033258792012929916, -0.011298771016299725, -0.04757513850927353, -0.05224163457751274, 0.15656690299510956, -0.3682935833930969, 0.06213179603219032, 0.16651077568531036, 0.03426094353199005, 0.03080114722251892, 0.02673438750207424, 0.08827059715986252, 0.1152452826499939, 0.04749817028641701, -0.14758148789405823, -0.07230862230062485, 0.024923574179410934, -0.05493896082043648, -0.018974684178829193, 0.0009641367942094803],
[-0.06684798002243042, 0.05922340601682663, 0.03716277703642845, -0.11239336431026459, -0.18310244381427765, -0.010310834273695946, 0.01709100976586342, -0.04950016736984253, 0.23486261069774628, -0.1753203421831131, 0.10588856041431427, -0.11691691726446152, -0.20737314224243164, 0.12660200893878937, -0.15450622141361237, 0.16686448454856873, -0.09791915118694305, -0.23812399804592133, -0.1779497265815735, -0.1746053546667099, -0.01481611281633377, 0.15330976247787476, -0.07816223055124283, 0.0861457884311676, -0.22246965765953064, -0.2708512544631958, -0.02174360305070877, -0.10684919357299805, 0.021600646898150444, -0.1305447816848755, 0.017891542986035347, 0.16137775778770447, -0.12418492138385773, 0.0313592366874218, 0.07718440145254135, 0.055879898369312286, -0.03148127347230911, -0.12486975640058517, 0.189072847366333, 0.07495227456092834, -0.2095707654953003, -0.044153619557619095, 0.09027774631977081, 0.3525503873825073, 0.23498742282390594, -0.04670509696006775, 0.023158598691225052, -0.1177847608923912, 0.19942888617515564, -0.28232964873313904, 0.04704594612121582, 0.12507376074790955, 0.04618150368332863, 0.15522485971450806, 0.06179865822196007, -0.1984272003173828, -0.00590172503143549, 0.18643082678318024, -0.16332827508449554, 0.09756657481193542, 0.08710655570030212, -0.12244651466608047, -0.09849074482917786, -0.11016225069761276, 0.24822495877742767, 0.08270874619483948, -0.1605307161808014, -0.20422376692295074, 0.23375490307807922, -0.1974012851715088, -0.06580758094787598, 0.053780052810907364, -0.12804099917411804, -0.09998556971549988, -0.19582915306091309, 0.06955525279045105, 0.3811401128768921, 0.1432715505361557, -0.0868157297372818, 0.0744328498840332, -0.03472564369440079, 0.03826254606246948, -0.036687128245830536, 0.14493891596794128, -0.08616751432418823, -0.07286832481622696, -0.013981971889734268, 0.07470613718032837, 0.22572174668312073, -0.0338052362203598, -0.028185199946165085, 0.2109299749135971, -0.05843852832913399, -0.05966894328594208, -0.030498074367642403, 0.054557397961616516, -0.2513957619667053, 0.06607527285814285, -0.027195891365408897, 0.014261949807405472, 0.07442091405391693, -0.008841129019856453, 0.037929460406303406, 0.15702521800994873, -0.13151004910469055, 0.24407081305980682, -0.12499392777681351, 0.036869585514068604, 0.006168995052576065, -0.03415437415242195, -0.026657897979021072, -0.02339569479227066, 0.17248684167861938, -0.2094629853963852, 0.153985857963562, 0.08107446879148483, 0.10295483469963074, 0.1670103818178177, 0.06929833441972733, 0.049807313829660416, 0.09714333713054657, 0.055026426911354065, -0.23590205609798431, -0.03289901092648506, 0.08273593336343765, -0.07015003263950348, -0.04177507013082504, 0.05598895624279976],
[-0.08472533524036407, -0.07459762692451477, 0.02486378885805607, 0.04267622157931328, -0.10762648284435272, -0.018162181600928307, -0.017197387292981148, -0.008626183494925499, 0.1363646239042282, 0.02008953131735325, 0.2114802598953247, -0.009057514369487762, -0.22394396364688873, 0.015354037284851074, -0.07964403927326202, 0.07980112731456757, -0.09115736186504364, -0.09144072234630585, -0.10522830486297607, -0.06722363084554672, 0.014975335448980331, 0.1418062299489975, 0.0029802080243825912, 0.07494466006755829, -0.09335871040821075, -0.2960969805717468, -0.07542618364095688, -0.14996489882469177, 0.03338634967803955, -0.1633545607328415, -0.029880113899707794, 0.15029563009738922, -0.16898927092552185, -0.09774809330701828, 0.0788545161485672, 0.034175511449575424, -0.05005204305052757, -0.04270431026816368, 0.22953447699546814, 0.1380377858877182, -0.14177939295768738, 0.021587608382105827, -0.00894791167229414, 0.39631208777427673, 0.14080992341041565, 0.023825256153941154, 0.016406040638685226, -0.06129332631826401, 0.13194742798805237, -0.3377301096916199, 0.1386328637599945, 0.16220861673355103, 0.17769357562065125, 0.03535967692732811, 0.0856858417391777, -0.20303654670715332, 0.0032975226640701294, 0.16135014593601227, -0.21643751859664917, 0.2512819170951843, 0.05723767727613449, -0.06505630910396576, -0.022172819823026657, -0.0987735390663147, 0.1570340096950531, 0.07361240684986115, -0.09804873913526535, -0.1041354089975357, 0.15816734731197357, -0.10689423233270645, -0.03592795878648758, 0.156087726354599, -0.141366645693779, -0.27767252922058105, -0.24497196078300476, 0.06890939176082611, 0.4552178680896759, 0.12109412252902985, -0.22622929513454437, 0.023518376052379608, -0.048931121826171875, -0.03883190080523491, 0.040007587522268295, 0.08146515488624573, -0.05167054384946823, 0.002223951742053032, -0.08394366502761841, 0.045353055000305176, 0.1902400106191635, 0.03459157049655914, -0.06566184014081955, 0.25482630729675293, -0.012820836156606674, -0.012055132538080215, 0.09240768849849701, 0.05135868862271309, -0.03279019147157669, -0.05750930681824684, -0.12544241547584534, -0.01694473810493946, -0.010068877600133419, -0.13741612434387207, -0.01865634135901928, 0.03838706389069557, -0.16031154990196228, 0.18955636024475098, -0.022180922329425812, -0.027361523360013962, -0.04520798474550247, -0.08959363400936127, -0.09608935564756393, 0.08018374443054199, 0.22481490671634674, -0.31406888365745544, 0.2105807214975357, 0.11666230857372284, 0.10967878252267838, 0.1732422262430191, -0.041490040719509125, 0.025021061301231384, -0.024598080664873123, -0.061457376927137375, -0.20317186415195465, -0.06095671281218529, 0.058688051998615265, -0.04292350634932518, -0.008037589490413666, 0.04240957275032997]]
\ No newline at end of file
[[-0.12167462706565857, 0.1573014110326767, 0.04441952705383301, -0.06329992413520813, -0.13990068435668945, -0.015591192059218884, 0.012788704596459866, -0.008481698110699654, 0.1696063131093979, -0.014996049925684929, 0.12257716059684753, -0.03980429470539093, -0.29841849207878113, 0.03535076230764389, -0.15113383531570435, 0.11026836931705475, -0.2229827493429184, -0.10611432790756226, -0.05942731350660324, -0.16390231251716614, 0.046904243528842926, 0.14842212200164795, -0.025903970003128052, 0.04469270259141922, -0.12351308763027191, -0.25148382782936096, -0.005807774141430855, -0.1652514785528183, 0.05275378376245499, -0.15742221474647522, 0.08188007771968842, 0.11311312764883041, -0.09362713247537613, -0.04295402765274048, 0.008263520896434784, 0.03907023370265961, -0.009292371571063995, -0.05819075182080269, 0.21716953814029694, 0.0575837567448616, -0.13047903776168823, 0.006304113194346428, 0.06138543412089348, 0.4273340702056885, 0.13800019025802612, -0.0001382799819111824, 0.0032735206186771393, -0.04304564744234085, 0.14757899940013885, -0.20370358228683472, 0.018630407750606537, 0.19079533219337463, 0.040192872285842896, 0.05428604409098625, 0.12165690213441849, -0.17821815609931946, 0.013662583194673061, 0.11021769046783447, -0.20762072503566742, 0.08905413001775742, 0.12038201838731766, 0.010113406926393509, 0.0077995555475354195, -0.09915349632501602, 0.10829996317625046, -0.014124175533652306, -0.08572334796190262, -0.11928706616163254, 0.13431409001350403, -0.1893075704574585, -0.10338127613067627, 0.09048596024513245, -0.10543692857027054, -0.11666816473007202, -0.21868345141410828, 0.0248338021337986, 0.3980242908000946, 0.19333718717098236, -0.05901395529508591, 0.028305314481258392, -0.029147764667868614, -0.07272812724113464, 0.0961914211511612, 0.025258097797632217, -0.1411222219467163, 0.017706695944070816, -0.06849132478237152, 0.05856060981750488, 0.14139112830162048, 0.06593677401542664, -0.09715200215578079, 0.16674935817718506, 0.0324891097843647, -0.06945095956325531, 0.02925178036093712, 0.0032800473272800446, -0.1456555873155594, 0.029739035293459892, -0.14144563674926758, -0.004989258944988251, 0.025253115221858025, -0.1740659773349762, -0.005913650617003441, 0.08276288956403732, -0.16652812063694, 0.12548843026161194, 0.02236875519156456, -0.005242745392024517, -0.11953147500753403, 0.0622311532497406, -0.1847297102212906, 0.09291570633649826, 0.2522027790546417, -0.22897060215473175, 0.18871718645095825, 0.16927888989448547, 0.04851491004228592, 0.10886490345001221, 0.10575688630342484, -0.004544427618384361, -0.02665218710899353, -0.044908855110406876, -0.11539579927921295, -0.1501932591199875, 0.056034334003925323, -0.022210951894521713, 0.10771076381206512, 0.03321328014135361],
[0.026710156351327896, 0.15993016958236694, 0.051298901438713074, -0.027689505368471146, -0.12765391170978546, 0.02057567425072193, 0.011408006772398949, -0.18867585062980652, 0.16971813142299652, -0.005916532129049301, 0.15141794085502625, -0.19089892506599426, -0.273203045129776, 0.04969186335802078, -0.03790351748466492, 0.1444900929927826, -0.05998031422495842, -0.2574175000190735, -0.13791732490062714, -0.07319991290569305, 0.04399561882019043, -0.07099040597677231, 0.049827899783849716, 0.15421688556671143, -0.23268118500709534, -0.320607990026474, -0.0635177418589592, -0.06433478742837906, -0.015135949477553368, -0.13927613198757172, 0.018019521608948708, 0.10932328552007675, -0.19695666432380676, 0.004285890609025955, 0.06306490302085876, 0.05544339492917061, -0.03674605116248131, -0.20572978258132935, 0.20981091260910034, 0.0071816034615039825, -0.24177101254463196, -0.028906401246786118, 0.09331867843866348, 0.29296544194221497, 0.21729028224945068, -0.017189648002386093, 0.11124007403850555, -0.0544382706284523, 0.03655306622385979, -0.25462275743484497, 0.04961680993437767, 0.2129996418952942, 0.04493676871061325, 0.07651542872190475, 0.04555081948637962, -0.11880011111497879, -0.03169477358460426, 0.17005720734596252, -0.18072761595249176, 0.08804536610841751, 0.15383599698543549, -0.06883589923381805, -0.08853815495967865, -0.19247302412986755, 0.26905274391174316, 0.1888093203306198, -0.17963823676109314, -0.17243841290473938, 0.12784215807914734, -0.18660622835159302, -0.12157630920410156, 0.051542166620492935, -0.18182675540447235, -0.16536642611026764, -0.2853504717350006, -0.012397468090057373, 0.45266157388687134, 0.17653466761112213, -0.2122398316860199, 0.012532774358987808, -0.09284307807683945, 0.10068180412054062, -0.031197573989629745, 0.17022091150283813, 0.0000429004430770874, 0.019613215699791908, -0.07911663502454758, 0.052209511399269104, 0.2233724296092987, -0.03784641623497009, -0.07938466966152191, 0.236005499958992, -0.04834647849202156, -0.09712183475494385, -0.06872403621673584, 0.05285634100437164, -0.14739540219306946, 0.010996431112289429, -0.09157370030879974, -0.0498482920229435, -0.0364869050681591, 0.10078536719083786, 0.027532480657100677, 0.1480652093887329, -0.17821556329727173, 0.16438715159893036, -0.04734153300523758, 0.006744745187461376, -0.0089455246925354, 0.003287798725068569, -0.05073399096727371, -0.06098368018865585, 0.139216810464859, -0.35419943928718567, 0.06192156672477722, 0.15003375709056854, 0.0546683706343174, 0.03925054520368576, 0.01514084730297327, 0.10351229459047318, 0.12202562391757965, 0.034057874232530594, -0.1624462753534317, -0.06088358908891678, 0.025108247995376587, -0.026649266481399536, -0.010498914867639542, 0.031228138133883476],
[-0.08668005466461182, -0.03483007103204727, -0.0003673918545246124, 0.008645879104733467, -0.09362030774354935, -0.025139985606074333, -0.007545042783021927, -0.028350263833999634, 0.15249204635620117, 0.014242732897400856, 0.21090523898601532, 0.008390436880290508, -0.23901423811912537, 0.0009689182043075562, -0.060625866055488586, 0.07270534336566925, -0.10198824852705002, -0.08482334762811661, -0.07779358327388763, -0.10252971947193146, 0.05460023507475853, 0.11731529235839844, 0.02852252684533596, 0.09077048301696777, -0.12390488386154175, -0.2868882119655609, -0.1304321587085724, -0.16742777824401855, 0.014583868905901909, -0.1479465663433075, -0.014099230989813805, 0.15778455138206482, -0.13920526206493378, -0.08188515901565552, 0.06384599953889847, 0.031834639608860016, -0.030622778460383415, -0.025880059227347374, 0.18735939264297485, 0.14199277758598328, -0.10691522061824799, 0.004796179011464119, -0.011263035237789154, 0.42503073811531067, 0.1619957834482193, 0.029321935027837753, 0.035238925367593765, -0.049069225788116455, 0.11016182601451874, -0.30699604749679565, 0.13611817359924316, 0.15381795167922974, 0.15551342070102692, 0.026638466864824295, 0.10271516442298889, -0.15697214007377625, 0.011516798287630081, 0.13340604305267334, -0.18056459724903107, 0.2431061863899231, 0.09686680138111115, -0.09295038878917694, -0.04036598652601242, -0.11260301619768143, 0.15670207142829895, 0.07631193846464157, -0.11658167839050293, -0.07479780167341232, 0.1658235639333725, -0.10525454580783844, -0.07321809232234955, 0.11354232579469681, -0.18063338100910187, -0.24993789196014404, -0.26583242416381836, 0.07639464735984802, 0.5029336214065552, 0.1548779010772705, -0.21183547377586365, 0.003467526286840439, -0.06831696629524231, 0.0013287980109453201, 0.05430014058947563, 0.06840413808822632, -0.08649691939353943, -0.01732945814728737, -0.12664976716041565, 0.07448068261146545, 0.19064179062843323, 0.01050578523427248, -0.07535020262002945, 0.23970095813274384, -0.01862136647105217, 0.018674911931157112, 0.084925077855587, 0.06750399619340897, -0.05313561484217644, -0.061520203948020935, -0.14313256740570068, -0.014466727152466774, -0.011937311850488186, -0.1633354276418686, -0.03539048507809639, 0.0666762962937355, -0.14286108314990997, 0.17977604269981384, 0.0038571711629629135, -0.03993326798081398, -0.07229620218276978, -0.04254306107759476, -0.1374536007642746, 0.06556740403175354, 0.21137236058712006, -0.2630103528499603, 0.17165997624397278, 0.1454516053199768, 0.1140887439250946, 0.1566239446401596, -0.025141002610325813, 0.058362193405628204, -0.0580839179456234, -0.07689760625362396, -0.18478064239025116, -0.06852684915065765, 0.053309667855501175, -0.0068711647763848305, -0.009051743894815445, 0.03138190135359764],
[-0.10812772810459137, 0.0960509404540062, 0.05075833201408386, -0.006992556154727936, -0.02430100180208683, 0.06688249111175537, -0.0361909419298172, -0.05283684283494949, 0.20916198194026947, -0.12132113426923752, 0.2426092028617859, 0.002393195405602455, -0.2717067003250122, -0.009222805500030518, -0.09324157238006592, 0.0888652354478836, -0.09238552302122116, -0.06716188043355942, -0.07962961494922638, -0.015433302149176598, 0.10823999345302582, 0.07980607450008392, 0.05156152695417404, 0.004107831045985222, -0.09902802109718323, -0.4029299020767212, -0.14082002639770508, -0.07390984892845154, -0.018068937584757805, -0.03710474818944931, 0.018614336848258972, -0.016432739794254303, -0.19106215238571167, -0.0028982944786548615, 0.015964776277542114, 0.06579974293708801, -0.07434473931789398, 0.018355894833803177, 0.14858579635620117, 0.012171495705842972, -0.1377386599779129, 0.026052771136164665, 0.03385549783706665, 0.2836731970310211, 0.10040508210659027, 0.04004503786563873, -0.001651376485824585, -0.10324104875326157, 0.03719945624470711, -0.2629179060459137, 0.16452158987522125, 0.14986106753349304, 0.13023321330547333, 0.09661436080932617, 0.18918710947036743, -0.10880888998508453, -0.031791239976882935, 0.15386438369750977, -0.14458853006362915, 0.11291952431201935, 0.06003924086689949, 0.06476171314716339, -0.009830544702708721, -0.10745155066251755, 0.1405407190322876, 0.1305859088897705, -0.16277149319648743, -0.18458272516727448, 0.05099692940711975, -0.19503474235534668, 0.010700453072786331, 0.11001631617546082, -0.08623851835727692, -0.2591833472251892, -0.26403212547302246, 0.12243489921092987, 0.4310947358608246, 0.20690849423408508, -0.15607987344264984, -0.026859350502490997, -0.052227526903152466, -0.0684361532330513, 0.06496615707874298, -0.0058087073266506195, -0.08738603442907333, 0.026622235774993896, -0.08068346232175827, 0.05664081126451492, 0.16467420756816864, -0.0009840866550803185, -0.07553625106811523, 0.25617775321006775, -0.05051515996456146, 0.009972795844078064, 0.007534991949796677, 0.03971076011657715, -0.11119121313095093, -0.011049863882362843, -0.1832604557275772, 0.021426139399409294, -0.076441191136837, -0.09806857258081436, -0.05703507363796234, 0.019011031836271286, -0.12956354022026062, 0.13923075795173645, -0.021157534793019295, -0.06298907101154327, -0.03315307945013046, 0.06146591901779175, -0.2764729857444763, -0.0066480981186032295, 0.1774473339319229, -0.2960206866264343, 0.2048717439174652, 0.0974058285355568, 0.08322597295045853, 0.16235607862472534, 0.11903765797615051, 0.07892667502164841, 0.012258721515536308, -0.022171374410390854, -0.1235252171754837, -0.08490340411663055, 0.04904255270957947, -0.021960634738206863, 0.03359823301434517, -0.02790735848248005],
[-0.0861644521355629, 0.0407138466835022, 0.016660915687680244, -0.11184008419513702, -0.17109225690364838, -0.008054183796048164, 0.0043458398431539536, -0.04717544466257095, 0.2360997051000595, -0.1792946606874466, 0.09372667968273163, -0.10778637230396271, -0.18022964894771576, 0.1057591438293457, -0.14819777011871338, 0.18456174433231354, -0.10886520892381668, -0.22280070185661316, -0.18811620771884918, -0.17934660613536835, -0.005707431584596634, 0.17547455430030823, -0.07589425891637802, 0.08822266757488251, -0.22329366207122803, -0.2647639811038971, 0.0006838683038949966, -0.12440066039562225, 0.014281416311860085, -0.1364075094461441, 0.009437749162316322, 0.17108798027038574, -0.0995604544878006, 0.059015583246946335, 0.08069150149822235, 0.06387726962566376, -0.03294640779495239, -0.10197870433330536, 0.1799873262643814, 0.08741294592618942, -0.2174263745546341, -0.04494243860244751, 0.06684452295303345, 0.34167376160621643, 0.23325026035308838, -0.04154399782419205, 0.011788692325353622, -0.11752628535032272, 0.20273643732070923, -0.29470184445381165, 0.028117917478084564, 0.12952283024787903, 0.011183657683432102, 0.1467585265636444, 0.09572306275367737, -0.19623562693595886, -0.006620150525122881, 0.1656893491744995, -0.15442757308483124, 0.101564422249794, 0.07097303867340088, -0.13614526391029358, -0.09262663125991821, -0.12386314570903778, 0.2525855004787445, 0.08339422941207886, -0.16481442749500275, -0.1946205347776413, 0.22902803122997284, -0.190300852060318, -0.07371577620506287, 0.06296952813863754, -0.11559777706861496, -0.11654040217399597, -0.19363869726657867, 0.05155988410115242, 0.34051868319511414, 0.14739015698432922, -0.10211460292339325, 0.07169584929943085, -0.01736035943031311, 0.032930076122283936, -0.012714214622974396, 0.15314114093780518, -0.06861979514360428, -0.07193847000598907, -0.004278216511011124, 0.04735196381807327, 0.22340130805969238, -0.034403908997774124, -0.014668408781290054, 0.22658497095108032, -0.06898515671491623, -0.06174619868397713, -0.041788697242736816, 0.04460466653108597, -0.24244919419288635, 0.057573556900024414, -0.008640434592962265, 0.012258822098374367, 0.08495114743709564, -0.02127320133149624, 0.03654727339744568, 0.14537471532821655, -0.11938325315713882, 0.2437717467546463, -0.13897383213043213, 0.04482175409793854, -0.0048943255096673965, -0.04026113823056221, -0.02975938469171524, -0.0013266131281852722, 0.1767640858888626, -0.20145171880722046, 0.18534420430660248, 0.0919744148850441, 0.11030878126621246, 0.1636233627796173, 0.05860510841012001, 0.04500507190823555, 0.07213535904884338, 0.0592627227306366, -0.23625873029232025, -0.031807538121938705, 0.08182771503925323, -0.08432832360267639, -0.06450914591550827, 0.0522795170545578],
[-0.1205408126115799, 0.05365414917469025, 0.11673855781555176, 0.0674002468585968, -0.2250651717185974, 0.009286951273679733, -0.03329429030418396, -0.0772957131266594, 0.13294319808483124, -0.07603689283132553, 0.13200519979000092, -0.07264070957899094, -0.2603042423725128, 0.13752751052379608, -0.03672562539577484, 0.19355109333992004, -0.06353013217449188, -0.09449752420186996, -0.13549655675888062, -0.07581540942192078, 0.022267043590545654, 0.03609134256839752, 0.046052027493715286, 0.12285836040973663, -0.17200231552124023, -0.32074880599975586, -0.031141264364123344, -0.05714007467031479, 0.036559414118528366, -0.005135348066687584, -0.07657670974731445, 0.09152858704328537, -0.1677236557006836, -0.02720966935157776, 0.039436280727386475, 0.023101013153791428, -0.006022093817591667, -0.020834414288401604, 0.21509629487991333, 0.007696652784943581, -0.29771655797958374, 0.012345774099230766, 0.09222178161144257, 0.19552744925022125, 0.17507854104042053, 0.08492477238178253, 0.10386379063129425, -0.08650008589029312, 0.09163841605186462, -0.35064804553985596, 0.011814825236797333, 0.15323927998542786, 0.038098711520433426, 0.012076972052454948, 0.05250523239374161, -0.22108733654022217, -0.05843188613653183, 0.11331582814455032, -0.15032993257045746, 0.12622590363025665, 0.10020335763692856, 0.03399747610092163, -0.05323035269975662, -0.09618151187896729, 0.19968774914741516, 0.13584056496620178, -0.12660281360149384, -0.14359796047210693, 0.15991385281085968, -0.21421459317207336, -0.006963425315916538, 0.16878095269203186, -0.11460451036691666, -0.23956090211868286, -0.27385738492012024, 0.019329074770212173, 0.5151724219322205, 0.15237875282764435, -0.17116820812225342, -0.054046861827373505, -0.03608296066522598, 0.01658475771546364, 0.05548166111111641, 0.060410335659980774, -0.05113377422094345, -0.04840084910392761, -0.0635150596499443, 0.0436171293258667, 0.24931558966636658, -0.035200394690036774, 0.03071688301861286, 0.26128140091896057, 0.11349756270647049, -0.017748894169926643, -0.03869530186057091, 0.18593837320804596, -0.14041811227798462, -0.04605704918503761, -0.16739949584007263, -0.0076591856777668, 0.01983969286084175, 0.0296650193631649, 0.06249948590993881, 0.07530646026134491, -0.19937050342559814, 0.1863221675157547, -0.1099039688706398, -0.06129539757966995, -0.05316010117530823, 0.06181564927101135, -0.13108719885349274, 0.006346769630908966, 0.18396040797233582, -0.19444288313388824, 0.09514211863279343, 0.1821029782295227, -0.029052872210741043, 0.08303431421518326, 0.044375088065862656, 0.07726173102855682, 0.03303125873208046, 0.026226479560136795, -0.12336263060569763, -0.12966996431350708, 0.08518747240304947, -0.17891094088554382, -0.004896830767393112, 0.01702316664159298]]
\ No newline at end of file
[[{"x": 54.4841331243515, "y": 279.8494456708431}, {"x": 52.762970216572285, "y": 295.0540763139725}, {"x": 51.44473014399409, "y": 308.63857716321945}, {"x": 52.77811277657747, "y": 322.50638687610626}, {"x": 56.076875776052475, "y": 335.40019261837006}, {"x": 62.24221035838127, "y": 348.28095012903214}, {"x": 71.04102492332458, "y": 360.55759835243225}, {"x": 81.31345927715302, "y": 370.8286249637604}, {"x": 93.04248213768005, "y": 375.1568164229393}, {"x": 104.32137668132782, "y": 374.03898203372955}, {"x": 114.66587007045746, "y": 366.7231530547142}, {"x": 124.12134230136871, "y": 359.58974558115005}, {"x": 134.0518993139267, "y": 351.187184214592}, {"x": 141.96835577487946, "y": 340.5877373814583}, {"x": 148.3436268568039, "y": 330.19438648223877}, {"x": 152.50264823436737, "y": 318.137512922287}, {"x": 156.57829463481903, "y": 306.21622332930565}, {"x": 65.60247659683228, "y": 287.6319411545992}, {"x": 73.90902906656265, "y": 286.56817772984505}, {"x": 83.04267644882202, "y": 288.6181264221668}, {"x": 92.16026186943054, "y": 292.26203539967537}, {"x": 100.0861269235611, "y": 297.7609884738922}, {"x": 119.06923472881317, "y": 301.443696141243}, {"x": 128.6662083864212, "y": 299.58680284023285}, {"x": 137.31286346912384, "y": 299.6509060263634}, {"x": 145.62582194805145, "y": 300.87507927417755}, {"x": 151.1600774526596, "y": 304.18568167090416}, {"x": 109.00326192378998, "y": 309.371550232172}, {"x": 106.5176647901535, "y": 319.28606486320496}, {"x": 105.35275757312775, "y": 328.463223695755}, {"x": 103.40745270252228, "y": 338.57382702827454}, {"x": 89.8807567358017, "y": 336.06577545404434}, {"x": 95.45948922634125, "y": 339.7772246003151}, {"x": 100.78338205814362, "y": 343.8431135416031}, {"x": 107.51278221607208, "y": 342.2273164987564}, {"x": 112.08169281482697, "y": 340.5178443789482}, {"x": 74.90537881851196, "y": 300.0762922167778}, {"x": 80.60373783111572, "y": 300.8516201376915}, {"x": 86.9048935174942, "y": 302.5063696503639}, {"x": 92.43779003620148, "y": 305.77344846725464}, {"x": 85.71294963359833, "y": 305.35038819909096}, {"x": 78.96711945533752, "y": 304.2834280729294}, {"x": 120.14831721782684, "y": 311.1841279864311}, {"x": 127.07818567752838, "y": 311.38264256715775}, {"x": 132.99132406711578, "y": 312.0929676890373}, {"x": 138.38761270046234, "y": 313.43911921977997}, {"x": 132.19313442707062, "y": 315.49242800474167}, {"x": 125.66980540752411, "y": 314.0371503531933}, {"x": 75.24729639291763, "y": 340.96445989608765}, {"x": 85.09969830513, "y": 344.38711792230606}, {"x": 93.9101380109787, "y": 347.62620878219604}, {"x": 97.92843282222748, "y": 349.9168846607208}, {"x": 104.11339223384857, "y": 348.53212666511536}, {"x": 111.40903055667877, "y": 348.52134668827057}, {"x": 118.41414988040924, "y": 347.4458556175232}, {"x": 110.3457373380661, "y": 356.35120064020157}, {"x": 101.84181749820709, "y": 359.49979197978973}, {"x": 95.72099566459656, "y": 359.8314492702484}, {"x": 89.90900933742523, "y": 357.86295783519745}, {"x": 82.39305138587952, "y": 352.26175260543823}, {"x": 78.12815725803375, "y": 342.695430457592}, {"x": 92.45323956012726, "y": 349.17965573072433}, {"x": 98.11183631420135, "y": 351.18774551153183}, {"x": 103.44208896160126, "y": 351.51249808073044}, {"x": 115.9059625864029, "y": 348.7764061689377}, {"x": 102.82863199710846, "y": 353.25923877954483}, {"x": 96.64927542209625, "y": 353.5822151899338}, {"x": 92.54582345485687, "y": 351.4128255844116}],
[{"x": 252.2517795562744, "y": 275.9289174377918}, {"x": 254.25690640509129, "y": 287.53185617923737}, {"x": 256.414415076375, "y": 297.4512948989868}, {"x": 259.92505625262856, "y": 307.5814243555069}, {"x": 264.258636072278, "y": 316.37451285123825}, {"x": 269.8492708206177, "y": 324.35249626636505}, {"x": 278.0556249022484, "y": 331.55529230833054}, {"x": 287.1583237051964, "y": 336.27545660734177}, {"x": 296.8990079164505, "y": 337.73418939113617}, {"x": 307.03963631391525, "y": 337.4403018951416}, {"x": 316.4400250315666, "y": 332.53693038225174}, {"x": 324.1076574921608, "y": 327.09237545728683}, {"x": 331.0278232693672, "y": 319.25563126802444}, {"x": 336.4769398570061, "y": 309.700075507164}, {"x": 340.1471373438835, "y": 300.1988312602043}, {"x": 342.4503394961357, "y": 290.12727427482605}, {"x": 345.31944888830185, "y": 280.28039038181305}, {"x": 256.71826735138893, "y": 265.7339949309826}, {"x": 262.03788154013455, "y": 259.11585661768913}, {"x": 270.9166807234287, "y": 257.50668972730637}, {"x": 280.2627809047699, "y": 258.66556200385094}, {"x": 289.1088626384735, "y": 261.9270911514759}, {"x": 301.11069256067276, "y": 262.1025609970093}, {"x": 311.6360414624214, "y": 259.07854360342026}, {"x": 321.5185950398445, "y": 259.56887099146843}, {"x": 330.6103066802025, "y": 262.17411509156227}, {"x": 335.8322302699089, "y": 269.03523257374763}, {"x": 295.83379566669464, "y": 271.5867927968502}, {"x": 295.19629764556885, "y": 281.26413801312447}, {"x": 294.8906948566437, "y": 290.08939987421036}, {"x": 294.68082946538925, "y": 299.63570845127106}, {"x": 283.9866814017296, "y": 300.46018624305725}, {"x": 289.1035366654396, "y": 303.70147758722305}, {"x": 294.6041773557663, "y": 306.9468694925308}, {"x": 300.2074119448662, "y": 304.7393867969513}, {"x": 305.01778668165207, "y": 303.0569154024124}, {"x": 267.1455246210098, "y": 273.36186504364014}, {"x": 271.29351702332497, "y": 269.5836756527424}, {"x": 276.98266848921776, "y": 269.2756244838238}, {"x": 282.18263429403305, "y": 273.6405881345272}, {"x": 276.73509895801544, "y": 274.3932607769966}, {"x": 271.15120989084244, "y": 274.9724545776844}, {"x": 306.2907273173332, "y": 273.96472772955894}, {"x": 312.3908343911171, "y": 270.7982785999775}, {"x": 317.99342280626297, "y": 271.1382451057434}, {"x": 322.57845264673233, "y": 275.3475582897663}, {"x": 318.4561406970024, "y": 277.04529172182083}, {"x": 311.7086290717125, "y": 276.4759971499443}, {"x": 278.4617110490799, "y": 313.9066096544266}, {"x": 284.0348202586174, "y": 313.8949286341667}, {"x": 289.79565292596817, "y": 314.03017741441727}, {"x": 294.1672314405441, "y": 315.7231233716011}, {"x": 299.386224091053, "y": 313.8113762140274}, {"x": 305.8218873143196, "y": 314.7099709510803}, {"x": 312.32251197099686, "y": 316.4275133013725}, {"x": 306.2943838238716, "y": 320.4392428994179}, {"x": 299.8657733798027, "y": 320.83152294158936}, {"x": 294.2846277952194, "y": 321.36408680677414}, {"x": 289.42114770412445, "y": 320.2233706712723}, {"x": 283.6190547347069, "y": 318.2715523838997}, {"x": 280.26574271917343, "y": 314.58500146865845}, {"x": 289.5005741715431, "y": 316.2997612953186}, {"x": 294.63025587797165, "y": 317.5675250887871}, {"x": 299.24741357564926, "y": 317.34421187639236}, {"x": 310.01027661561966, "y": 316.67098063230515}, {"x": 299.4706104397774, "y": 315.8766991496086}, {"x": 293.81820046901703, "y": 316.3997856974602}, {"x": 289.89974427223206, "y": 315.2797955274582}],
[{"x": 459.09935719519854, "y": 218.13032889366147}, {"x": 460.54768696054816, "y": 230.47838592529294}, {"x": 462.1836343817413, "y": 241.2056384682655}, {"x": 465.3896450996399, "y": 252.3832450509071}, {"x": 469.65624682605267, "y": 262.5285752415657}, {"x": 475.84583942592144, "y": 271.76317942142487}, {"x": 485.1275852024555, "y": 279.5509850382805}, {"x": 495.1621728539467, "y": 284.797351539135}, {"x": 505.5629862546921, "y": 286.8822603225708}, {"x": 517.4467390179634, "y": 285.96916061639786}, {"x": 529.4473886489868, "y": 281.45866310596466}, {"x": 538.869506418705, "y": 275.2687605023384}, {"x": 546.4981092214584, "y": 266.4664577841759}, {"x": 551.2324748039246, "y": 255.01710993051526}, {"x": 554.7127612829208, "y": 243.48108315467832}, {"x": 555.3402676582336, "y": 230.76776593923566}, {"x": 556.7988897562027, "y": 218.4927234649658}, {"x": 465.4299607281573, "y": 212.9801985621452}, {"x": 471.11858792975545, "y": 209.44992011785504}, {"x": 479.1034842133522, "y": 208.53261959552762}, {"x": 487.1113453656435, "y": 209.60203573107717}, {"x": 494.11751186847687, "y": 212.7387219071388}, {"x": 509.9350489974022, "y": 213.1643395423889}, {"x": 519.1484966278076, "y": 209.53720027208325}, {"x": 527.979111790657, "y": 209.10110011696813}, {"x": 536.5856410264969, "y": 209.8031400442123}, {"x": 543.1041161417961, "y": 212.92910945415494}, {"x": 503.02139446139336, "y": 221.04303660988805}, {"x": 501.8637197315693, "y": 229.62805640697476}, {"x": 501.6442012488842, "y": 237.6451110243797}, {"x": 500.7066152691841, "y": 246.5652552247047}, {"x": 493.0849750339985, "y": 248.23481416702268}, {"x": 497.4453990161419, "y": 250.85656070709226}, {"x": 501.78884494304657, "y": 253.37974423170087}, {"x": 506.8887307345867, "y": 251.58306765556333}, {"x": 511.9318815469742, "y": 249.9717735648155}, {"x": 474.91831866651773, "y": 223.4097787737846}, {"x": 478.91311833262444, "y": 221.78881421685216}, {"x": 485.4242661744356, "y": 221.74642735719678}, {"x": 490.9208631515503, "y": 224.01877999305722}, {"x": 485.0950839370489, "y": 225.26164376735684}, {"x": 479.2658910751343, "y": 225.63468092679975}, {"x": 515.9201887845993, "y": 223.75643199682233}, {"x": 521.5161144137383, "y": 222.7518747448921}, {"x": 527.4093854427338, "y": 222.81509780883786}, {"x": 533.0874189734459, "y": 224.11863917112348}, {"x": 528.3067688941956, "y": 226.18071675300595}, {"x": 521.1155691742897, "y": 226.13475763797757}, {"x": 488.6783328652382, "y": 261.41800594329834}, {"x": 493.67272448539734, "y": 260.5540979504585}, {"x": 499.263761729002, "y": 259.745807826519}, {"x": 503.3115651011467, "y": 261.4932536482811}, {"x": 507.9067933857441, "y": 259.28411626815796}, {"x": 515.3690929412842, "y": 260.15914314985275}, {"x": 522.2341315150261, "y": 262.72098511457443}, {"x": 515.8608915805817, "y": 267.2595453262329}, {"x": 509.24180537462234, "y": 268.3049160838127}, {"x": 503.9985676109791, "y": 268.8232405781746}, {"x": 499.3739930987358, "y": 267.47048407793045}, {"x": 493.8684720993042, "y": 265.5463541150093}, {"x": 491.0431921482086, "y": 261.91566210985184}, {"x": 499.28222247958183, "y": 262.4607531428337}, {"x": 504.12661519646645, "y": 263.96868216991425}, {"x": 508.22424668073654, "y": 263.86631655693054}, {"x": 519.7851538062096, "y": 262.86537450551987}, {"x": 508.41710847616196, "y": 263.2046567797661}, {"x": 503.3691011071205, "y": 263.3891100883484}, {"x": 500.2570376396179, "y": 262.4323624968529}],
[{"x": 234.3194352723658, "y": 74.57558505237103}, {"x": 234.25971424020827, "y": 87.65778727829456}, {"x": 234.4752733167261, "y": 99.06290532648563}, {"x": 237.1816885098815, "y": 110.96489562094212}, {"x": 241.94283635169268, "y": 121.28872080147266}, {"x": 248.6071757376194, "y": 131.3841907531023}, {"x": 256.9602437019348, "y": 141.17377139627934}, {"x": 266.1121906042099, "y": 148.39108677208424}, {"x": 276.5760519504547, "y": 150.81767751276493}, {"x": 286.06898361444473, "y": 148.72532434761524}, {"x": 294.6526671051979, "y": 141.04826493561268}, {"x": 301.33340084552765, "y": 133.15681870281696}, {"x": 308.6333589553833, "y": 123.89443911612034}, {"x": 314.2338796854019, "y": 113.46245117485523}, {"x": 317.77701437473297, "y": 103.2364257723093}, {"x": 319.5086579322815, "y": 92.14938713610172}, {"x": 320.94791293144226, "y": 81.37062980234623}, {"x": 243.06732543557882, "y": 74.08600734174252}, {"x": 248.8067600429058, "y": 70.98361553251743}, {"x": 256.3999947309494, "y": 70.81113044917583}, {"x": 264.76877000927925, "y": 72.02850545942783}, {"x": 272.0709395110607, "y": 75.7467415779829}, {"x": 287.7707560658455, "y": 76.91189719736576}, {"x": 296.2243087887764, "y": 73.96356584131718}, {"x": 304.44421088695526, "y": 73.51134298741817}, {"x": 311.53458148241043, "y": 75.23528026044369}, {"x": 316.1166696548462, "y": 78.9747758358717}, {"x": 280.17183458805084, "y": 84.96656738221645}, {"x": 279.3937808275223, "y": 94.36238606274128}, {"x": 279.35660767555237, "y": 103.06436260044575}, {"x": 278.7231848835945, "y": 112.29577179253101}, {"x": 266.31418666243553, "y": 112.0883796364069}, {"x": 272.27140441536903, "y": 115.1364913135767}, {"x": 277.6347743868828, "y": 117.94008959829807}, {"x": 283.3616907596588, "y": 115.64554668962955}, {"x": 287.8933918476105, "y": 113.81301428377628}, {"x": 251.0667580664158, "y": 82.9913242906332}, {"x": 255.89165657758713, "y": 81.49192760884762}, {"x": 261.7473086118698, "y": 81.9099268168211}, {"x": 267.2386226952076, "y": 85.10501013696194}, {"x": 261.30779322981834, "y": 85.19847224652767}, {"x": 255.44378045201302, "y": 85.36310140788555}, {"x": 289.71536606550217, "y": 86.42285586893559}, {"x": 296.3602277636528, "y": 84.84875993430614}, {"x": 301.8530383706093, "y": 84.7902014106512}, {"x": 306.5372512936592, "y": 86.6484913378954}, {"x": 301.7564742565155, "y": 88.82455034554005}, {"x": 295.51999282836914, "y": 88.34888525307178}, {"x": 254.46332001686096, "y": 119.8893206268549}, {"x": 262.4870464205742, "y": 120.17709912359715}, {"x": 270.87596663832664, "y": 121.66138370335102}, {"x": 275.98605555295944, "y": 123.45295931398869}, {"x": 282.2421624660492, "y": 121.90349115431309}, {"x": 290.1554627418518, "y": 121.4969637542963}, {"x": 297.10897439718246, "y": 122.38941462337971}, {"x": 290.4657106399536, "y": 131.62555201351643}, {"x": 281.68320965766907, "y": 135.24335576593876}, {"x": 275.27662420272827, "y": 136.08381040394306}, {"x": 269.31407338380814, "y": 134.49461282789707}, {"x": 261.1233943104744, "y": 129.75777430832386}, {"x": 256.483723372221, "y": 121.03552509844303}, {"x": 269.97673404216766, "y": 123.28837896883488}, {"x": 276.3295534849167, "y": 124.6335234194994}, {"x": 282.1039479970932, "y": 124.4526105672121}, {"x": 295.37730115652084, "y": 123.08822555840015}, {"x": 281.56651544570923, "y": 130.71057595312595}, {"x": 275.4741504788399, "y": 131.3456677943468}, {"x": 270.4244840145111, "y": 129.8541026264429}],
[{"x": 576.1535308398306, "y": 97.49992448091507}, {"x": 576.7155177183449, "y": 110.38267150521278}, {"x": 577.5447585117072, "y": 121.69842475652695}, {"x": 580.0351948738098, "y": 133.4102616906166}, {"x": 584.0166524648666, "y": 143.69573944807053}, {"x": 590.4294921457767, "y": 153.09628623723984}, {"x": 599.9136656522751, "y": 161.59112352132797}, {"x": 610.0823851823807, "y": 167.7023411989212}, {"x": 620.7162899971008, "y": 169.81312930583954}, {"x": 630.8302686214447, "y": 168.10430830717087}, {"x": 639.7988377809525, "y": 161.90262937545776}, {"x": 647.1597594022751, "y": 155.3944255709648}, {"x": 653.6100611686707, "y": 147.24894577264786}, {"x": 657.8869388103485, "y": 137.40559548139572}, {"x": 660.9833337068558, "y": 128.15908282995224}, {"x": 662.643195271492, "y": 118.08342492580414}, {"x": 664.5361260175705, "y": 108.1498429775238}, {"x": 586.8617853820324, "y": 97.57175967097282}, {"x": 594.0036285221577, "y": 95.91249337792397}, {"x": 602.0432872772217, "y": 96.55522811412811}, {"x": 610.2376074194908, "y": 98.67479193210602}, {"x": 617.003839969635, "y": 102.86477941274643}, {"x": 633.1619135141373, "y": 103.65319031476974}, {"x": 640.9453811645508, "y": 100.97537392377853}, {"x": 648.2738443613052, "y": 100.39352387189865}, {"x": 655.0860079526901, "y": 101.07310575246811}, {"x": 660.0833687782288, "y": 103.48732554912567}, {"x": 625.6144524812698, "y": 109.68697956204414}, {"x": 624.7979527711868, "y": 118.26480025053024}, {"x": 624.7070682048798, "y": 126.18410778045654}, {"x": 624.2883296012878, "y": 134.75663179159164}, {"x": 613.2230737805367, "y": 135.47273236513138}, {"x": 618.1410920619965, "y": 137.91179966926575}, {"x": 622.635379076004, "y": 140.55282950401306}, {"x": 627.3352084159851, "y": 139.2602106332779}, {"x": 630.9798530340195, "y": 137.74868369102478}, {"x": 595.8744234144688, "y": 108.09254574775696}, {"x": 600.7668833136559, "y": 106.68771129846573}, {"x": 606.626432955265, "y": 107.28530788421631}, {"x": 611.143786907196, "y": 110.6366218328476}, {"x": 605.9760230183601, "y": 111.01029747724533}, {"x": 600.2359576225281, "y": 110.95684599876404}, {"x": 635.414272069931, "y": 112.45622023940086}, {"x": 641.2012264728546, "y": 110.93172106146812}, {"x": 646.3275607824326, "y": 110.93707194924355}, {"x": 650.84887611866, "y": 113.17889940738678}, {"x": 646.4744521379471, "y": 115.17933741211891}, {"x": 640.3822878599167, "y": 114.80342638492584}, {"x": 602.286383330822, "y": 142.86055451631546}, {"x": 610.3222481012344, "y": 144.2252094745636}, {"x": 617.2720557451248, "y": 145.19179409742355}, {"x": 621.1298673152924, "y": 146.59095239639282}, {"x": 626.0349799394608, "y": 145.22645050287247}, {"x": 631.8482588529587, "y": 145.41272389888763}, {"x": 637.8517721891403, "y": 145.64175975322723}, {"x": 631.8961962461472, "y": 151.41622865200043}, {"x": 625.3036365509033, "y": 153.1305536031723}, {"x": 620.4127076864243, "y": 153.72984665632248}, {"x": 615.9534670710564, "y": 152.43784004449844}, {"x": 609.1786397099495, "y": 149.49363362789154}, {"x": 604.3942674398422, "y": 143.80317175388336}, {"x": 616.5883467197418, "y": 146.54858535528183}, {"x": 621.544314622879, "y": 147.8731118440628}, {"x": 625.545786857605, "y": 148.11420154571533}, {"x": 636.0422431230545, "y": 146.50075900554657}, {"x": 625.2157969474792, "y": 148.7349683046341}, {"x": 620.6214025020599, "y": 148.93819361925125}, {"x": 617.2931456565857, "y": 147.784716963768}],
[{"x": 78.23273684829473, "y": 51.60377091169357}, {"x": 76.88033521175385, "y": 66.98003154993057}, {"x": 75.81947349011898, "y": 80.6108932197094}, {"x": 77.60156836360693, "y": 94.52300694584846}, {"x": 82.98783158883452, "y": 106.64588287472725}, {"x": 91.29216834902763, "y": 117.48094370961189}, {"x": 102.34191060066223, "y": 127.35485658049583}, {"x": 114.33153130114079, "y": 134.41015151143074}, {"x": 126.54285334050655, "y": 137.1719016134739}, {"x": 137.02404104173183, "y": 134.17398390173912}, {"x": 144.87608276307583, "y": 125.8565858900547}, {"x": 150.99320016801357, "y": 117.1223518550396}, {"x": 156.92030988633633, "y": 108.0576830804348}, {"x": 161.7889528721571, "y": 96.96261104941368}, {"x": 165.50497464835644, "y": 88.02793249487877}, {"x": 167.33607403934002, "y": 77.9478391110897}, {"x": 168.52052859961987, "y": 68.2689423263073}, {"x": 101.93711385130882, "y": 49.88659825921059}, {"x": 110.3638557344675, "y": 48.155562341213226}, {"x": 119.74964939057827, "y": 47.33247035741806}, {"x": 129.26901035010815, "y": 49.19584932923317}, {"x": 136.74703143537045, "y": 53.64960214495659}, {"x": 150.53537838160992, "y": 56.168887346982956}, {"x": 156.60274527966976, "y": 53.865505397319794}, {"x": 162.9648243635893, "y": 53.695709228515625}, {"x": 167.64181904494762, "y": 55.2644918859005}, {"x": 170.2478265017271, "y": 57.97207298874855}, {"x": 143.26418243348598, "y": 65.35158225893974}, {"x": 142.97341756522655, "y": 73.4936634004116}, {"x": 143.46804820001125, "y": 80.94419595599174}, {"x": 143.31419222056866, "y": 89.40759268403053}, {"x": 127.23943196237087, "y": 91.14958158135414}, {"x": 133.96387301385403, "y": 93.79053750634193}, {"x": 139.21318851411343, "y": 96.51258161664009}, {"x": 144.33787517249584, "y": 94.92185071110725}, {"x": 148.012380823493, "y": 93.06354263424873}, {"x": 110.23061253130436, "y": 61.62617373466492}, {"x": 116.00698433816433, "y": 61.24231967329979}, {"x": 121.71143271028996, "y": 62.20304474234581}, {"x": 126.237358674407, "y": 64.05169928073883}, {"x": 120.87435774505138, "y": 64.04682829976082}, {"x": 115.38067065179348, "y": 63.669275373220444}, {"x": 150.21302603185177, "y": 66.67289063334465}, {"x": 155.51250331103802, "y": 66.83099183440208}, {"x": 159.8145430535078, "y": 67.2261371910572}, {"x": 163.32536689937115, "y": 68.20647689700127}, {"x": 159.1250678151846, "y": 69.86667791008949}, {"x": 154.3889170140028, "y": 68.73301187157631}, {"x": 109.60967443883419, "y": 99.71434238553047}, {"x": 120.61101846396923, "y": 99.09809216856956}, {"x": 130.8056702464819, "y": 100.23262706398964}, {"x": 135.622381195426, "y": 101.86354067921638}, {"x": 141.09942518174648, "y": 100.71261283755302}, {"x": 145.90711049735546, "y": 101.11367872357368}, {"x": 148.4175955504179, "y": 103.14160612225533}, {"x": 144.75522689521313, "y": 111.54441353678703}, {"x": 138.66864643990993, "y": 115.6854547560215}, {"x": 132.96517930924892, "y": 116.56978961825371}, {"x": 126.65391467511654, "y": 115.17637380957603}, {"x": 117.94208191335201, "y": 110.3444068133831}, {"x": 112.67831824719906, "y": 100.54041001200676}, {"x": 129.04413394629955, "y": 102.71197560429573}, {"x": 135.24945132434368, "y": 104.3828412592411}, {"x": 140.17994605004787, "y": 104.53136196732521}, {"x": 147.12589286267757, "y": 103.85854378342628}, {"x": 139.35607872903347, "y": 109.56546506285667}, {"x": 133.77587251365185, "y": 110.46680799126625}, {"x": 128.98411504924297, "y": 109.03905311226845}]]
\ No newline at end of file
[[{"x":78.70259515941143,"y":52.51417603031282},{"x":77.29212034493685,"y":67.79463638797883},{"x":75.96786513924599,"y":81.19702972904328},{"x":77.55778755992651,"y":95.10474457279328},{"x":82.7516676671803,"y":107.26841797367219},{"x":90.95986064523458,"y":118.21680511966828},{"x":102.14005157351494,"y":128.21510376468783},{"x":114.62478905916214,"y":135.2918554736722},{"x":127.32324719429016,"y":138.03325714603548},{"x":138.40828508138657,"y":134.74910034671908},{"x":146.35919749736786,"y":126.07305206791047},{"x":152.45407491922379,"y":117.23459686771515},{"x":158.06465715169907,"y":108.20900024906281},{"x":162.75949746370316,"y":97.26863159671906},{"x":166.2566688656807,"y":88.79356445804719},{"x":168.04164320230484,"y":79.34612335697297},{"x":169.36942607164383,"y":70.26872314945344},{"x":102.91276805102825,"y":50.643921513711255},{"x":111.27321466803551,"y":49.04517044559602},{"x":120.5909226834774,"y":48.130798955117505},{"x":130.22007316350937,"y":49.942017216836255},{"x":137.50274896621704,"y":54.37205566898469},{"x":151.10849559307098,"y":56.971192021523755},{"x":157.206868827343,"y":54.84915985599641},{"x":163.5188040137291,"y":54.879048009072584},{"x":168.17984849214554,"y":56.56761230960969},{"x":170.71705728769302,"y":59.195000310097974},{"x":144.00014102458954,"y":65.82685913578156},{"x":143.64073395729065,"y":73.73413528934601},{"x":144.01135712862015,"y":80.87621368900422},{"x":143.81918132305145,"y":89.1312643481839},{"x":127.48411267995834,"y":91.08588470951203},{"x":134.3283948302269,"y":93.52732910648469},{"x":139.52339559793472,"y":96.25963654056672},{"x":144.6911182999611,"y":94.77147736087922},{"x":148.38068902492523,"y":93.12768616214875},{"x":110.77253341674805,"y":61.845120091592115},{"x":116.32553532719612,"y":61.369480748330396},{"x":121.9557535648346,"y":62.41514648929719},{"x":126.42488598823547,"y":64.1528707934964},{"x":121.1322371661663,"y":64.05634369388703},{"x":115.86301743984222,"y":63.73924698368196},{"x":151.31528049707413,"y":67.09088959232453},{"x":156.5901842713356,"y":67.58717407718781},{"x":160.71268141269684,"y":67.98275055423859},{"x":164.26825791597366,"y":69.03151001468781},{"x":159.96803760528564,"y":70.4249998523343},{"x":155.32326072454453,"y":69.34288086429719},{"x":109.29639756679535,"y":99.49584259525422},{"x":120.37595704197884,"y":98.69051422611359},{"x":130.81436574459076,"y":99.85762085453156},{"x":135.84903299808502,"y":101.38172974125031},{"x":141.50265127420425,"y":100.36422028080109},{"x":146.36948823928833,"y":100.81127228275422},{"x":148.87848913669586,"y":103.27265228763703},{"x":145.39085745811462,"y":111.92646851078156},{"x":139.0690517425537,"y":116.24958862796906},{"x":133.29149305820465,"y":117.21682800785187},{"x":126.7722275853157,"y":115.69017853275422},{"x":117.6860836148262,"y":110.5495001269925},{"x":112.19027310609818,"y":100.30053009525422},{"x":128.99872034788132,"y":102.20095123783234},{"x":135.47163724899292,"y":103.91561188236359},{"x":140.5216109752655,"y":104.06739868656281},{"x":147.77944058179855,"y":103.93721832767609},{"x":139.77415084838867,"y":110.27076782718781},{"x":134.04826045036316,"y":111.13681854740265},{"x":129.13511961698532,"y":109.6996466113675}],[{"x":234.0432442203164,"y":72.18237426973158},{"x":234.07302695512772,"y":85.62781944967085},{"x":234.37448344379663,"y":97.4441898176747},{"x":237.17955499887466,"y":109.70111182428175},{"x":241.91123768687248,"y":120.36310103631789},{"x":248.58186745643616,"y":130.7120940992909},{"x":256.8855963945389,"y":140.60736063218886},{"x":265.9586812257767,"y":147.82223680711562},{"x":276.25926327705383,"y":150.3243668387013},{"x":285.8509864807129,"y":148.31700566507155},{"x":294.7637438774109,"y":140.88661578393751},{"x":301.7397997379303,"y":133.12361434198195},{"x":309.3003182411194,"y":123.77570560670668},{"x":315.0203056335449,"y":113.1747544119435},{"x":318.7541444301605,"y":102.62603953576857},{"x":320.53608107566833,"y":91.02094796396071},{"x":321.9833137989044,"y":79.67927816606337},{"x":243.00755888223648,"y":73.02197197175795},{"x":249.12357711791992,"y":70.65327099061781},{"x":256.6056842803955,"y":71.09805408216292},{"x":264.78340232372284,"y":72.58571747041518},{"x":271.8189814090729,"y":76.387691766985},{"x":287.77085518836975,"y":77.74872616029555},{"x":296.0825273990631,"y":74.829839498766},{"x":304.0993046760559,"y":74.12636736131483},{"x":311.1268253326416,"y":75.38659361100966},{"x":315.988929271698,"y":78.4906996796208},{"x":280.03022742271423,"y":84.97809437013441},{"x":279.16452836990356,"y":94.6116987774449},{"x":279.08406925201416,"y":103.45783117509657},{"x":278.3811960220337,"y":112.86081102586562},{"x":266.2560445070267,"y":112.55670645929152},{"x":271.9997515678406,"y":115.64479592538649},{"x":277.2981472015381,"y":118.46189263559157},{"x":282.88522934913635,"y":116.32307294107252},{"x":287.3393099308014,"y":114.4629829237538},{"x":250.71771156787872,"y":82.93822291589552},{"x":255.54227709770203,"y":81.77080169416243},{"x":261.47561633586884,"y":82.18437436319167},{"x":266.8718521595001,"y":85.09909179902846},{"x":261.02278220653534,"y":85.3523221323567},{"x":255.04803681373596,"y":85.51701000428969},{"x":289.57169246673584,"y":86.59227958417708},{"x":296.12601137161255,"y":85.3894476006108},{"x":301.7222509384155,"y":85.33728733754927},{"x":306.4811248779297,"y":87.00033477044875},{"x":301.593781709671,"y":89.3077004263478},{"x":295.20851707458496,"y":88.81555894113356},{"x":255.08908998966217,"y":120.65163496232802},{"x":262.8024332523346,"y":120.9952745745259},{"x":270.70326828956604,"y":122.40383675790602},{"x":275.5565803050995,"y":124.17473438478285},{"x":281.3999750614166,"y":122.6351008722859},{"x":288.9069309234619,"y":122.48449710107619},{"x":295.68931913375854,"y":123.3941626856404},{"x":289.19691920280457,"y":131.8845453569966},{"x":280.730872631073,"y":135.16087916589552},{"x":274.73176169395447,"y":135.9497754881459},{"x":269.14574098587036,"y":134.31138041711623},{"x":261.4882484674454,"y":129.7903356859761},{"x":257.0808787345886,"y":121.76479747987563},{"x":269.8720760345459,"y":124.05334284997755},{"x":275.90254950523376,"y":125.26417449212843},{"x":281.2652223110199,"y":125.28600934243971},{"x":293.8986005783081,"y":124.10085681176955},{"x":280.6464831829071,"y":130.53398588395888},{"x":274.9977023601532,"y":131.01732567048842},{"x":270.30089259147644,"y":129.57399228311354}],[{"x":53.439819291234016,"y":280.0604960024357},{"x":51.78455471992493,"y":295.82864955067635},{"x":50.52304560318589,"y":309.732116997242},{"x":51.996403969824314,"y":323.6099224090576},{"x":55.675161108374596,"y":336.0948809981346},{"x":61.96380540728569,"y":348.32347297668457},{"x":70.6162890791893,"y":359.93272376060486},{"x":80.40928453207016,"y":369.4880121946335},{"x":91.329245865345,"y":373.68778198957443},{"x":102.01146513223648,"y":372.54405105113983},{"x":111.70319825410843,"y":365.45324832201004},{"x":120.67031770944595,"y":358.51266556978226},{"x":130.24644166231155,"y":350.4673351049423},{"x":138.3169224858284,"y":340.3098750114441},{"x":144.8827138543129,"y":330.19956386089325},{"x":149.44279700517654,"y":318.41588428616524},{"x":153.5925915837288,"y":306.7800603210926},{"x":65.65944314002991,"y":287.4393824636936},{"x":74.17677998542786,"y":286.2525469958782},{"x":83.42234998941422,"y":288.1453210115433},{"x":92.45077580213547,"y":291.6930521428585},{"x":100.51797538995743,"y":297.3068634867668},{"x":118.6282268166542,"y":301.29853051900864},{"x":127.82300025224686,"y":299.62827998399734},{"x":136.26429945230484,"y":299.9250599741936},{"x":144.1239109635353,"y":301.3865695595741},{"x":148.95386010408401,"y":305.0852126777172},{"x":108.77183586359024,"y":309.15458089113235},{"x":106.31233364343643,"y":318.45475602149963},{"x":105.28086930513382,"y":327.17767184972763},{"x":103.4764102101326,"y":336.8808351159096},{"x":89.56375032663345,"y":334.2973544597626},{"x":95.11743992567062,"y":338.2826396226883},{"x":100.4269888997078,"y":342.33478063344955},{"x":107.09357768297195,"y":340.92600989341736},{"x":111.45647674798965,"y":339.16702830791473},{"x":74.23168629407883,"y":299.72840997576714},{"x":80.10014206171036,"y":300.2405780553818},{"x":86.55913084745407,"y":301.92818266153336},{"x":92.06215351819992,"y":305.3237600326538},{"x":85.29800087213516,"y":304.93414691090584},{"x":78.56964975595474,"y":303.802222430706},{"x":118.96386057138443,"y":311.23431676626205},{"x":125.75672775506973,"y":311.30804657936096},{"x":131.75824791193008,"y":312.1569736003876},{"x":136.9495204091072,"y":313.76811495423317},{"x":130.84956914186478,"y":315.8736974000931},{"x":124.32669311761856,"y":314.29629921913147},{"x":73.89956772327423,"y":338.6452066898346},{"x":83.92988950014114,"y":341.8424386382103},{"x":92.97652751207352,"y":345.22819715738297},{"x":97.08906918764114,"y":347.40902799367905},{"x":103.12756806612015,"y":346.28637260198593},{"x":109.98074918985367,"y":346.54456919431686},{"x":116.1731830239296,"y":346.1622644662857},{"x":108.54592829942703,"y":355.2732748389244},{"x":100.48217684030533,"y":358.47334402799606},{"x":94.37009304761887,"y":358.60381096601486},{"x":88.47060412168503,"y":356.5071900486946},{"x":80.98365873098373,"y":350.6593140363693},{"x":77.15098470449448,"y":340.4296311736107},{"x":91.45873099565506,"y":346.6854547262192},{"x":97.10512071847916,"y":348.816522359848},{"x":102.38213449716568,"y":349.28899598121643},{"x":113.6016657948494,"y":347.39245051145554},{"x":101.5955975651741,"y":352.2493990659714},{"x":95.40374487638474,"y":352.52306592464447},{"x":91.18230849504471,"y":350.25800210237503}],[{"x":253.64455546438694,"y":274.96428267994236},{"x":255.63555194437504,"y":286.48764054813694},{"x":257.851626470685,"y":296.23062263765644},{"x":261.372583928518,"y":306.3042299036057},{"x":265.716976583004,"y":315.1606678847344},{"x":271.16881239414215,"y":323.31880401411365},{"x":278.9179730415344,"y":330.80829064884495},{"x":287.70828092098236,"y":335.8823236111672},{"x":297.32989490032196,"y":337.5626749877007},{"x":307.2697219848633,"y":337.14021955766987},{"x":316.2518972158432,"y":331.886222172168},{"x":323.5890635251999,"y":326.10143922605823},{"x":330.2825126647949,"y":317.92637084760975},{"x":335.62895596027374,"y":308.1591898683579},{"x":339.1303542852402,"y":298.46708123722385},{"x":341.2391484975815,"y":288.10848497190784},{"x":343.8476446866989,"y":277.8678704980881},{"x":257.7795196324587,"y":265.5684605125458},{"x":262.86759052053094,"y":259.46244253554653},{"x":271.30318570137024,"y":258.1320998553307},{"x":280.2511401772499,"y":259.1452731970818},{"x":288.77513176202774,"y":262.10564820804905},{"x":300.88161540031433,"y":262.23092802205394},{"x":311.1703190803528,"y":259.23369329014133},{"x":320.83708584308624,"y":259.57886569538425},{"x":329.8163455724716,"y":262.03275864162754},{"x":335.06606125831604,"y":268.59239073434185},{"x":295.74934458732605,"y":272.0405581776173},{"x":295.10603761672974,"y":281.74871687927555},{"x":294.78596317768097,"y":290.4453236345322},{"x":294.52324163913727,"y":299.96156142988514},{"x":283.94295251369476,"y":300.7173269275696},{"x":289.2285826802254,"y":303.9793581847222},{"x":294.77881556749344,"y":307.2272955063851},{"x":300.44683516025543,"y":304.92734794655155},{"x":305.3025699853897,"y":303.13202046194385},{"x":267.63354451954365,"y":273.45072929897617},{"x":271.67723220586777,"y":269.8547673110039},{"x":277.1182727217674,"y":269.4767764691384},{"x":282.23915058374405,"y":273.35298531093906},{"x":276.9609106183052,"y":274.3409555439026},{"x":271.4951269328594,"y":275.01823853292774},{"x":306.1736521720886,"y":273.4907934550316},{"x":311.9750416278839,"y":270.6327790502579},{"x":317.42995393276215,"y":270.95207189359974},{"x":321.920796751976,"y":274.85076408424686},{"x":317.8245964050293,"y":276.6930876258881},{"x":311.29924952983856,"y":276.1408682648213},{"x":279.1467461884022,"y":314.23833452501606},{"x":284.48106986284256,"y":313.8286061767609},{"x":290.1339142918587,"y":313.8490322712929},{"x":294.4958354830742,"y":315.5466157678635},{"x":299.6633150577545,"y":313.5960527543099},{"x":306.1217950582504,"y":314.4668222550423},{"x":312.643159031868,"y":316.36552153864216},{"x":306.774823307991,"y":320.4127126220734},{"x":300.28556072711945,"y":321.05349503794025},{"x":294.83206647634506,"y":321.65074037351917},{"x":289.9620077610016,"y":320.5292926911385},{"x":284.2809522151947,"y":318.41614382305454},{"x":280.899098277092,"y":314.7755777720482},{"x":289.82488214969635,"y":316.4206280592949},{"x":295.0254775881767,"y":317.5740782503159},{"x":299.5264849662781,"y":317.32291888037037},{"x":310.37555599212646,"y":316.52424393930744},{"x":299.7602632045746,"y":316.030502903846},{"x":294.2890642285347,"y":316.5608839873345},{"x":290.4010553956032,"y":315.42048167028736}],[{"x":458.6051552891731,"y":217.72117600517888},{"x":460.09398763626814,"y":230.39350793438572},{"x":461.8694317564368,"y":241.38930038290638},{"x":465.2278592949733,"y":252.93564961033482},{"x":469.7518924251199,"y":263.28325144129417},{"x":476.1563876569271,"y":272.5680155523362},{"x":485.33472272753716,"y":280.3438511856141},{"x":495.19925662875175,"y":285.54824075775764},{"x":505.49679857492447,"y":287.5497344501557},{"x":517.0163440704346,"y":286.52682790833137},{"x":528.832253575325,"y":281.7315962679925},{"x":538.1082131266594,"y":275.27146718578956},{"x":545.7302697896957,"y":266.1297963030877},{"x":550.5768427848816,"y":254.52256647425313},{"x":554.072232067585,"y":242.63067278938908},{"x":554.7430681586266,"y":229.65774825888295},{"x":555.9324762821198,"y":217.0304150350632},{"x":464.2269563060254,"y":212.73671243267674},{"x":469.75961059704423,"y":209.3460837967934},{"x":477.53361535072327,"y":208.5074197419228},{"x":485.2873308509588,"y":209.6281631358208},{"x":492.1645346879959,"y":212.7300988801064},{"x":509.10780626535416,"y":212.85144624786992},{"x":518.4267295002937,"y":209.07269889789242},{"x":527.5292375683784,"y":208.5795753963532},{"x":536.2015252709389,"y":209.3796590931954},{"x":542.8177468776703,"y":212.6919245489182},{"x":501.580209761858,"y":221.49086973744053},{"x":500.3731763958931,"y":230.08853492813725},{"x":500.1400006711483,"y":238.13611183243412},{"x":499.29766792058945,"y":247.00497171955723},{"x":491.3476411700249,"y":249.0014759667458},{"x":496.11430510878563,"y":251.5378865964951},{"x":500.55614736676216,"y":253.75804135876317},{"x":505.92355754971504,"y":252.01613936501164},{"x":511.2390627861023,"y":250.3659447796883},{"x":473.7797954529524,"y":223.44442883806843},{"x":477.8101897537708,"y":221.86633915382046},{"x":484.2290563881397,"y":221.8739053078236},{"x":489.7755623757839,"y":224.0043203301968},{"x":483.90396946668625,"y":225.28688494043965},{"x":478.22655925154686,"y":225.68062556462903},{"x":515.0914766192436,"y":223.67829639392514},{"x":520.7296285629272,"y":222.6548081405701},{"x":526.719929933548,"y":222.64345110135693},{"x":532.6925768852234,"y":223.93850103693623},{"x":527.5918874144554,"y":225.99143520670552},{"x":520.3674954771996,"y":225.9896002777161},{"x":487.79902869462967,"y":262.3062415488305},{"x":492.6807407736778,"y":261.1754646070542},{"x":498.5045930147171,"y":260.4150388010087},{"x":502.8779262602329,"y":262.09455320912025},{"x":507.8618753552437,"y":259.85850093441627},{"x":515.5100157856941,"y":260.53184179144523},{"x":522.529854118824,"y":263.0671280391755},{"x":516.0406350493431,"y":268.1145648129525},{"x":509.29703161120415,"y":269.5725134976449},{"x":503.7219538986683,"y":270.14516750651023},{"x":498.86963579058647,"y":268.82049713211677},{"x":493.03908091783524,"y":266.78845772820137},{"x":490.25995284318924,"y":262.70426497536323},{"x":498.5697175562382,"y":263.05846873598716},{"x":503.7999505698681,"y":264.6741432316842},{"x":508.1411578655243,"y":264.4359304078164},{"x":519.9255567789078,"y":263.10483387785575},{"x":508.362724006176,"y":264.35242161827705},{"x":502.9432258605957,"y":264.6728236444535},{"x":499.6765456497669,"y":263.62174508648536}],[{"x":575.6323959156871,"y":97.81703020103517},{"x":576.2381174787879,"y":110.7464268899733},{"x":577.1207508929074,"y":122.03858585126939},{"x":579.777023255825,"y":133.77679456241668},{"x":584.0378794074059,"y":144.10788560875},{"x":590.4641489386559,"y":153.66901643045486},{"x":599.8314836025238,"y":162.39560646780075},{"x":609.7557654380798,"y":168.6680013037497},{"x":620.3408677577972,"y":170.72851402528823},{"x":630.3204867839813,"y":168.76506347425521},{"x":639.1392222642899,"y":162.16524405248703},{"x":646.3353884220123,"y":155.17852164276184},{"x":652.9094681739807,"y":146.6903299189383},{"x":657.3339067697525,"y":136.69562412269653},{"x":660.4557708501816,"y":127.19812996156755},{"x":662.051419377327,"y":116.94922510750833},{"x":663.7066377401352,"y":106.84000776179376},{"x":586.5404289513826,"y":98.32077897556367},{"x":593.4320383369923,"y":96.36920688517633},{"x":601.3935153484344,"y":96.69368816383424},{"x":609.6317137479782,"y":98.59984148510041},{"x":616.5242365002632,"y":102.5144240654284},{"x":632.4123371839523,"y":103.04483480222764},{"x":640.0241969823837,"y":100.34534380801263},{"x":647.2620248794556,"y":99.55671287544313},{"x":654.129354596138,"y":100.25115479000154},{"x":659.0634310245514,"y":102.63225520618501},{"x":624.9623003005981,"y":109.84366706975045},{"x":624.2144320011139,"y":118.4606404400641},{"x":624.1961280107498,"y":126.39726979501786},{"x":623.8329563140869,"y":134.92106182344497},{"x":612.6639273762703,"y":135.8878824210936},{"x":617.6502031683922,"y":138.31433464057983},{"x":622.2424348592758,"y":140.77128500707687},{"x":627.0541898012161,"y":139.33917314775528},{"x":630.7270992994308,"y":137.66990662582458},{"x":595.4028062522411,"y":108.46945513256135},{"x":600.3049533367157,"y":107.04665152199807},{"x":606.116753757,"y":107.49907577999177},{"x":610.7790024876595,"y":110.7087984777266},{"x":605.489963054657,"y":111.36523569591584},{"x":599.8057773709297,"y":111.40001646645608},{"x":634.6385422945023,"y":112.09705091484132},{"x":640.4470851421356,"y":110.56770468719544},{"x":645.5006678104401,"y":110.40809006460252},{"x":650.1414160728455,"y":112.40711540945115},{"x":645.6796168088913,"y":114.66480738885942},{"x":639.6736890077591,"y":114.49185062416139},{"x":602.1105405688286,"y":143.5170047379309},{"x":610.0115154385567,"y":144.67639989622177},{"x":616.9459272027016,"y":145.61259479291977},{"x":620.7917128801346,"y":146.8697983122641},{"x":625.5570113658905,"y":145.48805750377716},{"x":631.3872159719467,"y":145.44158322580398},{"x":637.3369463682175,"y":145.60786254175247},{"x":631.6403584480286,"y":151.87812722690643},{"x":624.9396952390671,"y":154.0825080371672},{"x":620.1312247514725,"y":154.83688754804672},{"x":615.6493668556213,"y":153.58939088352264},{"x":608.7029096484184,"y":150.48222155340255},{"x":604.0386838912964,"y":144.4577150440985},{"x":616.3138701319695,"y":147.00049645669998},{"x":621.2553536891937,"y":148.18779403932632},{"x":625.1526666879654,"y":148.3215981221968},{"x":635.5966547727585,"y":146.52967573173584},{"x":624.8098908662796,"y":149.64450467594207},{"x":620.2795970439911,"y":149.9581789470488},{"x":616.9711862206459,"y":148.92591042287887}]]
\ No newline at end of file
[[{"x":117.85171800851822,"y":58.91067498922348},{"x":157.70139408111572,"y":64.48519098758698},{"x":142.3133249282837,"y":88.54253697395325},{"x":110.16610914468765,"y":99.86233913898468},{"x":149.25052666664124,"y":106.37608766555786}], [{"x":260.46802616119385,"y":82.86598587036133},{"x":305.55760955810547,"y":83.54110813140869},{"x":281.4357223510742,"y":113.98349380493164},{"x":257.06039476394653,"y":125.50608730316164},{"x":306.0191822052002,"y":127.20984458923341}], [{"x":82.91613873839378,"y":292.6100924015045},{"x":133.91112035512924,"y":304.814593821764},{"x":104.43486452102661,"y":330.3951778411865},{"x":72.6984107196331,"y":342.633121073246},{"x":120.51901644468307,"y":354.2677878141403}], [{"x":278.20400857925415,"y":273.83238887786865},{"x":318.7582621574402,"y":273.39686036109924},{"x":295.54277753829956,"y":300.43398427963257},{"x":279.5109224319458,"y":311.497838973999},{"x":317.0187101364136,"y":313.05305886268616}], [{"x":489.58824399113655,"y":224.56882098317146},{"x":534.514480471611,"y":223.28146517276764},{"x":507.2082565128803,"y":250.17186474800113},{"x":493.0139665305615,"y":271.0716395378113},{"x":530.7517347931862,"y":270.4143014550209}], [{"x":606.397784024477,"y":105.43332603573799},{"x":645.2468676567078,"y":111.50095802545547},{"x":625.1735819578171,"y":133.40740483999252},{"x":598.8033188581467,"y":141.26283955574036},{"x":637.2144679427147,"y":147.32198816537857}]]
\ No newline at end of file
import { bufferToImage, extractFaceTensors, Rect } from '../../../src';
import { bufferToImage, extractFaceTensors, Rect, tf } from '../../../src';
describe('extractFaceTensors', () => {
let imgEl: HTMLImageElement
let imgTensor: tf.Tensor3D
beforeAll(async () => {
const img = await (await fetch('base/test/images/face1.png')).blob()
imgEl = await bufferToImage(img)
imgTensor = tf.fromPixels(await bufferToImage(img))
})
describe('extracts tensors', () => {
it('single box', async () => {
const rect = new Rect(0, 0, 50, 60)
const tensors = await extractFaceTensors(imgEl, [rect])
const tensors = await extractFaceTensors(imgTensor, [rect])
expect(tensors.length).toEqual(1)
expect(tensors[0].shape).toEqual([1, 60, 50, 3])
expect(tensors[0].shape).toEqual([60, 50, 3])
tensors[0].dispose()
})
......@@ -25,11 +25,11 @@ describe('extractFaceTensors', () => {
new Rect(0, 0, 50, 60),
new Rect(50, 50, 70, 80),
]
const tensors = await extractFaceTensors(imgEl, rects)
const tensors = await extractFaceTensors(imgTensor, rects)
expect(tensors.length).toEqual(2)
expect(tensors[0].shape).toEqual([1, 60, 50, 3])
expect(tensors[1].shape).toEqual([1, 80, 70, 3])
expect(tensors[0].shape).toEqual([60, 50, 3])
expect(tensors[1].shape).toEqual([80, 70, 3])
tensors[0].dispose()
tensors[1].dispose()
})
......@@ -40,25 +40,25 @@ describe('extractFaceTensors', () => {
it('clips upper left corner', async () => {
const rect = new Rect(-10, -10, 110, 110)
const tensors = await extractFaceTensors(imgEl, [rect])
const tensors = await extractFaceTensors(imgTensor, [rect])
expect(tensors[0].shape).toEqual([1, 100, 100, 3])
expect(tensors[0].shape).toEqual([100, 100, 3])
tensors[0].dispose()
})
it('clips bottom right corner', async () => {
const rect = new Rect(imgEl.width - 100, imgEl.height - 100, 110, 110)
const tensors = await extractFaceTensors(imgEl, [rect])
const rect = new Rect(imgTensor.shape[1] - 100, imgTensor.shape[0] - 100, 110, 110)
const tensors = await extractFaceTensors(imgTensor, [rect])
expect(tensors[0].shape).toEqual([1, 100, 100, 3])
expect(tensors[0].shape).toEqual([100, 100, 3])
tensors[0].dispose()
})
it('clips upper left and bottom right corners', async () => {
const rect = new Rect(-10, -10, imgEl.width + 20, imgEl.height + 20)
const tensors = await extractFaceTensors(imgEl, [rect])
const rect = new Rect(-10, -10, imgTensor.shape[1] + 20, imgTensor.shape[0] + 20)
const tensors = await extractFaceTensors(imgTensor, [rect])
expect(tensors[0].shape).toEqual([1, imgEl.height, imgEl.width, 3])
expect(tensors[0].shape).toEqual([imgTensor.shape[1], imgTensor.shape[0], 3])
tensors[0].dispose()
})
......
import { bufferToImage } from 'tfjs-image-recognition-base';
import { FaceLandmarks5 } from '../../../src';
import { describeWithNets, expectAllTensorsReleased } from '../../utils';
import { expectMtcnnResults } from './expectedResults';
import {
assembleExpectedFullFaceDescriptions,
describeWithNets,
expectAllTensorsReleased,
ExpectedFullFaceDescription,
} from '../../utils';
import { expectAllFacesResults, expectedMtcnnBoxes } from './expectedResults';
describe('allFacesMtcnn', () => {
let imgEl: HTMLImageElement
let facesFaceDescriptors: number[][]
let expectedFullFaceDescriptions: ExpectedFullFaceDescription[]
beforeAll(async () => {
const img = await (await fetch('base/test/images/faces.jpg')).blob()
imgEl = await bufferToImage(img)
facesFaceDescriptors = await (await fetch('base/test/data/facesFaceDescriptorsMtcnn.json')).json()
expectedFullFaceDescriptions = await assembleExpectedFullFaceDescriptions(expectedMtcnnBoxes, 'mtcnnFaceLandmarkPositions.json')
})
describeWithNets('computes full face descriptions', { withAllFacesMtcnn: true }, ({ allFacesMtcnn }) => {
......@@ -25,14 +29,13 @@ describe('allFacesMtcnn', () => {
const results = await allFacesMtcnn(imgEl, forwardParams)
expect(results.length).toEqual(6)
const mtcnnResult = results.map(res => ({
faceDetection: res.detection,
faceLandmarks: res.landmarks as FaceLandmarks5
}))
expectMtcnnResults(mtcnnResult, [0, 1, 2, 3, 4, 5], 1, 1)
results.forEach(({ descriptor }, i) => {
expect(descriptor).toEqual(new Float32Array(facesFaceDescriptors[i]))
})
const expectedScores = [1, 1, 1, 1, 0.99, 0.99]
const deltas = {
maxBoxDelta: 2,
maxLandmarksDelta: 1,
maxDescriptorDelta: 0.4
}
expectAllFacesResults(results, expectedFullFaceDescriptions, expectedScores, deltas)
})
})
......
import * as tf from '@tensorflow/tfjs-core';
import { bufferToImage, NetInput, Point, toNetInput } from '../../../src';
import { describeWithNets, expectAllTensorsReleased, expectPointClose, expectRectClose } from '../../utils';
import { expectedSsdBoxes } from './expectedResults';
import { bufferToImage } from '../../../src';
import {
assembleExpectedFullFaceDescriptions,
describeWithNets,
expectAllTensorsReleased,
ExpectedFullFaceDescription,
} from '../../utils';
import { expectAllFacesResults, expectedSsdBoxes } from './expectedResults';
describe('allFacesSsdMobilenetv1', () => {
let imgEl: HTMLImageElement
let facesFaceLandmarkPositions: Point[][]
let facesFaceDescriptors: number[][]
let expectedFullFaceDescriptions: ExpectedFullFaceDescription[]
beforeAll(async () => {
const img = await (await fetch('base/test/images/faces.jpg')).blob()
imgEl = await bufferToImage(img)
facesFaceLandmarkPositions = await (await fetch('base/test/data/facesFaceLandmarkPositions.json')).json()
facesFaceDescriptors = await (await fetch('base/test/data/facesFaceDescriptorsSsd.json')).json()
expectedFullFaceDescriptions = await assembleExpectedFullFaceDescriptions(expectedSsdBoxes)
})
describeWithNets('computes full face descriptions', { withAllFacesSsdMobilenetv1: true }, ({ allFacesSsdMobilenetv1 }) => {
const expectedScores = [0.97, 0.88, 0.83, 0.82, 0.59, 0.52]
const maxBoxDelta = 5
const maxLandmarkPointsDelta = 1
it('scores > 0.8', async () => {
const results = await allFacesSsdMobilenetv1(imgEl, 0.8)
expect(results.length).toEqual(4)
results.forEach(({ detection, landmarks, descriptor }, i) => {
expect(detection.getImageWidth()).toEqual(imgEl.width)
expect(detection.getImageHeight()).toEqual(imgEl.height)
expect(detection.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(detection.getBox(), expectedSsdBoxes[i], maxBoxDelta)
landmarks.getPositions().forEach((pt, j) => expectPointClose(pt, facesFaceLandmarkPositions[i][j], maxLandmarkPointsDelta))
expect(descriptor).toEqual(new Float32Array(facesFaceDescriptors[i]))
})
const expectedScores = [-1, 0.81, 0.97, 0.88, 0.84, -1]
const deltas = {
maxBoxDelta: 5,
maxLandmarksDelta: 4,
maxDescriptorDelta: 0.01
}
expectAllFacesResults(results, expectedFullFaceDescriptions, expectedScores, deltas)
})
it('scores > 0.5', async () => {
const results = await allFacesSsdMobilenetv1(imgEl, 0.5)
expect(results.length).toEqual(6)
results.forEach(({ detection, landmarks, descriptor }, i) => {
expect(detection.getImageWidth()).toEqual(imgEl.width)
expect(detection.getImageHeight()).toEqual(imgEl.height)
expect(detection.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(detection.getBox(), expectedSsdBoxes[i], maxBoxDelta)
landmarks.getPositions().forEach((pt, j) => expectPointClose(pt, facesFaceLandmarkPositions[i][j], maxLandmarkPointsDelta))
expect(descriptor).toEqual(new Float32Array(facesFaceDescriptors[i]))
})
const expectedScores = [0.54, 0.81, 0.97, 0.88, 0.84, 0.61]
const deltas = {
maxBoxDelta: 5,
maxLandmarksDelta: 4,
maxDescriptorDelta: 0.01
}
expectAllFacesResults(results, expectedFullFaceDescriptions, expectedScores, deltas)
})
})
......@@ -66,8 +65,7 @@ describe('allFacesSsdMobilenetv1', () => {
const tensor = tf.fromPixels(imgEl)
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([tensor])).managed()
await allFacesSsdMobilenetv1(netInput)
await allFacesSsdMobilenetv1(tensor)
})
tensor.dispose()
......@@ -77,7 +75,7 @@ describe('allFacesSsdMobilenetv1', () => {
const tensor = tf.tidy(() => tf.fromPixels(imgEl).expandDims()) as tf.Tensor4D
await expectAllTensorsReleased(async () => {
await allFacesSsdMobilenetv1(await toNetInput(tensor, true))
await allFacesSsdMobilenetv1(tensor)
})
tensor.dispose()
......
import * as tf from '@tensorflow/tfjs-core';
import { TinyYolov2Types } from 'tfjs-tiny-yolov2';
import { bufferToImage, NetInput, Point, toNetInput } from '../../../src';
import { describeWithNets, expectAllTensorsReleased, expectMaxDelta, expectPointClose, expectRectClose } from '../../utils';
import { expectedTinyYolov2SeparableConvBoxes } from './expectedResults';
import { bufferToImage } from '../../../src';
import {
assembleExpectedFullFaceDescriptions,
describeWithNets,
expectAllTensorsReleased,
ExpectedFullFaceDescription,
} from '../../utils';
import { expectAllFacesResults, expectedTinyYolov2Boxes } from './expectedResults';
describe('allFacesTinyYolov2', () => {
let imgEl: HTMLImageElement
let facesFaceLandmarkPositions: Point[][]
let facesFaceDescriptors: number[][]
let expectedFullFaceDescriptions: ExpectedFullFaceDescription[]
beforeAll(async () => {
const img = await (await fetch('base/test/images/faces.jpg')).blob()
imgEl = await bufferToImage(img)
facesFaceLandmarkPositions = await (await fetch('base/test/data/facesFaceLandmarkPositions.json')).json()
facesFaceDescriptors = await (await fetch('base/test/data/facesFaceDescriptorsSsd.json')).json()
expectedFullFaceDescriptions = await assembleExpectedFullFaceDescriptions(expectedTinyYolov2Boxes)
})
describeWithNets('computes full face descriptions', { withAllFacesTinyYolov2: true }, ({ allFacesTinyYolov2 }) => {
it('TinyYolov2Types.SizeType.LG', async () => {
const expectedScores = [0.9, 0.9, 0.89, 0.85, 0.85, 0.85]
const maxBoxDelta = 5
const maxLandmarkPointsDelta = 10
const maxDescriptorDelta = 0.06
const results = await allFacesTinyYolov2(imgEl, { inputSize: TinyYolov2Types.SizeType.LG })
const detectionOrder = [0, 2, 3, 4, 1, 5]
expect(results.length).toEqual(6)
results.forEach(({ detection, landmarks, descriptor }, i) => {
expect(detection.getImageWidth()).toEqual(imgEl.width)
expect(detection.getImageHeight()).toEqual(imgEl.height)
expect(detection.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(detection.getBox(), expectedTinyYolov2SeparableConvBoxes[i], maxBoxDelta)
landmarks.getPositions().forEach((pt, j) => expectPointClose(pt, facesFaceLandmarkPositions[detectionOrder[i]][j], maxLandmarkPointsDelta))
descriptor.forEach((val, j) => expectMaxDelta(val, facesFaceDescriptors[detectionOrder[i]][j], maxDescriptorDelta))
})
const expectedScores = [0.85, 0.88, 0.9, 0.86, 0.9, 0.85]
const deltas = {
maxBoxDelta: 25,
maxLandmarksDelta: 10,
maxDescriptorDelta: 0.24
}
expectAllFacesResults(results, expectedFullFaceDescriptions, expectedScores, deltas)
})
it('TinyYolov2Types.SizeType.MD', async () => {
const expectedScores = [0.85, 0.85, 0.84, 0.83, 0.8, 0.8]
const maxBoxDelta = 17
const maxLandmarkPointsDelta = 16
const maxDescriptorDelta = 0.05
const results = await allFacesTinyYolov2(imgEl, { inputSize: TinyYolov2Types.SizeType.MD })
const boxOrder = [5, 1, 4, 3, 2, 0]
const detectionOrder = [5, 2, 1, 4, 3, 0]
expect(results.length).toEqual(6)
results.forEach(({ detection, landmarks, descriptor }, i) => {
expect(detection.getImageWidth()).toEqual(imgEl.width)
expect(detection.getImageHeight()).toEqual(imgEl.height)
expect(detection.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(detection.getBox(), expectedTinyYolov2SeparableConvBoxes[boxOrder[i]], maxBoxDelta)
landmarks.getPositions().forEach((pt, j) => expectPointClose(pt, facesFaceLandmarkPositions[detectionOrder[i]][j], maxLandmarkPointsDelta))
descriptor.forEach((val, j) => expectMaxDelta(val, facesFaceDescriptors[detectionOrder[i]][j], maxDescriptorDelta))
})
const expectedScores = [0.85, 0.8, 0.8, 0.85, 0.85, 0.82]
const deltas = {
maxBoxDelta: 34,
maxLandmarksDelta: 18,
maxDescriptorDelta: 0.2
}
expectAllFacesResults(results, expectedFullFaceDescriptions, expectedScores, deltas)
})
})
......@@ -76,8 +68,7 @@ describe('allFacesTinyYolov2', () => {
const tensor = tf.fromPixels(imgEl)
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([tensor])).managed()
await allFacesTinyYolov2(netInput)
await allFacesTinyYolov2(tensor)
})
tensor.dispose()
......@@ -87,7 +78,7 @@ describe('allFacesTinyYolov2', () => {
const tensor = tf.tidy(() => tf.fromPixels(imgEl).expandDims()) as tf.Tensor4D
await expectAllTensorsReleased(async () => {
await allFacesTinyYolov2(await toNetInput(tensor, true))
await allFacesTinyYolov2(tensor)
})
tensor.dispose()
......
import * as faceapi from '../../../src';
import { expectMaxDelta, expectPointClose, expectRectClose } from '../../utils';
import { Point } from '../../../src';
import { FullFaceDescription } from '../../../src/classes/FullFaceDescription';
import { euclideanDistance } from '../../../src/euclideanDistance';
import {
ExpectedFullFaceDescription,
expectMaxDelta,
expectPointClose,
expectRectClose,
sortBoxes,
sortByDistanceToOrigin,
sortFullFaceDescriptions,
} from '../../utils';
import { IPoint, IRect } from '../../../src';
import { FaceDetection } from '../../../src/classes/FaceDetection';
import { sortFaceDetections } from '../../utils';
export const expectedSsdBoxes = [
export type BoxAndLandmarksDeltas = {
maxBoxDelta: number
maxLandmarksDelta: number
}
export type AllFacesDeltas = BoxAndLandmarksDeltas & {
maxDescriptorDelta: number
}
export const expectedSsdBoxes = sortBoxes([
{ x: 48, y: 253, width: 104, height: 129 },
{ x: 260, y: 227, width: 76, height: 117 },
{ x: 466, y: 165, width: 88, height: 130 },
{ x: 234, y: 36, width: 84, height: 119 },
{ x: 577, y: 65, width: 84, height: 105 },
{ x: 84, y: 14, width: 79, height: 132 }
]
])
export const expectedMtcnnBoxes = [
{ x: 70, y: 21, width: 112, height: 112 },
{ x: 36, y: 250, width: 133, height: 132 },
{ x: 221, y: 43, width: 112, height: 111 },
{ x: 247, y: 231, width: 106, height: 107 },
{ x: 566, y: 67, width: 104, height: 104 },
{ x: 451, y: 176, width: 122, height: 122 }
]
export const expectedTinyYolov2Boxes = [
export const expectedTinyYolov2Boxes = sortBoxes([
{ x: 52, y: 263, width: 106, height: 102 },
{ x: 455, y: 191, width: 103, height: 97 },
{ x: 236, y: 57, width: 90, height: 85 },
{ x: 257, y: 243, width: 86, height: 95 },
{ x: 578, y: 76, width: 86, height: 91 },
{ x: 87, y: 30, width: 92, height: 93 }
]
])
export const expectedTinyYolov2SeparableConvBoxes = [
export const expectedTinyYolov2SeparableConvBoxes = sortBoxes([
{ x: 42, y: 257, width: 111, height: 121 },
{ x: 454, y: 175, width: 104, height: 121 },
{ x: 230, y: 45, width: 94, height: 104 },
{ x: 574, y: 62, width: 88, height: 113 },
{ x: 260, y: 233, width: 82, height: 104 },
{ x: 83, y: 24, width: 85, height: 111 }
]
])
export const expectedMtcnnFaceLandmarks = [
[new Point(117, 58), new Point(156, 63), new Point(141, 86), new Point(109, 98), new Point(147, 104)],
[new Point(82, 292), new Point(134, 304), new Point(104, 330), new Point(72, 342), new Point(120, 353)],
[new Point(261, 82), new Point(306, 83), new Point(282, 113), new Point(257, 124), new Point(306, 126)],
[new Point(277, 273), new Point(318, 273), new Point(295, 300), new Point(279, 311), new Point(316, 313)],
[new Point(607, 110), new Point(645, 115), new Point(626, 138), new Point(601, 144), new Point(639, 150)],
[new Point(489, 224), new Point(534, 223), new Point(507, 250), new Point(493, 271), new Point(530, 270)]
]
export const expectedMtcnnBoxes = sortBoxes([
{ x: 70, y: 21, width: 112, height: 112 },
{ x: 36, y: 250, width: 133, height: 132 },
{ x: 221, y: 43, width: 112, height: 111 },
{ x: 247, y: 231, width: 106, height: 107 },
{ x: 566, y: 67, width: 104, height: 104 },
{ x: 451, y: 176, width: 122, height: 122 }
])
export function expectMtcnnResults(
results: { faceDetection: faceapi.FaceDetection, faceLandmarks: faceapi.FaceLandmarks5 }[],
boxOrder: number[],
maxBoxDelta: number,
maxLandmarkPointsDelta: number
expectedMtcnnFaceLandmarks: IPoint[][],
deltas: BoxAndLandmarksDeltas
) {
results.forEach((result, i) => {
sortByDistanceToOrigin(results, res => res.faceDetection.box).forEach((result, i) => {
const { faceDetection, faceLandmarks } = result
expect(faceDetection instanceof faceapi.FaceDetection).toBe(true)
expect(faceLandmarks instanceof faceapi.FaceLandmarks5).toBe(true)
expectRectClose(faceDetection.getBox(), expectedMtcnnBoxes[boxOrder[i]], maxBoxDelta)
faceLandmarks.getPositions().forEach((pt, j) => expectPointClose(pt, expectedMtcnnFaceLandmarks[boxOrder[i]][j], maxLandmarkPointsDelta))
expectRectClose(faceDetection.getBox(), expectedMtcnnBoxes[i], deltas.maxBoxDelta)
faceLandmarks.getPositions().forEach((pt, j) => expectPointClose(pt, expectedMtcnnFaceLandmarks[i][j], deltas.maxLandmarksDelta))
expectMaxDelta(faceDetection.getScore(), 0.99, 0.01)
})
}
export function expectDetectionResults(results: FaceDetection[], allExpectedFaceDetections: IRect[], expectedScores: number[], maxBoxDelta: number) {
const expectedDetections = expectedScores
.map((score, i) => ({
score,
...allExpectedFaceDetections[i]
}))
.filter(expected => expected.score !== -1)
const sortedResults = sortFaceDetections(results)
expectedDetections.forEach((expectedDetection, i) => {
const det = sortedResults[i]
expect(det.score).toBeCloseTo(expectedDetection.score, 2)
expectRectClose(det.box, expectedDetection, maxBoxDelta)
})
}
export function expectAllFacesResults(results: FullFaceDescription[], allExpectedFullFaceDescriptions: ExpectedFullFaceDescription[], expectedScores: number[], deltas: AllFacesDeltas) {
const expectedFullFaceDescriptions = expectedScores
.map((score, i) => ({
score,
...allExpectedFullFaceDescriptions[i]
}))
.filter(expected => expected.score !== -1)
const sortedResults = sortFullFaceDescriptions(results)
expectedFullFaceDescriptions.forEach((expected, i) => {
const { detection, landmarks, descriptor } = sortedResults[i]
expect(detection.score).toBeCloseTo(expected.score, 2)
expectRectClose(detection.box, expected.detection, deltas.maxBoxDelta)
landmarks.getPositions().forEach((pt, j) => expectPointClose(pt, expected.landmarks[j], deltas.maxLandmarksDelta))
expect(euclideanDistance(descriptor, expected.descriptor)).toBeLessThan(deltas.maxDescriptorDelta)
})
}
\ No newline at end of file
import * as faceapi from '../../../src';
import { describeWithNets, expectAllTensorsReleased, expectRectClose } from '../../utils';
import { expectedSsdBoxes } from './expectedResults';
import { expectedSsdBoxes, expectDetectionResults } from './expectedResults';
describe('faceDetectionNet', () => {
......@@ -13,62 +13,52 @@ describe('faceDetectionNet', () => {
describeWithNets('uncompressed weights', { withFaceDetectionNet: { quantized: false } }, ({ faceDetectionNet }) => {
const expectedScores = [0.98, 0.89, 0.82, 0.75, 0.58, 0.55]
const maxBoxDelta = 1
it('scores > 0.8', async () => {
const detections = await faceDetectionNet.locateFaces(imgEl) as faceapi.FaceDetection[]
expect(detections.length).toEqual(3)
detections.forEach((det, i) => {
expect(det.getImageWidth()).toEqual(imgEl.width)
expect(det.getImageHeight()).toEqual(imgEl.height)
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
})
const expectedScores = [-1, -1, 0.98, 0.88, 0.81, -1]
const maxBoxDelta = 3
expectDetectionResults(detections, expectedSsdBoxes, expectedScores, maxBoxDelta)
})
it('scores > 0.5', async () => {
const detections = await faceDetectionNet.locateFaces(imgEl, 0.5) as faceapi.FaceDetection[]
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getImageWidth()).toEqual(imgEl.width)
expect(det.getImageHeight()).toEqual(imgEl.height)
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
})
const expectedScores = [0.57, 0.74, 0.98, 0.88, 0.81, 0.58]
const maxBoxDelta = 3
expectDetectionResults(detections, expectedSsdBoxes, expectedScores, maxBoxDelta)
})
})
describeWithNets('quantized weights', { withFaceDetectionNet: { quantized: true } }, ({ faceDetectionNet }) => {
const expectedScores = [0.97, 0.88, 0.83, 0.82, 0.59, 0.52]
const maxBoxDelta = 5
it('scores > 0.8', async () => {
const detections = await faceDetectionNet.locateFaces(imgEl) as faceapi.FaceDetection[]
expect(detections.length).toEqual(4)
detections.forEach((det, i) => {
expect(det.getImageWidth()).toEqual(imgEl.width)
expect(det.getImageHeight()).toEqual(imgEl.height)
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
})
const expectedScores = [-1, 0.81, 0.97, 0.88, 0.84, -1]
const maxBoxDelta = 4
expectDetectionResults(detections, expectedSsdBoxes, expectedScores, maxBoxDelta)
})
it('scores > 0.5', async () => {
const detections = await faceDetectionNet.locateFaces(imgEl, 0.5) as faceapi.FaceDetection[]
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getImageWidth()).toEqual(imgEl.width)
expect(det.getImageHeight()).toEqual(imgEl.height)
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
})
const expectedScores = [0.54, 0.81, 0.97, 0.88, 0.84, 0.61]
const maxBoxDelta = 5
expectDetectionResults(detections, expectedSsdBoxes, expectedScores, maxBoxDelta)
})
})
......
......@@ -11,7 +11,7 @@ import {
toNetInput,
} from '../../../src';
import { FaceLandmarks68 } from '../../../src/classes/FaceLandmarks68';
import { FaceLandmarkNet } from '../../../src/faceLandmarkNet/FaceLandmarkNet';
import { FaceLandmark68Net } from '../../../src/faceLandmarkNet/FaceLandmark68Net';
import { describeWithNets, expectAllTensorsReleased, expectMaxDelta } from '../../utils';
function getInputDims (input: tf.Tensor | TMediaElement): Dimensions {
......@@ -54,8 +54,8 @@ describe('faceLandmarkNet', () => {
expect(result.getShift().x).toEqual(0)
expect(result.getShift().y).toEqual(0)
result.getPositions().forEach(({ x, y }, i) => {
expectMaxDelta(x, faceLandmarkPositions1[i].x, 0.1)
expectMaxDelta(y, faceLandmarkPositions1[i].y, 0.1)
expectMaxDelta(x, faceLandmarkPositions1[i].x, 1)
expectMaxDelta(y, faceLandmarkPositions1[i].y, 1)
})
})
......@@ -68,8 +68,8 @@ describe('faceLandmarkNet', () => {
expect(result.getShift().x).toEqual(0)
expect(result.getShift().y).toEqual(0)
result.getPositions().forEach(({ x, y }, i) => {
expectMaxDelta(x, faceLandmarkPositionsRect[i].x, 0.1)
expectMaxDelta(y, faceLandmarkPositionsRect[i].y, 0.1)
expectMaxDelta(x, faceLandmarkPositionsRect[i].x, 2)
expectMaxDelta(y, faceLandmarkPositionsRect[i].y, 2)
})
})
......@@ -128,8 +128,8 @@ describe('faceLandmarkNet', () => {
expect(result.getShift().x).toEqual(0)
expect(result.getShift().y).toEqual(0)
result.getPositions().forEach(({ x, y }, i) => {
expectMaxDelta(x, faceLandmarkPositions[batchIdx][i].x, 0.1)
expectMaxDelta(y, faceLandmarkPositions[batchIdx][i].y, 0.1)
expectMaxDelta(x, faceLandmarkPositions[batchIdx][i].x, 2)
expectMaxDelta(y, faceLandmarkPositions[batchIdx][i].y, 2)
})
})
})
......@@ -153,33 +153,8 @@ describe('faceLandmarkNet', () => {
expect(result.getShift().x).toEqual(0)
expect(result.getShift().y).toEqual(0)
result.getPositions().forEach(({ x, y }, i) => {
expectMaxDelta(x, faceLandmarkPositions[batchIdx][i].x, 0.1)
expectMaxDelta(y, faceLandmarkPositions[batchIdx][i].y, 0.1)
})
})
})
it('computes face landmarks for tf.Tensor4D', async () => {
const inputs = [imgEl1, imgEl2].map(el => tf.fromPixels(el))
const faceLandmarkPositions = [
faceLandmarkPositions1,
faceLandmarkPositions2,
faceLandmarkPositionsRect
]
const results = await faceLandmarkNet.detectLandmarks(tf.stack(inputs) as tf.Tensor4D) as FaceLandmarks68[]
expect(Array.isArray(results)).toBe(true)
expect(results.length).toEqual(2)
results.forEach((result, batchIdx) => {
const { width, height } = getInputDims(inputs[batchIdx])
expect(result.getImageWidth()).toEqual(width)
expect(result.getImageHeight()).toEqual(height)
expect(result.getShift().x).toEqual(0)
expect(result.getShift().y).toEqual(0)
result.getPositions().forEach(({ x, y }, i) => {
expectMaxDelta(x, faceLandmarkPositions[batchIdx][i].x, 0.1)
expectMaxDelta(y, faceLandmarkPositions[batchIdx][i].y, 0.1)
expectMaxDelta(x, faceLandmarkPositions[batchIdx][i].x, 1)
expectMaxDelta(y, faceLandmarkPositions[batchIdx][i].y, 1)
})
})
})
......@@ -203,8 +178,8 @@ describe('faceLandmarkNet', () => {
expect(result.getShift().x).toEqual(0)
expect(result.getShift().y).toEqual(0)
result.getPositions().forEach(({ x, y }, i) => {
expectMaxDelta(x, faceLandmarkPositions[batchIdx][i].x, 0.1)
expectMaxDelta(y, faceLandmarkPositions[batchIdx][i].y, 0.1)
expectMaxDelta(x, faceLandmarkPositions[batchIdx][i].x, 1)
expectMaxDelta(y, faceLandmarkPositions[batchIdx][i].y, 1)
})
})
})
......@@ -230,7 +205,7 @@ describe('faceLandmarkNet', () => {
it('disposes all param tensors', async () => {
await expectAllTensorsReleased(async () => {
const net = new FaceLandmarkNet()
const net = new FaceLandmark68Net()
await net.load('base/weights')
net.dispose()
})
......@@ -242,7 +217,7 @@ describe('faceLandmarkNet', () => {
it('single image element', async () => {
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([imgEl1])).managed()
const netInput = new NetInput([imgEl1])
const outTensor = await faceLandmarkNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -250,7 +225,7 @@ describe('faceLandmarkNet', () => {
it('multiple image elements', async () => {
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([imgEl1, imgEl1, imgEl1])).managed()
const netInput = new NetInput([imgEl1, imgEl1, imgEl1])
const outTensor = await faceLandmarkNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -260,7 +235,7 @@ describe('faceLandmarkNet', () => {
const tensor = tf.fromPixels(imgEl1)
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([tensor])).managed()
const netInput = new NetInput([tensor])
const outTensor = await faceLandmarkNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -272,7 +247,7 @@ describe('faceLandmarkNet', () => {
const tensors = [imgEl1, imgEl1, imgEl1].map(el => tf.fromPixels(el))
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput(tensors)).managed()
const netInput = new NetInput(tensors)
const outTensor = await faceLandmarkNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -284,7 +259,7 @@ describe('faceLandmarkNet', () => {
const tensor = tf.tidy(() => tf.fromPixels(imgEl1).expandDims()) as tf.Tensor4D
await expectAllTensorsReleased(async () => {
const outTensor = await faceLandmarkNet.forwardInput(await toNetInput(tensor, true))
const outTensor = await faceLandmarkNet.forwardInput(await toNetInput(tensor))
outTensor.dispose()
})
......@@ -296,7 +271,7 @@ describe('faceLandmarkNet', () => {
.map(el => tf.tidy(() => tf.fromPixels(el).expandDims())) as tf.Tensor4D[]
await expectAllTensorsReleased(async () => {
const outTensor = await faceLandmarkNet.forwardInput(await toNetInput(tensors, true))
const outTensor = await faceLandmarkNet.forwardInput(await toNetInput(tensors))
outTensor.dispose()
})
......
import * as tf from '@tensorflow/tfjs-core';
import { bufferToImage, FaceRecognitionNet, NetInput, toNetInput } from '../../../src';
import { euclideanDistance } from '../../../src/euclideanDistance';
import { createFaceRecognitionNet } from '../../../src/faceRecognitionNet';
import { describeWithNets, expectAllTensorsReleased } from '../../utils';
......@@ -30,13 +31,13 @@ describe('faceRecognitionNet', () => {
it('computes face descriptor for squared input', async () => {
const result = await faceRecognitionNet.computeFaceDescriptor(imgEl1) as Float32Array
expect(result.length).toEqual(128)
expect(result).toEqual(new Float32Array(faceDescriptor1))
expect(euclideanDistance(result, faceDescriptor1)).toBeLessThan(0.1)
})
it('computes face descriptor for rectangular input', async () => {
const result = await faceRecognitionNet.computeFaceDescriptor(imgElRect) as Float32Array
expect(result.length).toEqual(128)
expect(result).toEqual(new Float32Array(faceDescriptorRect))
expect(euclideanDistance(result, faceDescriptorRect)).toBeLessThan(0.1)
})
})
......@@ -75,11 +76,11 @@ describe('faceRecognitionNet', () => {
expect(Array.isArray(results)).toBe(true)
expect(results.length).toEqual(3)
results.forEach((result, batchIdx) => {
expect(result).toEqual(new Float32Array(faceDescriptors[batchIdx]))
expect(euclideanDistance(result, faceDescriptors[batchIdx])).toBeLessThan(0.1)
})
})
it('computes face landmarks for batch of tf.Tensor3D', async () => {
it('computes face descriptors for batch of tf.Tensor3D', async () => {
const inputs = [imgEl1, imgEl2, imgElRect].map(el => tf.fromPixels(el))
const faceDescriptors = [
......@@ -92,28 +93,11 @@ describe('faceRecognitionNet', () => {
expect(Array.isArray(results)).toBe(true)
expect(results.length).toEqual(3)
results.forEach((result, batchIdx) => {
expect(result).toEqual(new Float32Array(faceDescriptors[batchIdx]))
})
})
it('computes face landmarks for tf.Tensor4D', async () => {
const inputs = [imgEl1, imgEl2].map(el => tf.fromPixels(el))
const faceDescriptors = [
faceDescriptor1,
faceDescriptor2,
faceDescriptorRect
]
const results = await faceRecognitionNet.computeFaceDescriptor(tf.stack(inputs) as tf.Tensor4D) as Float32Array[]
expect(Array.isArray(results)).toBe(true)
expect(results.length).toEqual(2)
results.forEach((result, batchIdx) => {
expect(result).toEqual(new Float32Array(faceDescriptors[batchIdx]))
expect(euclideanDistance(result, faceDescriptors[batchIdx])).toBeLessThan(0.1)
})
})
it('computes face landmarks for batch of mixed inputs', async () => {
it('computes face descriptors for batch of mixed inputs', async () => {
const inputs = [imgEl1, tf.fromPixels(imgEl2), tf.fromPixels(imgElRect)]
const faceDescriptors = [
......@@ -126,7 +110,7 @@ describe('faceRecognitionNet', () => {
expect(Array.isArray(results)).toBe(true)
expect(results.length).toEqual(3)
results.forEach((result, batchIdx) => {
expect(result).toEqual(new Float32Array(faceDescriptors[batchIdx]))
expect(euclideanDistance(result, faceDescriptors[batchIdx])).toBeLessThan(0.1)
})
})
......@@ -163,7 +147,7 @@ describe('faceRecognitionNet', () => {
it('single image element', async () => {
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([imgEl1])).managed()
const netInput = new NetInput([imgEl1])
const outTensor = await faceRecognitionNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -171,7 +155,7 @@ describe('faceRecognitionNet', () => {
it('multiple image elements', async () => {
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([imgEl1, imgEl1, imgEl1])).managed()
const netInput = new NetInput([imgEl1, imgEl1, imgEl1])
const outTensor = await faceRecognitionNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -181,7 +165,7 @@ describe('faceRecognitionNet', () => {
const tensor = tf.fromPixels(imgEl1)
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput([tensor])).managed()
const netInput = new NetInput([tensor])
const outTensor = await faceRecognitionNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -193,7 +177,7 @@ describe('faceRecognitionNet', () => {
const tensors = [imgEl1, imgEl1, imgEl1].map(el => tf.fromPixels(el))
await expectAllTensorsReleased(async () => {
const netInput = (new NetInput(tensors)).managed()
const netInput = new NetInput(tensors)
const outTensor = await faceRecognitionNet.forwardInput(netInput)
outTensor.dispose()
})
......@@ -205,7 +189,7 @@ describe('faceRecognitionNet', () => {
const tensor = tf.tidy(() => tf.fromPixels(imgEl1).expandDims()) as tf.Tensor4D
await expectAllTensorsReleased(async () => {
const outTensor = await faceRecognitionNet.forwardInput(await toNetInput(tensor, true))
const outTensor = await faceRecognitionNet.forwardInput(await toNetInput(tensor))
outTensor.dispose()
})
......@@ -217,7 +201,7 @@ describe('faceRecognitionNet', () => {
.map(el => tf.tidy(() => tf.fromPixels(el).expandDims())) as tf.Tensor4D[]
await expectAllTensorsReleased(async () => {
const outTensor = await faceRecognitionNet.forwardInput(await toNetInput(tensors, true))
const outTensor = await faceRecognitionNet.forwardInput(await toNetInput(tensors))
outTensor.dispose()
})
......
import * as faceapi from '../../../src';
import { describeWithNets, expectAllTensorsReleased } from '../../utils';
import { describeWithNets, expectAllTensorsReleased, sortByDistanceToOrigin } from '../../utils';
import { expectMtcnnResults } from './expectedResults';
import { IPoint } from '../../../src';
describe('mtcnn', () => {
let imgEl: HTMLImageElement
let expectedMtcnnLandmarks: IPoint[][]
beforeAll(async () => {
const img = await (await fetch('base/test/images/faces.jpg')).blob()
imgEl = await faceapi.bufferToImage(img)
expectedMtcnnLandmarks = await (await fetch('base/test/data/mtcnnFaceLandmarkPositions.json')).json()
})
describeWithNets('uncompressed weights', { withMtcnn: { quantized: false } }, ({ mtcnn }) => {
......@@ -22,7 +25,12 @@ describe('mtcnn', () => {
const results = await mtcnn.forward(imgEl, forwardParams)
expect(results.length).toEqual(6)
expectMtcnnResults(results, [0, 1, 2, 3, 4, 5], 1, 1)
const deltas = {
maxBoxDelta: 2,
maxLandmarksDelta: 5
}
expectMtcnnResults(results, expectedMtcnnLandmarks, deltas)
})
it('minFaceSize = 80, finds all faces', async () => {
......@@ -33,7 +41,11 @@ describe('mtcnn', () => {
const results = await mtcnn.forward(imgEl, forwardParams)
expect(results.length).toEqual(6)
expectMtcnnResults(results, [0, 5, 3, 1, 2, 4], 12, 12)
const deltas = {
maxBoxDelta: 15,
maxLandmarksDelta: 13
}
expectMtcnnResults(results, expectedMtcnnLandmarks, deltas)
})
it('all optional params passed, finds all faces', async () => {
......@@ -46,7 +58,12 @@ describe('mtcnn', () => {
const results = await mtcnn.forward(imgEl, forwardParams)
expect(results.length).toEqual(6)
expectMtcnnResults(results, [5, 1, 4, 2, 3, 0], 6, 10)
const deltas = {
maxBoxDelta: 8,
maxLandmarksDelta: 7
}
expectMtcnnResults(results, expectedMtcnnLandmarks, deltas)
})
it('scale steps passed, finds all faces', async () => {
......@@ -56,7 +73,12 @@ describe('mtcnn', () => {
const results = await mtcnn.forward(imgEl, forwardParams)
expect(results.length).toEqual(6)
expectMtcnnResults(results, [5, 1, 3, 0, 2, 4], 7, 15)
const deltas = {
maxBoxDelta: 8,
maxLandmarksDelta: 10
}
expectMtcnnResults(results, expectedMtcnnLandmarks, deltas)
})
})
......
import { TinyYolov2Types } from 'tfjs-tiny-yolov2';
import { bufferToImage, createTinyYolov2, TinyYolov2 } from '../../../src';
import { describeWithNets, expectAllTensorsReleased, expectRectClose } from '../../utils';
import { expectedTinyYolov2Boxes } from './expectedResults';
import { describeWithNets, expectAllTensorsReleased } from '../../utils';
import { expectDetectionResults, expectedTinyYolov2Boxes } from './expectedResults';
describe('tinyYolov2', () => {
......@@ -18,43 +18,31 @@ describe('tinyYolov2', () => {
it('inputSize lg, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: TinyYolov2Types.SizeType.LG })
const expectedScores = [0.86, 0.86, 0.85, 0.83, 0.81, 0.81]
const maxBoxDelta = 3
const boxOrder = [0, 1, 2, 3, 4, 5]
const expectedScores = [0.8, 0.85, 0.86, 0.83, 0.86, 0.81]
const maxBoxDelta = 4
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2Boxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
it('inputSize md, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: TinyYolov2Types.SizeType.MD })
const expectedScores = [0.89, 0.87, 0.83, 0.82, 0.81, 0.72]
const maxBoxDelta = 16
const boxOrder = [5, 4, 0, 2, 1, 3]
const expectedScores = [0.89, 0.81, 0.82, 0.72, 0.81, 0.86]
const maxBoxDelta = 27
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2Boxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
it('inputSize custom, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: 416 })
const expectedScores = [0.89, 0.87, 0.83, 0.82, 0.81, 0.72]
const maxBoxDelta = 16
const boxOrder = [5, 4, 0, 2, 1, 3]
const expectedScores = [0.89, 0.81, 0.82, 0.72, 0.81, 0.86]
const maxBoxDelta = 27
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2Boxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
})
......@@ -64,43 +52,31 @@ describe('tinyYolov2', () => {
it('inputSize lg, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: TinyYolov2Types.SizeType.LG })
const expectedScores = [0.86, 0.86, 0.85, 0.83, 0.81, 0.81]
const expectedScores = [0.81, 0.85, 0.86, 0.83, 0.86, 0.81]
const maxBoxDelta = 1
const boxOrder = [0, 1, 2, 3, 4, 5]
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2Boxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
it('inputSize md, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: TinyYolov2Types.SizeType.MD })
const expectedScores = [0.89, 0.87, 0.83, 0.83, 0.81, 0.73]
const maxBoxDelta = 14
const boxOrder = [5, 4, 2, 0, 1, 3]
const expectedScores = [0.89, 0.82, 0.82, 0.72, 0.81, 0.86]
const maxBoxDelta = 24
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2Boxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
it('inputSize custom, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: 416 })
const expectedScores = [0.89, 0.87, 0.83, 0.83, 0.81, 0.73]
const maxBoxDelta = 14
const boxOrder = [5, 4, 2, 0, 1, 3]
const expectedScores = [0.89, 0.82, 0.82, 0.72, 0.81, 0.86]
const maxBoxDelta = 24
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2Boxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
})
......
......@@ -2,7 +2,7 @@ import { TinyYolov2Types } from 'tfjs-tiny-yolov2';
import { bufferToImage, createTinyYolov2, TinyYolov2 } from '../../../src';
import { describeWithNets, expectAllTensorsReleased, expectRectClose } from '../../utils';
import { expectedTinyYolov2SeparableConvBoxes } from './expectedResults';
import { expectedTinyYolov2SeparableConvBoxes, expectDetectionResults, expectedTinyYolov2Boxes } from './expectedResults';
describe('tinyYolov2, with separable convolutions', () => {
......@@ -18,43 +18,31 @@ describe('tinyYolov2, with separable convolutions', () => {
it('inputSize lg, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: TinyYolov2Types.SizeType.LG })
const expectedScores = [0.9, 0.9, 0.89, 0.85, 0.85, 0.85]
const maxBoxDelta = 1
const boxOrder = [0, 1, 2, 3, 4, 5]
const expectedScores = [0.85, 0.88, 0.9, 0.85, 0.9, 0.85]
const maxBoxDelta = 25
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2SeparableConvBoxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
it('inputSize md, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: TinyYolov2Types.SizeType.MD })
const expectedScores = [0.85, 0.85, 0.84, 0.83, 0.8, 0.8]
const maxBoxDelta = 17
const boxOrder = [5, 1, 4, 3, 2, 0]
const expectedScores = [0.85, 0.8, 0.8, 0.85, 0.85, 0.83]
const maxBoxDelta = 34
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2SeparableConvBoxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
it('inputSize custom, finds all faces', async () => {
const detections = await tinyYolov2.locateFaces(imgEl, { inputSize: 416 })
const expectedScores = [0.85, 0.85, 0.84, 0.83, 0.8, 0.8]
const maxBoxDelta = 17
const boxOrder = [5, 1, 4, 3, 2, 0]
const expectedScores = [0.85, 0.8, 0.8, 0.85, 0.85, 0.83]
const maxBoxDelta = 34
expect(detections.length).toEqual(6)
detections.forEach((det, i) => {
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
expectRectClose(det.getBox(), expectedTinyYolov2SeparableConvBoxes[boxOrder[i]], maxBoxDelta)
})
expectDetectionResults(detections, expectedTinyYolov2Boxes, expectedScores, maxBoxDelta)
})
})
......
import * as tf from '@tensorflow/tfjs-core';
import { FaceLandmark68NetBase } from '../../../src/faceLandmarkNet/FaceLandmark68NetBase';
describe('FaceLandmark68NetBase', () => {
describe('postProcess', () => {
const net = new FaceLandmark68NetBase('')
describe('single batch', () => {
it('transform x coordinates for width < height', () => {
const landmarksData = Array(136).fill(0)
landmarksData[0] = 0.4
landmarksData[1] = 0.55
landmarksData[2] = 0.2
landmarksData[3] = 0.55
landmarksData[4] = 0.1
landmarksData[5] = 0.55
const out = net.postProcess(
tf.tensor2d(landmarksData, [1, 136]),
128,
[{ width: 200, height: 300 }]
).dataSync()
expect(out[0]).toBeCloseTo(0.35, 2)
expect(out[1]).toBeCloseTo(0.55, 2)
expect(out[2]).toBeCloseTo(0.05, 2)
expect(out[3]).toBeCloseTo(0.55, 2)
expect(out[4]).toBeCloseTo(-0.1, 2)
expect(out[5]).toBeCloseTo(0.55, 2)
})
it('transform y coordinates for height < width', () => {
const landmarksData = Array(136).fill(0)
landmarksData[0] = 0.55
landmarksData[1] = 0.4
landmarksData[2] = 0.55
landmarksData[3] = 0.2
landmarksData[4] = 0.55
landmarksData[5] = 0.1
const out = net.postProcess(
tf.tensor2d(landmarksData, [1, 136]),
128,
[{ width: 300, height: 200 }]
).dataSync()
expect(out[0]).toBeCloseTo(0.55, 2)
expect(out[1]).toBeCloseTo(0.35, 2)
expect(out[2]).toBeCloseTo(0.55, 2)
expect(out[3]).toBeCloseTo(0.05, 2)
expect(out[4]).toBeCloseTo(0.55, 2)
expect(out[5]).toBeCloseTo(-0.1, 2)
})
it('no transformation for height === width', () => {
const landmarksData = Array(136).fill(0)
landmarksData[0] = 0.55
landmarksData[1] = 0.4
landmarksData[2] = 0.55
landmarksData[3] = 0.2
landmarksData[4] = 0.55
landmarksData[5] = 0.1
const out = net.postProcess(
tf.tensor2d(landmarksData, [1, 136]),
128,
[{ width: 300, height: 300 }]
).dataSync()
expect(out[0]).toBeCloseTo(0.55, 2)
expect(out[1]).toBeCloseTo(0.4, 2)
expect(out[2]).toBeCloseTo(0.55, 2)
expect(out[3]).toBeCloseTo(0.2, 2)
expect(out[4]).toBeCloseTo(0.55, 2)
expect(out[5]).toBeCloseTo(0.1, 2)
})
})
describe('multiple batches', () => {
it('transform coordinates correctly for multiple batches', () => {
const landmarksData1 = Array(136).fill(0)
landmarksData1[0] = 0.4
landmarksData1[1] = 0.55
landmarksData1[2] = 0.2
landmarksData1[3] = 0.55
landmarksData1[4] = 0.1
landmarksData1[5] = 0.55
const landmarksData2 = Array(136).fill(0)
landmarksData2[0] = 0.55
landmarksData2[1] = 0.4
landmarksData2[2] = 0.55
landmarksData2[3] = 0.2
landmarksData2[4] = 0.55
landmarksData2[5] = 0.1
const out = net.postProcess(
tf.tensor2d(landmarksData1.concat(landmarksData2).concat(landmarksData1), [3, 136]),
128,
[{ width: 200, height: 300 }, { width: 300, height: 200 }, { width: 300, height: 300 }]
).dataSync()
expect(out[0]).toBeCloseTo(0.35, 2)
expect(out[1]).toBeCloseTo(0.55, 2)
expect(out[2]).toBeCloseTo(0.05, 2)
expect(out[3]).toBeCloseTo(0.55, 2)
expect(out[4]).toBeCloseTo(-0.1, 2)
expect(out[5]).toBeCloseTo(0.55, 2)
expect(out[0 + 136]).toBeCloseTo(0.55, 2)
expect(out[1 + 136]).toBeCloseTo(0.35, 2)
expect(out[2 + 136]).toBeCloseTo(0.55, 2)
expect(out[3 + 136]).toBeCloseTo(0.05, 2)
expect(out[4 + 136]).toBeCloseTo(0.55, 2)
expect(out[5 + 136]).toBeCloseTo(-0.1, 2)
expect(out[0 + (136 * 2)]).toBeCloseTo(0.4, 2)
expect(out[1 + (136 * 2)]).toBeCloseTo(0.55, 2)
expect(out[2 + (136 * 2)]).toBeCloseTo(0.2, 2)
expect(out[3 + (136 * 2)]).toBeCloseTo(0.55, 2)
expect(out[4 + (136 * 2)]).toBeCloseTo(0.1, 2)
expect(out[5 + (136 * 2)]).toBeCloseTo(0.55, 2)
})
})
})
})
......@@ -11,6 +11,9 @@ import {
TinyYolov2,
} from '../src/';
import { allFacesMtcnnFactory, allFacesSsdMobilenetv1Factory, allFacesTinyYolov2Factory } from '../src/allFacesFactory';
import { FaceDetection } from '../src/classes/FaceDetection';
import { FaceLandmarks } from '../src/classes/FaceLandmarks';
import { FullFaceDescription } from '../src/classes/FullFaceDescription';
import { allFacesMtcnnFunction, allFacesSsdMobilenetv1Function, allFacesTinyYolov2Function } from '../src/globalApi';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000
......@@ -25,14 +28,16 @@ export async function expectAllTensorsReleased(fn: () => any) {
expect(tf.memory().numTensors - numTensorsBefore).toEqual(0)
}
export function pointDistance(pt1: IPoint, pt2: IPoint) {
return Math.sqrt(Math.pow(pt1.x - pt2.x, 2) + Math.pow(pt1.y - pt2.y, 2))
}
export function expectPointClose(
result: IPoint,
expectedPoint: IPoint,
maxDelta: number
) {
const { x, y } = result
expectMaxDelta(x, expectedPoint.x, maxDelta)
expectMaxDelta(y, expectedPoint.y, maxDelta)
expect(pointDistance(result, expectedPoint)).toBeLessThan(maxDelta)
}
export function expectRectClose(
......@@ -40,10 +45,52 @@ export function expectRectClose(
expectedBox: IRect,
maxDelta: number
) {
const { width, height } = result
expectPointClose(result, expectedBox, maxDelta)
expectMaxDelta(width, expectedBox.width, maxDelta)
expectMaxDelta(height, expectedBox.height, maxDelta)
expectPointClose({ x: result.width, y: result.height }, { x:expectedBox.width, y: expectedBox.height }, maxDelta)
}
export function sortByDistanceToOrigin<T>(objs: T[], originGetter: (obj: T) => IPoint) {
const origin = { x: 0, y: 0 }
return objs.sort((obj1, obj2) =>
pointDistance(originGetter(obj1), origin)
- pointDistance(originGetter(obj2), origin)
)
}
export function sortBoxes(boxes: IRect[]) {
return sortByDistanceToOrigin(boxes, rect => rect)
}
export function sortFaceDetections(boxes: FaceDetection[]) {
return sortByDistanceToOrigin(boxes, det => det.box)
}
export function sortLandmarks(landmarks: FaceLandmarks[]) {
return sortByDistanceToOrigin(landmarks, l => l.getPositions()[0])
}
export function sortFullFaceDescriptions(descs: FullFaceDescription[]) {
return sortByDistanceToOrigin(descs, d => d.detection.box)
}
export type ExpectedFullFaceDescription = {
detection: IRect
landmarks: IPoint[]
descriptor: Float32Array
}
export async function assembleExpectedFullFaceDescriptions(
detections: IRect[],
landmarksFile: string = 'facesFaceLandmarkPositions.json'
): Promise<ExpectedFullFaceDescription[]> {
const landmarks = await (await fetch(`base/test/data/${landmarksFile}`)).json()
const descriptors = await (await fetch('base/test/data/facesFaceDescriptors.json')).json()
return detections.map((detection, i) => ({
detection,
landmarks: landmarks[i],
descriptor: descriptors[i]
}))
}
export type WithNetOptions = {
......
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