new module: face description

master
Vladimir Mandic 2021-03-21 14:18:59 -04:00
parent f18a5e77f9
commit ad7e00dab1
6 changed files with 52 additions and 44 deletions

@ -25,7 +25,7 @@ With **config.face** having several subsections:
- **config.face.age**: controls age prediction - **config.face.age**: controls age prediction
- **config.face.gender**: controls gender prediction - **config.face.gender**: controls gender prediction
- **config.face.emotion**: controls emotion prediction - **config.face.emotion**: controls emotion prediction
- **config.face.embedding**: controls generation of face embedding data used for face simmilarity checks - **config.face.embedding**: controls generation of face embedding data used for face similarity checks
<br> <br>
@ -36,7 +36,7 @@ Note that user object and default configuration are merged using deep-merge, so
All configuration details can be changed in real-time! All configuration details can be changed in real-time!
```js ```js
config = { const config: Config = {
backend: 'webgl', // select tfjs backend to use backend: 'webgl', // select tfjs backend to use
// can be 'webgl', 'wasm', 'cpu', or 'humangl' which is a custom version of webgl // can be 'webgl', 'wasm', 'cpu', or 'humangl' which is a custom version of webgl
// leave as empty string to continue using default backend // leave as empty string to continue using default backend
@ -133,21 +133,14 @@ config = {
modelPath: '../models/iris.json', modelPath: '../models/iris.json',
}, },
age: { description: {
enabled: true, enabled: true, // to improve accuracy of face embedding extraction it is
modelPath: '../models/age.json', // recommended to enable detector.rotation and mesh.enabled
modelPath: '../models/faceres.json',
skipFrames: 31, // how many frames to go without re-running the detector skipFrames: 31, // how many frames to go without re-running the detector
// only used for video inputs // only used for video inputs
}, },
gender: {
enabled: true,
minConfidence: 0.1, // threshold for discarding a prediction
modelPath: '../models/gender.json',
skipFrames: 32, // how many frames to go without re-running the detector
// only used for video inputs
},
emotion: { emotion: {
enabled: true, enabled: true,
minConfidence: 0.1, // threshold for discarding a prediction minConfidence: 0.1, // threshold for discarding a prediction
@ -155,9 +148,23 @@ config = {
modelPath: '../models/emotion.json', modelPath: '../models/emotion.json',
}, },
age: {
enabled: false, // obsolete, replaced by description module
modelPath: '../models/age.json',
skipFrames: 31, // how many frames to go without re-running the detector
// only used for video inputs
},
gender: {
enabled: false, // obsolete, replaced by description module
minConfidence: 0.1, // threshold for discarding a prediction
modelPath: '../models/gender.json',
skipFrames: 32, // how many frames to go without re-running the detector
// only used for video inputs
},
embedding: { embedding: {
enabled: false, // to improve accuracy of face embedding extraction it is enabled: false, // obsolete, replaced by description module
// highly recommended to enable detector.rotation and mesh.enabled
modelPath: '../models/mobileface.json', modelPath: '../models/mobileface.json',
}, },
}, },

@ -55,7 +55,7 @@ It highlights functionality such as:
- Loading images - Loading images
- Extracting faces from images - Extracting faces from images
- Calculating face embedding descriptors - Calculating face embedding descriptors
- Finding face simmilarity and sorting them by simmilarity - Finding face similarity and sorting them by similarity
- Finding best face match based on a known list of faces and printing matches - Finding best face match based on a known list of faces and printing matches
<br><hr><br> <br><hr><br>

@ -1,16 +1,14 @@
# Face Feature Embedding and Simmilarity Compare # Face Feature Embedding and Similarity Compare
<br> <br>
## Demo
To see a demo of all all face embedding features, see `/demo/embedding.js` To see a demo of all all face embedding features, see `/demo/embedding.js`
It highlights functionality such as: It highlights functionality such as:
- Loading images - Loading images
- Extracting faces from images - Extracting faces from images
- Calculating face embedding descriptors - Calculating face embedding descriptors
- Finding face simmilarity and sorting them by simmilarity - Finding face similarity and sorting them by similarity
- Finding best face match based on a known list of faces and printing matches - Finding best face match based on a known list of faces and printing matches
<br> <br>
@ -23,7 +21,7 @@ and calculate embedding vectors for both first and second image you want to comp
To achieve quality results, it is also highly recommended to have `face.mesh` and `face.detection.rotation` To achieve quality results, it is also highly recommended to have `face.mesh` and `face.detection.rotation`
enabled as calculating feature vectors on non-quality inputs can lead to false results enabled as calculating feature vectors on non-quality inputs can lead to false results
Simmilarity match above 75% is considered good match while simmilarity match above 60% is considered best-guess Similarity match above 65% is considered good match while similarity match above 55% is considered best-guess
For example, For example,
@ -33,7 +31,8 @@ const myConfig = {
enabled: true, enabled: true,
detector: { rotation: true, return: true }, detector: { rotation: true, return: true },
mesh: { enabled: true }, mesh: { enabled: true },
embedding: { enabled: true }, description: { enabled: true },
// embedding: { enabled: true }, // alternative you can use embedding module instead of description
}, },
}; };
@ -42,9 +41,9 @@ const human = new Human(myConfig);
const firstResult = await human.detect(firstImage); const firstResult = await human.detect(firstImage);
const secondResult = await human.detect(secondImage); const secondResult = await human.detect(secondImage);
const simmilarity = human.simmilarity(firstResult.face[0].embedding, secondResult.face[0].embedding); const similarity = human.similarity(firstResult.face[0].embedding, secondResult.face[0].embedding);
console.log(`faces are ${100 * simmilarity}% simmilar`); console.log(`faces are ${100 * similarity}% simmilar`);
``` ```
If the image or video frame have multiple faces and you want to match all of them, simply loop through all `results.face` If the image or video frame have multiple faces and you want to match all of them, simply loop through all `results.face`
@ -52,8 +51,8 @@ If the image or video frame have multiple faces and you want to match all of the
```js ```js
for (let i = 0; i < secondResult.face.length; i++) { for (let i = 0; i < secondResult.face.length; i++) {
const secondEmbedding = secondResult.face[i].embedding; const secondEmbedding = secondResult.face[i].embedding;
const simmilarity = human.simmilarity(firstEmbedding, secondEmbedding); const similarity = human.similarity(firstEmbedding, secondEmbedding);
console.log(`face ${i} is ${100 * simmilarity}% simmilar`); console.log(`face ${i} is ${100 * similarity}% simmilar`);
} }
``` ```
@ -76,28 +75,28 @@ They can be stored as normal arrays and reused as needed
<br> <br>
## Face Simmilarity ## Face Similarity
Simmilarity function is based on general *Minkowski distance* between all points in vector Similarity function is based on general *Minkowski distance* between all points in vector
*[Minkowski distance](https://en.wikipedia.org/wiki/Minkowski_distance) is a nth root of sum of nth powers of distances between each point (each value in 192-member array)* *[Minkowski distance](https://en.wikipedia.org/wiki/Minkowski_distance) is a nth root of sum of nth powers of distances between each point (each value in 192-member array)*
*Default is Eucliean distance which is a limited case of Minkowski distance with order of 2* *Default is Eucliean distance which is a limited case of Minkowski distance with order of 2*
Changing `order` can make simmilarity matching more or less sensitive (default order is 2nd order) Changing `order` can make similarity matching more or less sensitive (default order is 2nd order)
For example, those will produce slighly different results: For example, those will produce slighly different results:
```js ```js
const simmilarity2ndOrder = human.simmilarity(firstEmbedding, secondEmbedding, 2); const similarity2ndOrder = human.similarity(firstEmbedding, secondEmbedding, 2);
const simmilarity3rdOrder = human.simmilarity(firstEmbedding, secondEmbedding, 3); const similarity3rdOrder = human.similarity(firstEmbedding, secondEmbedding, 3);
``` ```
How simmilarity is calculated: How similarity is calculated:
```js ```js
const distance = ((firstEmbedding.map((val, i) => (val - secondEmbedding[i])).reduce((dist, diff) => dist + (diff ** order), 0) ** (1 / order))); const distance = ((firstEmbedding.map((val, i) => (val - secondEmbedding[i])).reduce((dist, diff) => dist + (diff ** order), 0) ** (1 / order)));
``` ```
*Once embedding values are calculated and stored, if you want to use stored embedding values without requiring `Human` library you can use above formula to calculate simmilarity on the fly* *Once embedding values are calculated and stored, if you want to use stored embedding values without requiring `Human` library you can use above formula to calculate similarity on the fly*
<br> <br>
@ -125,12 +124,12 @@ Last parameter is optional and notes a minimal threshold for a match
```js ```js
const best = human.match(current.embedding, db, 0) const best = human.match(current.embedding, db, 0)
// return is object: { name: 'person a', simmilarity '0.99', source 'some-image-file' } // return is object: { name: 'person a', similarity '0.99', source 'some-image-file' }
``` ```
Database can be further stored in a JS or JSON file and retrieved when needed to have Database can be further stored in a JS or JSON file and retrieved when needed to have
a permanent database of faces that can be expanded over time to cover any number of known faces a permanent database of faces that can be expanded over time to cover any number of known faces
For example, see `/demo/embedding.js`: For example, see `/demo/embedding.js` and example database `/demo/faces.json`:
```js ```js
// download db with known faces // download db with known faces
@ -158,6 +157,6 @@ To achieve optimal result, `Human` performs following operations on an image bef
`Human` contains a demo that enumerates number of images, `Human` contains a demo that enumerates number of images,
extracts all faces from them, processed them and then allows extracts all faces from them, processed them and then allows
for a selection of any face which sorts faces by simmilarity for a selection of any face which sorts faces by similarity
Demo is available in `demo/embedding.html` which uses `demo/embedding.js` as JavaSript module Demo is available in `demo/embedding.html` which uses `demo/embedding.js` as JavaSript module

@ -7,11 +7,9 @@ Default models in Human library are:
- **Face Detection**: MediaPipe BlazeFace-Back - **Face Detection**: MediaPipe BlazeFace-Back
- **Face Mesh**: MediaPipe FaceMesh - **Face Mesh**: MediaPipe FaceMesh
- **Face Iris Analysis**: MediaPipe Iris - **Face Iris Analysis**: MediaPipe Iris
- **Face Description**: HSE FaceRes
- **Emotion Detection**: Oarriaga Emotion - **Emotion Detection**: Oarriaga Emotion
- **Gender Detection**: Oarriaga Gender
- **Age Detection**: SSR-Net Age IMDB
- **Body Analysis**: PoseNet - **Body Analysis**: PoseNet
- **Face Embedding**: BecauseofAI MobileFace Embedding
- **Object Detection**: NanoDet - **Object Detection**: NanoDet
## Notes ## Notes
@ -24,9 +22,11 @@ Default models in Human library are:
But if conditions are met, it returns far more details (39 vs 17 keypoints) and is far more accurate But if conditions are met, it returns far more details (39 vs 17 keypoints) and is far more accurate
Furthermore, it returns 3D approximation of each point instead of 2D Furthermore, it returns 3D approximation of each point instead of 2D
<br> **Face description** can be switched from default combined model `FaceRes` to individual models
**Gender detection** can be switched from the default model to `SSR-Net` trained on IMDB dataset - `Gender Detection`: Oarriaga Gender
- `Age Detection`: SSR-Net Age IMDB
- `Face Embedding`: BecauseofAI MobileFace Embedding
<br><hr><br> <br><hr><br>
@ -52,6 +52,7 @@ Default models in Human library are:
| BecauseofAI MobileFace | 33K | mobileface.json | 2.1M | mobileface.bin | 75 | | BecauseofAI MobileFace | 33K | mobileface.json | 2.1M | mobileface.bin | 75 |
| FaceBoxes | 212K | faceboxes.json | 2.0M | faceboxes.bin | N/A | | FaceBoxes | 212K | faceboxes.json | 2.0M | faceboxes.bin | N/A |
| NanoDet | 255K | nanodet.json | 1.9M | nanodet.bin | 524 | | NanoDet | 255K | nanodet.json | 1.9M | nanodet.bin | 524 |
| FaceRes | 70K | faceres.json | 6.7M | faceres.bin | 524 |
<br> <br>
@ -64,6 +65,7 @@ Default models in Human library are:
- Face Detection: [**MediaPipe BlazeFace**](https://drive.google.com/file/d/1f39lSzU5Oq-j_OXgS67KfN5wNsoeAZ4V/view) - Face Detection: [**MediaPipe BlazeFace**](https://drive.google.com/file/d/1f39lSzU5Oq-j_OXgS67KfN5wNsoeAZ4V/view)
- Facial Spacial Geometry: [**MediaPipe FaceMesh**](https://drive.google.com/file/d/1VFC_wIpw4O7xBOiTgUldl79d9LA-LsnA/view) - Facial Spacial Geometry: [**MediaPipe FaceMesh**](https://drive.google.com/file/d/1VFC_wIpw4O7xBOiTgUldl79d9LA-LsnA/view)
- Eye Iris Details: [**MediaPipe Iris**](https://drive.google.com/file/d/1bsWbokp9AklH2ANjCfmjqEzzxO1CNbMu/view) - Eye Iris Details: [**MediaPipe Iris**](https://drive.google.com/file/d/1bsWbokp9AklH2ANjCfmjqEzzxO1CNbMu/view)
- Face Description: [**HSE-FaceRes**](https://github.com/HSE-asavchenko/HSE_FaceRec_tf)
- Hand Detection & Skeleton: [**MediaPipe HandPose**](https://drive.google.com/file/d/1sv4sSb9BSNVZhLzxXJ0jBv9DqD-4jnAz/view) - Hand Detection & Skeleton: [**MediaPipe HandPose**](https://drive.google.com/file/d/1sv4sSb9BSNVZhLzxXJ0jBv9DqD-4jnAz/view)
- Body Pose Detection: [**BlazePose**](https://drive.google.com/file/d/10IU-DRP2ioSNjKFdiGbmmQX81xAYj88s/view) - Body Pose Detection: [**BlazePose**](https://drive.google.com/file/d/10IU-DRP2ioSNjKFdiGbmmQX81xAYj88s/view)
- Body Pose Detection: [**PoseNet**](https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5) - Body Pose Detection: [**PoseNet**](https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5)

@ -21,7 +21,7 @@ result = {
iris, // <number> relative distance of iris to camera, multiple by focal lenght to get actual distance iris, // <number> relative distance of iris to camera, multiple by focal lenght to get actual distance
age, // <number> estimated age age, // <number> estimated age
gender, // <string> 'male', 'female' gender, // <string> 'male', 'female'
embedding, // <array>[float] vector of 192 values used for face simmilarity compare embedding, // <array>[float] vector of 192 values used for face similarity compare
angle: // 3d face rotation values in radians in range of -pi/2 to pi/2 which is -90 to +90 degrees angle: // 3d face rotation values in radians in range of -pi/2 to pi/2 which is -90 to +90 degrees
{ {
roll, // roll is face lean left/right, value of 0 means center roll, // roll is face lean left/right, value of 0 means center

@ -39,7 +39,7 @@ Additionally, `Human` library exposes several objects and methods:
human.image(image, config?) // runs image processing without detection and returns canvas human.image(image, config?) // runs image processing without detection and returns canvas
human.warmup(config, image? // warms up human library for faster initial execution after loading human.warmup(config, image? // warms up human library for faster initial execution after loading
// if image is not provided, it will generate internal sample // if image is not provided, it will generate internal sample
human.simmilarity(embedding1, embedding2) // runs simmilarity calculation between two provided embedding vectors human.similarity(embedding1, embedding2) // runs similarity calculation between two provided embedding vectors
// vectors for source and target must be previously detected using // vectors for source and target must be previously detected using
// face.embedding module // face.embedding module
human.enhance(face) // returns enhanced tensor of a previously detected face that can be used for visualizations human.enhance(face) // returns enhanced tensor of a previously detected face that can be used for visualizations
@ -50,7 +50,7 @@ For details, see [embedding documentation](https://github.com/vladmandic/human/w
```js ```js
human.simmilarity(embedding1, embedding2) // runs simmilarity calculation between two provided embedding vectors human.similarity(embedding1, embedding2) // runs similarity calculation between two provided embedding vectors
// vectors for source and target must be previously detected using // vectors for source and target must be previously detected using
// face.embedding module // face.embedding module
human.match(embedding, db, threshold) // finds best match for current face in a provided list of faces human.match(embedding, db, threshold) // finds best match for current face in a provided list of faces