mirror of https://github.com/vladmandic/human
new module: face description
parent
f18a5e77f9
commit
ad7e00dab1
|
@ -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',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
2
Demos.md
2
Demos.md
|
@ -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>
|
||||||
|
|
39
Embedding.md
39
Embedding.md
|
@ -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
|
||||||
|
|
12
Models.md
12
Models.md
|
@ -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
|
||||||
|
|
4
Usage.md
4
Usage.md
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue