improve face compare performance
parent
7f613367a3
commit
fa33c1281c
|
@ -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
|
||||
|
|
|
@ -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
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
14
package.json
14
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}`);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue