added face angle calculations
parent
3d7007f13d
commit
8b6d1b76df
|
@ -38,6 +38,7 @@ Unfortunately, changes ended up being too large for a simple pull request on ori
|
||||||
- Added test/dev built-in HTTP & HTTPS Web server
|
- Added test/dev built-in HTTP & HTTPS Web server
|
||||||
- Removed `mtcnn` and `tinyYolov2` models as they were non-functional in latest public version of `Face-API`
|
- Removed `mtcnn` and `tinyYolov2` models as they were non-functional in latest public version of `Face-API`
|
||||||
*If there is a demand, I can re-implement them back.*
|
*If there is a demand, I can re-implement them back.*
|
||||||
|
- Added `face angle` calculations that returns `roll`, `yaw` and `pitch`
|
||||||
|
|
||||||
Which means valid models are **tinyFaceDetector** and **mobileNetv1**
|
Which means valid models are **tinyFaceDetector** and **mobileNetv1**
|
||||||
|
|
||||||
|
@ -388,7 +389,7 @@ npm run build
|
||||||
|
|
||||||
## Face Mesh
|
## Face Mesh
|
||||||
|
|
||||||
`FaceAPI` returns 68-point face mesh as detailed in the image below:
|
`FaceAPI` landmark model returns 68-point face mesh as detailed in the image below:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1221,7 +1221,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytes": 1643,
|
"bytes": 3192,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/classes/FaceDetection.ts",
|
"path": "src/classes/FaceDetection.ts",
|
||||||
|
@ -1292,7 +1292,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"bytes": 1878,
|
"bytes": 1914,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/xception/extractParams.ts": {
|
"src/xception/extractParams.ts": {
|
||||||
|
@ -1830,7 +1830,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||||
"bytes": 3675,
|
"bytes": 3652,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -2322,7 +2322,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytes": 4604,
|
"bytes": 4124,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/factories/WithFaceDetection.ts",
|
"path": "src/factories/WithFaceDetection.ts",
|
||||||
|
@ -2363,7 +2363,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytes": 638,
|
"bytes": 624,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
||||||
|
@ -2591,7 +2591,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 313824
|
"bytes": 315464
|
||||||
},
|
},
|
||||||
"dist/face-api.esm-nobundle.js": {
|
"dist/face-api.esm-nobundle.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -2981,7 +2981,7 @@
|
||||||
"bytesInOutput": 420
|
"bytesInOutput": 420
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 333
|
"bytesInOutput": 784
|
||||||
},
|
},
|
||||||
"src/draw/DrawFaceLandmarks.ts": {
|
"src/draw/DrawFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 1225
|
"bytesInOutput": 1225
|
||||||
|
@ -3164,7 +3164,7 @@
|
||||||
"bytesInOutput": 751
|
"bytesInOutput": 751
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytesInOutput": 1342
|
"bytesInOutput": 1334
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytesInOutput": 84
|
"bytesInOutput": 84
|
||||||
|
@ -3188,7 +3188,7 @@
|
||||||
"bytesInOutput": 443
|
"bytesInOutput": 443
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 82147
|
"bytes": 82590
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1221,7 +1221,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytes": 1643,
|
"bytes": 3192,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/classes/FaceDetection.ts",
|
"path": "src/classes/FaceDetection.ts",
|
||||||
|
@ -1830,7 +1830,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||||
"bytes": 3675,
|
"bytes": 3652,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -2322,7 +2322,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytes": 4604,
|
"bytes": 4124,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/factories/WithFaceDetection.ts",
|
"path": "src/factories/WithFaceDetection.ts",
|
||||||
|
@ -2363,7 +2363,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytes": 638,
|
"bytes": 624,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
||||||
|
@ -2591,7 +2591,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 1464559
|
"bytes": 1466199
|
||||||
},
|
},
|
||||||
"dist/face-api.esm.js": {
|
"dist/face-api.esm.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -2978,7 +2978,7 @@
|
||||||
"bytesInOutput": 422
|
"bytesInOutput": 422
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 337
|
"bytesInOutput": 790
|
||||||
},
|
},
|
||||||
"src/draw/DrawFaceLandmarks.ts": {
|
"src/draw/DrawFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 1228
|
"bytesInOutput": 1228
|
||||||
|
@ -3164,7 +3164,7 @@
|
||||||
"bytesInOutput": 1093
|
"bytesInOutput": 1093
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytesInOutput": 1348
|
"bytesInOutput": 1340
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytesInOutput": 87
|
"bytesInOutput": 87
|
||||||
|
@ -3188,7 +3188,7 @@
|
||||||
"bytesInOutput": 446
|
"bytesInOutput": 446
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 1126714
|
"bytes": 1127159
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1221,7 +1221,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytes": 1643,
|
"bytes": 3192,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/classes/FaceDetection.ts",
|
"path": "src/classes/FaceDetection.ts",
|
||||||
|
@ -1830,7 +1830,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||||
"bytes": 3675,
|
"bytes": 3652,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -2322,7 +2322,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytes": 4604,
|
"bytes": 4124,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/factories/WithFaceDetection.ts",
|
"path": "src/factories/WithFaceDetection.ts",
|
||||||
|
@ -2363,7 +2363,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytes": 638,
|
"bytes": 624,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
||||||
|
@ -2591,7 +2591,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 1464566
|
"bytes": 1466206
|
||||||
},
|
},
|
||||||
"dist/face-api.js": {
|
"dist/face-api.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -2860,7 +2860,7 @@
|
||||||
"bytesInOutput": 422
|
"bytesInOutput": 422
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 337
|
"bytesInOutput": 790
|
||||||
},
|
},
|
||||||
"src/draw/DrawFaceLandmarks.ts": {
|
"src/draw/DrawFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 1228
|
"bytesInOutput": 1228
|
||||||
|
@ -3043,7 +3043,7 @@
|
||||||
"bytesInOutput": 1093
|
"bytesInOutput": 1093
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytesInOutput": 1348
|
"bytesInOutput": 1340
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytesInOutput": 86
|
"bytesInOutput": 86
|
||||||
|
@ -3067,7 +3067,7 @@
|
||||||
"bytesInOutput": 446
|
"bytesInOutput": 446
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 1126877
|
"bytes": 1127322
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1221,7 +1221,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytes": 1643,
|
"bytes": 3192,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/classes/FaceDetection.ts",
|
"path": "src/classes/FaceDetection.ts",
|
||||||
|
@ -1292,7 +1292,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"bytes": 1878,
|
"bytes": 1914,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/xception/extractParams.ts": {
|
"src/xception/extractParams.ts": {
|
||||||
|
@ -1830,7 +1830,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||||
"bytes": 3675,
|
"bytes": 3652,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -2322,7 +2322,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytes": 4604,
|
"bytes": 4124,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/factories/WithFaceDetection.ts",
|
"path": "src/factories/WithFaceDetection.ts",
|
||||||
|
@ -2363,7 +2363,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytes": 638,
|
"bytes": 624,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
||||||
|
@ -2591,7 +2591,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 313700
|
"bytes": 315340
|
||||||
},
|
},
|
||||||
"dist/face-api.node-cpu.js": {
|
"dist/face-api.node-cpu.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -2860,7 +2860,7 @@
|
||||||
"bytesInOutput": 420
|
"bytesInOutput": 420
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 333
|
"bytesInOutput": 784
|
||||||
},
|
},
|
||||||
"src/draw/DrawFaceLandmarks.ts": {
|
"src/draw/DrawFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 1225
|
"bytesInOutput": 1225
|
||||||
|
@ -3043,7 +3043,7 @@
|
||||||
"bytesInOutput": 752
|
"bytesInOutput": 752
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytesInOutput": 1343
|
"bytesInOutput": 1335
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytesInOutput": 84
|
"bytesInOutput": 84
|
||||||
|
@ -3067,7 +3067,7 @@
|
||||||
"bytesInOutput": 443
|
"bytesInOutput": 443
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 82859
|
"bytes": 83302
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1221,7 +1221,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytes": 1643,
|
"bytes": 3192,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/classes/FaceDetection.ts",
|
"path": "src/classes/FaceDetection.ts",
|
||||||
|
@ -1292,7 +1292,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"bytes": 1878,
|
"bytes": 1914,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/xception/extractParams.ts": {
|
"src/xception/extractParams.ts": {
|
||||||
|
@ -1830,7 +1830,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||||
"bytes": 3675,
|
"bytes": 3652,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -2322,7 +2322,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytes": 4604,
|
"bytes": 4124,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/factories/WithFaceDetection.ts",
|
"path": "src/factories/WithFaceDetection.ts",
|
||||||
|
@ -2363,7 +2363,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytes": 638,
|
"bytes": 624,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
||||||
|
@ -2591,7 +2591,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 313709
|
"bytes": 315349
|
||||||
},
|
},
|
||||||
"dist/face-api.node-gpu.js": {
|
"dist/face-api.node-gpu.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -2860,7 +2860,7 @@
|
||||||
"bytesInOutput": 420
|
"bytesInOutput": 420
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 333
|
"bytesInOutput": 784
|
||||||
},
|
},
|
||||||
"src/draw/DrawFaceLandmarks.ts": {
|
"src/draw/DrawFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 1225
|
"bytesInOutput": 1225
|
||||||
|
@ -3043,7 +3043,7 @@
|
||||||
"bytesInOutput": 752
|
"bytesInOutput": 752
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytesInOutput": 1343
|
"bytesInOutput": 1335
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytesInOutput": 84
|
"bytesInOutput": 84
|
||||||
|
@ -3067,7 +3067,7 @@
|
||||||
"bytesInOutput": 443
|
"bytesInOutput": 443
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 82868
|
"bytes": 83311
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1221,7 +1221,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytes": 1643,
|
"bytes": 3192,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/classes/FaceDetection.ts",
|
"path": "src/classes/FaceDetection.ts",
|
||||||
|
@ -1292,7 +1292,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"bytes": 1878,
|
"bytes": 1914,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/xception/extractParams.ts": {
|
"src/xception/extractParams.ts": {
|
||||||
|
@ -1830,7 +1830,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||||
"bytes": 3675,
|
"bytes": 3652,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -2322,7 +2322,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytes": 4604,
|
"bytes": 4124,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/factories/WithFaceDetection.ts",
|
"path": "src/factories/WithFaceDetection.ts",
|
||||||
|
@ -2363,7 +2363,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytes": 638,
|
"bytes": 624,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
"path": "src/ssdMobilenetv1/SsdMobilenetv1Options.ts",
|
||||||
|
@ -2591,7 +2591,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 313701
|
"bytes": 315341
|
||||||
},
|
},
|
||||||
"dist/face-api.node.js": {
|
"dist/face-api.node.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -2860,7 +2860,7 @@
|
||||||
"bytesInOutput": 420
|
"bytesInOutput": 420
|
||||||
},
|
},
|
||||||
"src/factories/WithFaceLandmarks.ts": {
|
"src/factories/WithFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 333
|
"bytesInOutput": 784
|
||||||
},
|
},
|
||||||
"src/draw/DrawFaceLandmarks.ts": {
|
"src/draw/DrawFaceLandmarks.ts": {
|
||||||
"bytesInOutput": 1225
|
"bytesInOutput": 1225
|
||||||
|
@ -3043,7 +3043,7 @@
|
||||||
"bytesInOutput": 752
|
"bytesInOutput": 752
|
||||||
},
|
},
|
||||||
"src/globalApi/DetectFacesTasks.ts": {
|
"src/globalApi/DetectFacesTasks.ts": {
|
||||||
"bytesInOutput": 1343
|
"bytesInOutput": 1335
|
||||||
},
|
},
|
||||||
"src/globalApi/detectFaces.ts": {
|
"src/globalApi/detectFaces.ts": {
|
||||||
"bytesInOutput": 84
|
"bytesInOutput": 84
|
||||||
|
@ -3067,7 +3067,7 @@
|
||||||
"bytesInOutput": 443
|
"bytesInOutput": 443
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 82860
|
"bytes": 83303
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 14 KiB |
|
@ -28,7 +28,7 @@ function drawFaces(canvas, data, fps) {
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
// draw title
|
// draw title
|
||||||
ctx.font = '1.4rem sans-serif';
|
ctx.font = '1.2rem sans-serif';
|
||||||
ctx.fillStyle = 'white';
|
ctx.fillStyle = 'white';
|
||||||
ctx.fillText(`FPS: ${fps}`, 10, 25);
|
ctx.fillText(`FPS: ${fps}`, 10, 25);
|
||||||
for (const person of data) {
|
for (const person of data) {
|
||||||
|
@ -43,16 +43,18 @@ function drawFaces(canvas, data, fps) {
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
// const expression = person.expressions.sort((a, b) => Object.values(a)[0] - Object.values(b)[0]);
|
// const expression = person.expressions.sort((a, b) => Object.values(a)[0] - Object.values(b)[0]);
|
||||||
const expression = Object.entries(person.expressions).sort((a, b) => b[1] - a[1]);
|
const expression = Object.entries(person.expressions).sort((a, b) => b[1] - a[1]);
|
||||||
ctx.fillText(`gender ${Math.round(100 * person.genderProbability)}% ${person.gender}`, person.detection.box.x, person.detection.box.y - 45);
|
ctx.fillText(`gender ${Math.round(100 * person.genderProbability)}% ${person.gender}`, person.detection.box.x, person.detection.box.y - 60);
|
||||||
ctx.fillText(`expression ${Math.round(100 * expression[0][1])}% ${expression[0][0]}`, person.detection.box.x, person.detection.box.y - 25);
|
ctx.fillText(`expression ${Math.round(100 * expression[0][1])}% ${expression[0][0]}`, person.detection.box.x, person.detection.box.y - 42);
|
||||||
ctx.fillText(`age ${Math.round(person.age)} years`, person.detection.box.x, person.detection.box.y - 5);
|
ctx.fillText(`age ${Math.round(person.age)} years`, person.detection.box.x, person.detection.box.y - 24);
|
||||||
|
ctx.fillText(`roll:${Math.trunc(1000 * person.angle.roll) / 1000} pitch:${Math.trunc(1000 * person.angle.pitch) / 1000} yaw:${Math.trunc(1000 * person.angle.yaw) / 1000}`, person.detection.box.x, person.detection.box.y - 6);
|
||||||
// draw face points for each face
|
// draw face points for each face
|
||||||
ctx.fillStyle = 'lightblue';
|
ctx.fillStyle = 'lightblue';
|
||||||
ctx.globalAlpha = 0.5;
|
ctx.globalAlpha = 0.5;
|
||||||
const pointSize = 2;
|
const pointSize = 2;
|
||||||
for (const pt of person.landmarks.positions) {
|
for (let i = 0; i < person.landmarks.positions.length; i++) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(pt.x, pt.y, pointSize, 0, 2 * Math.PI);
|
ctx.arc(person.landmarks.positions[i].x, person.landmarks.positions[i].y, pointSize, 0, 2 * Math.PI);
|
||||||
|
ctx.fillText(`${i}`, person.landmarks.positions[i].x + 4, person.landmarks.positions[i].y + 4);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,7 +205,6 @@ function compile(fileNames, options) {
|
||||||
async function build(f, msg) {
|
async function build(f, msg) {
|
||||||
log.info('Build: file', msg, f, 'target:', common.target);
|
log.info('Build: file', msg, f, 'target:', common.target);
|
||||||
if (!es) es = await esbuild.startService();
|
if (!es) es = await esbuild.startService();
|
||||||
// common build options
|
|
||||||
try {
|
try {
|
||||||
// rebuild all target groups and types
|
// rebuild all target groups and types
|
||||||
for (const [targetGroupName, targetGroup] of Object.entries(targets)) {
|
for (const [targetGroupName, targetGroup] of Object.entries(targets)) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ const http2 = require('http2');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
// eslint-disable-next-line node/no-unpublished-require, import/no-extraneous-dependencies
|
// eslint-disable-next-line node/no-unpublished-require, import/no-extraneous-dependencies
|
||||||
const chokidar = require('chokidar');
|
const chokidar = require('chokidar');
|
||||||
|
// eslint-disable-next-line node/no-unpublished-require, import/no-extraneous-dependencies
|
||||||
const log = require('@vladmandic/pilogger');
|
const log = require('@vladmandic/pilogger');
|
||||||
const build = require('./build.js');
|
const build = require('./build.js');
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,36 @@ export function isWithFaceLandmarks(obj: any): obj is WithFaceLandmarks<WithFace
|
||||||
&& obj['alignedRect'] instanceof FaceDetection;
|
&& obj['alignedRect'] instanceof FaceDetection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateFaceAngle(mesh) {
|
||||||
|
const radians = (a1, a2, b1, b2) => Math.atan2(b2 - a2, b1 - a1);
|
||||||
|
|
||||||
|
const angle = { roll: <number | undefined>undefined, pitch: <number | undefined>undefined, yaw: <number | undefined>undefined };
|
||||||
|
|
||||||
|
if (!mesh || !mesh._positions || mesh._positions.length !== 68) return angle;
|
||||||
|
const pt = mesh._positions;
|
||||||
|
|
||||||
|
// roll is face lean left/right
|
||||||
|
// comparing x,y of outside corners of leftEye and rightEye
|
||||||
|
angle.roll = radians(pt[36]._x, pt[36]._y, pt[45]._x, pt[45]._y);
|
||||||
|
|
||||||
|
// yaw is face turn left/right
|
||||||
|
// comparing x distance of bottom of nose to left and right edge of face
|
||||||
|
// and y distance of top of nose to left and right edge of face
|
||||||
|
// precision is lacking since coordinates are not precise enough
|
||||||
|
angle.pitch = radians(pt[30]._x - pt[0]._x, pt[27]._y - pt[0]._y, pt[16]._x - pt[30]._x, pt[27]._y - pt[16]._y);
|
||||||
|
|
||||||
|
// pitch is face move up/down
|
||||||
|
// comparing size of the box around the face with top and bottom of detected landmarks
|
||||||
|
// silly hack, but this gives us face compression on y-axis
|
||||||
|
// e.g., tilting head up hides the forehead that doesn't have any landmarks so ratio drops
|
||||||
|
// value is normalized to range, but is not in actual radians
|
||||||
|
const bottom = pt.reduce((prev, cur) => (prev < cur._y ? prev : cur._y), +Infinity);
|
||||||
|
const top = pt.reduce((prev, cur) => (prev > cur._y ? prev : cur._y), -Infinity);
|
||||||
|
angle.yaw = 10 * (mesh._imgDims._height / (top - bottom) / 1.45 - 1);
|
||||||
|
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
export function extendWithFaceLandmarks<
|
export function extendWithFaceLandmarks<
|
||||||
TSource extends WithFaceDetection<{}>,
|
TSource extends WithFaceDetection<{}>,
|
||||||
TFaceLandmarks extends FaceLandmarks = FaceLandmarks68 >(sourceObj: TSource, unshiftedLandmarks: TFaceLandmarks): WithFaceLandmarks<TSource, TFaceLandmarks> {
|
TFaceLandmarks extends FaceLandmarks = FaceLandmarks68 >(sourceObj: TSource, unshiftedLandmarks: TFaceLandmarks): WithFaceLandmarks<TSource, TFaceLandmarks> {
|
||||||
|
@ -30,11 +60,13 @@ export function extendWithFaceLandmarks<
|
||||||
const rect = landmarks.align();
|
const rect = landmarks.align();
|
||||||
const { imageDims } = sourceObj.detection;
|
const { imageDims } = sourceObj.detection;
|
||||||
const alignedRect = new FaceDetection(sourceObj.detection.score, rect.rescale(imageDims.reverse()), imageDims);
|
const alignedRect = new FaceDetection(sourceObj.detection.score, rect.rescale(imageDims.reverse()), imageDims);
|
||||||
|
const angle = calculateFaceAngle(unshiftedLandmarks);
|
||||||
|
|
||||||
const extension = {
|
const extension = {
|
||||||
landmarks,
|
landmarks,
|
||||||
unshiftedLandmarks,
|
unshiftedLandmarks,
|
||||||
alignedRect,
|
alignedRect,
|
||||||
|
angle,
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...sourceObj, ...extension };
|
return { ...sourceObj, ...extension };
|
||||||
|
|
|
@ -27,28 +27,13 @@ export class DetectAllFacesTask extends DetectFacesTaskBase<FaceDetection[]> {
|
||||||
public async run(): Promise<FaceDetection[]> {
|
public async run(): Promise<FaceDetection[]> {
|
||||||
const { input, options } = this;
|
const { input, options } = this;
|
||||||
|
|
||||||
// eslint-disable-next-line no-nested-ternary
|
let result;
|
||||||
const faceDetectionFunction = options instanceof TinyFaceDetectorOptions
|
if (options instanceof TinyFaceDetectorOptions) result = nets.tinyFaceDetector.locateFaces(input, options);
|
||||||
// eslint-disable-next-line no-shadow
|
else if (options instanceof SsdMobilenetv1Options) result = nets.ssdMobilenetv1.locateFaces(input, options);
|
||||||
? (input: TNetInput) => nets.tinyFaceDetector.locateFaces(input, options)
|
else if (options instanceof TinyYolov2Options) result = nets.tinyYolov2.locateFaces(input, options);
|
||||||
: (
|
else throw new Error('detectFaces - expected options to be instance of TinyFaceDetectorOptions | SsdMobilenetv1Options | TinyYolov2Options');
|
||||||
// eslint-disable-next-line no-nested-ternary
|
|
||||||
options instanceof SsdMobilenetv1Options
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
? (input: TNetInput) => nets.ssdMobilenetv1.locateFaces(input, options)
|
|
||||||
: (
|
|
||||||
options instanceof TinyYolov2Options
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
? (input: TNetInput) => nets.tinyYolov2.locateFaces(input, options)
|
|
||||||
: null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!faceDetectionFunction) {
|
return result;
|
||||||
throw new Error('detectFaces - expected options to be instance of TinyFaceDetectorOptions | SsdMobilenetv1Options | MtcnnOptions | TinyYolov2Options');
|
|
||||||
}
|
|
||||||
|
|
||||||
return faceDetectionFunction(input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private runAndExtendWithFaceDetections(): Promise<WithFaceDetection<{}>[]> {
|
private runAndExtendWithFaceDetections(): Promise<WithFaceDetection<{}>[]> {
|
||||||
|
@ -87,9 +72,7 @@ export class DetectSingleFaceTask extends DetectFacesTaskBase<FaceDetection | un
|
||||||
const faceDetections = await new DetectAllFacesTask(this.input, this.options);
|
const faceDetections = await new DetectAllFacesTask(this.input, this.options);
|
||||||
let faceDetectionWithHighestScore = faceDetections[0];
|
let faceDetectionWithHighestScore = faceDetections[0];
|
||||||
faceDetections.forEach((faceDetection) => {
|
faceDetections.forEach((faceDetection) => {
|
||||||
if (faceDetection.score > faceDetectionWithHighestScore.score) {
|
if (faceDetection.score > faceDetectionWithHighestScore.score) faceDetectionWithHighestScore = faceDetection;
|
||||||
faceDetectionWithHighestScore = faceDetection;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return faceDetectionWithHighestScore;
|
return faceDetectionWithHighestScore;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,10 @@ import { SsdMobilenetv1Options } from '../ssdMobilenetv1/SsdMobilenetv1Options';
|
||||||
import { DetectAllFacesTask, DetectSingleFaceTask } from './DetectFacesTasks';
|
import { DetectAllFacesTask, DetectSingleFaceTask } from './DetectFacesTasks';
|
||||||
import { FaceDetectionOptions } from './types';
|
import { FaceDetectionOptions } from './types';
|
||||||
|
|
||||||
export function detectSingleFace(
|
export function detectSingleFace(input: TNetInput, options: FaceDetectionOptions = new SsdMobilenetv1Options()): DetectSingleFaceTask {
|
||||||
input: TNetInput,
|
|
||||||
options: FaceDetectionOptions = new SsdMobilenetv1Options(),
|
|
||||||
): DetectSingleFaceTask {
|
|
||||||
return new DetectSingleFaceTask(input, options);
|
return new DetectSingleFaceTask(input, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function detectAllFaces(
|
export function detectAllFaces(input: TNetInput, options: FaceDetectionOptions = new SsdMobilenetv1Options()): DetectAllFacesTask {
|
||||||
input: TNetInput,
|
|
||||||
options: FaceDetectionOptions = new SsdMobilenetv1Options(),
|
|
||||||
): DetectAllFacesTask {
|
|
||||||
return new DetectAllFacesTask(input, options);
|
return new DetectAllFacesTask(input, options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,7 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
|
||||||
const x = tf.sub(tf.mul(batchTensor, tf.scalar(0.007843137718737125)), tf.scalar(1)) as tf.Tensor4D;
|
const x = tf.sub(tf.mul(batchTensor, tf.scalar(0.007843137718737125)), tf.scalar(1)) as tf.Tensor4D;
|
||||||
const features = mobileNetV1(x, params.mobilenetv1);
|
const features = mobileNetV1(x, params.mobilenetv1);
|
||||||
|
|
||||||
const {
|
const { boxPredictions, classPredictions } = predictionLayer(features.out, features.conv11, params.prediction_layer);
|
||||||
boxPredictions,
|
|
||||||
classPredictions,
|
|
||||||
} = predictionLayer(features.out, features.conv11, params.prediction_layer);
|
|
||||||
|
|
||||||
return outputLayer(boxPredictions, classPredictions, params.output_layer);
|
return outputLayer(boxPredictions, classPredictions, params.output_layer);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue