improve face compare performance

pull/130/head
Vladimir Mandic 2022-09-14 08:18:51 -04:00
parent 7f613367a3
commit fa33c1281c
15 changed files with 58 additions and 24 deletions

View File

@ -9,7 +9,7 @@
## Changelog
### **HEAD -> master** 2022/08/24 mandic00@live.com
### **HEAD -> master** 2022/09/04 mandic00@live.com
### **1.7.3** 2022/08/24 mandic00@live.com

35
demo/node-face-compare.js Normal file
View File

@ -0,0 +1,35 @@
/**
* FaceAPI demo that loads two images and finds similarity most prominant face in each image
*/
const fs = require('fs');
const tf = require('@tensorflow/tfjs-node');
const faceapi = require('../dist/face-api.node');
let optionsSSDMobileNet;
const getDescriptors = async (imageFile) => {
const buffer = fs.readFileSync(imageFile);
const tensor = tf.node.decodeImage(buffer, 3);
const faces = await faceapi.detectAllFaces(tensor, optionsSSDMobileNet)
.withFaceLandmarks()
.withFaceDescriptors();
tf.dispose(tensor);
return faces.map((face) => face.descriptor);
};
const main = async (file1, file2) => {
console.log('input images:', file1, file2); // eslint-disable-line no-console
await tf.ready();
await faceapi.nets.ssdMobilenetv1.loadFromDisk('model');
optionsSSDMobileNet = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.5, maxResults: 1 });
await faceapi.nets.faceLandmark68Net.loadFromDisk('model');
await faceapi.nets.faceRecognitionNet.loadFromDisk('model');
const desc1 = await getDescriptors(file1);
const desc2 = await getDescriptors(file2);
const distance = faceapi.euclideanDistance(desc1[0], desc2[0]); // only compare first found face in each image
console.log('distance between most prominant detected faces:', distance); // eslint-disable-line no-console
console.log('similarity between most prominant detected faces:', 1 - distance); // eslint-disable-line no-console
};
main('demo/sample1.jpg', 'demo/sample2.jpg');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/face-api.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -42,7 +42,7 @@
"tfjs"
],
"devDependencies": {
"@microsoft/api-extractor": "^7.30.0",
"@microsoft/api-extractor": "^7.31.0",
"@tensorflow/tfjs": "^3.20.0",
"@tensorflow/tfjs-backend-cpu": "^3.20.0",
"@tensorflow/tfjs-backend-wasm": "^3.20.0",
@ -54,15 +54,15 @@
"@tensorflow/tfjs-layers": "^3.20.0",
"@tensorflow/tfjs-node": "^3.20.0",
"@tensorflow/tfjs-node-gpu": "^3.20.0",
"@types/node": "^18.7.14",
"@types/node": "^18.7.18",
"@types/offscreencanvas": "^2019.7.0",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"@vladmandic/build": "^0.7.11",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"@vladmandic/build": "^0.7.12",
"@vladmandic/pilogger": "^0.4.6",
"@vladmandic/tfjs": "github:vladmandic/tfjs",
"esbuild": "^0.15.7",
"eslint": "^8.23.0",
"eslint": "^8.23.1",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-json": "^3.1.0",
@ -72,6 +72,6 @@
"seedrandom": "^3.0.5",
"tslib": "^2.4.0",
"typedoc": "^0.23.14",
"typescript": "4.8.2"
"typescript": "4.8.3"
}
}

View File

@ -103,7 +103,7 @@ export abstract class NeuralNetwork<TNetParams> {
const { readFile } = env.getEnv();
const { manifestUri, modelBaseUri } = getModelUris(filePath, this.getDefaultModelName());
const fetchWeightsFromDisk = (filePaths: string[]) => Promise.all(filePaths.map((fp) => readFile(fp).then((buf) => buf.buffer)));
const loadWeights = tf.io.weightsLoaderFactory(fetchWeightsFromDisk);
const loadWeights = tf['io'].weightsLoaderFactory(fetchWeightsFromDisk);
const manifest = JSON.parse((await readFile(manifestUri)).toString());
const weightMap = await loadWeights(manifest, modelBaseUri);
this.loadFromWeightMap(weightMap);

View File

@ -131,14 +131,14 @@ export class NetInput {
imgTensor = padToSquare(imgTensor as tf.Tensor4D, isCenterInputs);
if (imgTensor.shape[1] !== inputSize || imgTensor.shape[2] !== inputSize) {
imgTensor = tf.image.resizeBilinear(imgTensor as tf.Tensor4D, [inputSize, inputSize], false, false);
imgTensor = tf['image'].resizeBilinear(imgTensor as tf.Tensor4D, [inputSize, inputSize], false, false);
}
return imgTensor.as3D(inputSize, inputSize, 3);
}
if (input instanceof env.getEnv().Canvas) {
return tf.browser.fromPixels(imageToSquare(input, inputSize, isCenterInputs));
return tf['browser'].fromPixels(imageToSquare(input, inputSize, isCenterInputs));
}
throw new Error(`toBatchTensor - at batchIdx ${batchIdx}, expected input to be instanceof tf.Tensor or instanceof HTMLCanvasElement, instead have ${input}`);

View File

@ -11,7 +11,7 @@ export async function imageTensorToCanvas(
const [height, width, numChannels] = imgTensor.shape.slice(isTensor4D(imgTensor) ? 1 : 0);
const imgTensor3D = tf.tidy(() => imgTensor.as3D(height, width, numChannels).toInt());
await tf.browser.toPixels(imgTensor3D, targetCanvas);
await tf['browser'].toPixels(imgTensor3D, targetCanvas);
imgTensor3D.dispose();

View File

@ -8,7 +8,8 @@ export async function loadWeightMap(
defaultModelName: string,
): Promise<tf.NamedTensorMap> {
const { manifestUri, modelBaseUri } = getModelUris(uri, defaultModelName);
// @ts-ignore
const manifest = await fetchJson<tf.io.WeightsManifestConfig>(manifestUri);
// if (manifest['weightsManifest']) manifest = manifest['weightsManifest'];
return tf.io.loadWeights(manifest, modelBaseUri);
return tf['io'].loadWeights(manifest, modelBaseUri);
}

View File

@ -1,12 +1,10 @@
export function euclideanDistance(arr1: number[] | Float32Array, arr2: number[] | Float32Array) {
if (arr1.length !== arr2.length) throw new Error('euclideanDistance: arr1.length !== arr2.length');
const desc1 = Array.from(arr1);
const desc2 = Array.from(arr2);
return Math.sqrt(
desc1
.map((val, i) => val - desc2[i])
.reduce((res, diff) => res + (diff ** 2), 0),
.reduce((res, diff) => res + (diff * diff), 0),
);
}