face feature position - face bounding box

This commit is contained in:
Michal Kolář
2023-04-25 11:50:42 +02:00
committed by Stanislav Fifik
parent 2991b627ef
commit 3d2e2db928

View File

@@ -1,22 +1,22 @@
import { import {
ImageVar, ImageVar,
NumberVar NumberVar, VariableValueType
} from '../io/AbstractIOSet'; } from '../io/AbstractIOSet';
import Node from "../Node"; import Node from "../Node";
import '@tensorflow/tfjs-backend-webgl'; import '@tensorflow/tfjs-backend-webgl';
import { import {Face,
Face,
FaceDetector, FaceDetector,
FaceDetectorInput, FaceDetectorInput,
MediaPipeFaceDetectorModelConfig,
SupportedModels, SupportedModels,
createDetector, createDetector,
MediaPipeFaceDetectorTfjsModelConfig
} from '@tensorflow-models/face-detection'; } from '@tensorflow-models/face-detection';
import {canvasPool2D} from "../../canvas/CanvasPool"; import {canvasPool2D} from "../../canvas/CanvasPool";
import {waitForMedia} from "../../utils"; import {waitForMedia} from "../../utils";
import { mediaSize } from '../canvas'; import { mediaSize } from '../canvas';
import {first} from 'lodash/array'; import {first} from 'lodash/array';
import keyBy from 'lodash/keyBy'; import keyBy from 'lodash/keyBy';
import {OutputProperties} from "graphfx/src/nodes/io/AbstractIOSet";
const inputs = { const inputs = {
image: { image: {
@@ -28,6 +28,18 @@ const outputs = {
image: { image: {
type: 'Image' type: 'Image'
} as ImageVar, } as ImageVar,
faceX: {
type: 'Number',
} as NumberVar,
faceY: {
type: 'Number',
} as NumberVar,
faceWidth: {
type: 'Number',
} as NumberVar,
faceHeight: {
type: 'Number',
} as NumberVar,
rightEyeX: { rightEyeX: {
type: 'Number', type: 'Number',
} as NumberVar, } as NumberVar,
@@ -64,7 +76,10 @@ const outputs = {
leftEarTragionY: { leftEarTragionY: {
type: 'Number', type: 'Number',
} as NumberVar, } as NumberVar,
} };
type Outputs = typeof outputs;
type UpdateOutputNodesPayload = Partial<{[key in keyof OutputProperties<Outputs>]: VariableValueType<Outputs[key]>}>;
export default class FaceFeaturePosition extends Node<typeof inputs, typeof outputs> { export default class FaceFeaturePosition extends Node<typeof inputs, typeof outputs> {
private detector: FaceDetector private detector: FaceDetector
@@ -82,7 +97,7 @@ export default class FaceFeaturePosition extends Node<typeof inputs, typeof outp
maxFaces: 1, maxFaces: 1,
modelType: 'short', modelType: 'short',
runtime: 'tfjs', runtime: 'tfjs',
} as MediaPipeFaceDetectorTfjsModelConfig; } as MediaPipeFaceDetectorModelConfig;
this.detector = await createDetector(model, detectorConfig); this.detector = await createDetector(model, detectorConfig);
} }
@@ -104,30 +119,59 @@ export default class FaceFeaturePosition extends Node<typeof inputs, typeof outp
const detections = await this.detector.estimateFaces(this.in.image.value as FaceDetectorInput, {flipHorizontal: false}); const detections = await this.detector.estimateFaces(this.in.image.value as FaceDetectorInput, {flipHorizontal: false});
if (!detections.length) { if (detections.length) {
this.out.image.value = this.in.image.value; const detection: Face = first(detections);
return; const keyPoints = keyBy(detection.keypoints, (keyPoint) => keyPoint.name)
this.updateOutputNodes({
faceX: detection.box.xMin,
faceY: detection.box.yMin,
faceWidth: detection.box.width,
faceHeight: detection.box.height,
rightEyeX: keyPoints['rightEye'].x,
rightEyeY: keyPoints['rightEye'].y,
leftEyeX: keyPoints['leftEye'].x,
leftEyeY: keyPoints['leftEye'].y,
noseTipX: keyPoints['noseTip'].x,
noseTipY: keyPoints['noseTip'].y,
mouthCenterX: keyPoints['mouthCenter'].x,
mouthCenterY: keyPoints['mouthCenter'].y,
rightEarTragionX: keyPoints['rightEarTragion'].x,
rightEarTragionY: keyPoints['rightEarTragion'].y,
leftEarTragionX: keyPoints['leftEarTragion'].x,
leftEarTragionY: keyPoints['leftEarTragion'].y,
image: this.in.image.value,
})
} else {
this.updateOutputNodes({
faceX: undefined,
faceY: undefined,
faceWidth: undefined,
faceHeight: undefined,
rightEyeX: undefined,
rightEyeY: undefined,
leftEyeX: undefined,
leftEyeY: undefined,
noseTipX: undefined,
noseTipY: undefined,
mouthCenterX: undefined,
mouthCenterY: undefined,
rightEarTragionX: undefined,
rightEarTragionY: undefined,
leftEarTragionX: undefined,
leftEarTragionY: undefined,
image: this.in.image.value,
});
} }
}
const detection: Face = first(detections); updateOutputNodes(payload: UpdateOutputNodesPayload) {
const keyPoints = keyBy(detection.keypoints, (keyPoint) => keyPoint.name) for(let key of Object.keys(payload)) {
this.out[key].__value = payload[key];
this.out.image.value = this.in.image.value; this.out[key].__notifyListeners();
this.out.rightEyeX.value = keyPoints['rightEye'].x; }
this.out.rightEyeY.value = keyPoints['rightEye'].y;
this.out.leftEyeX.value = keyPoints['leftEye'].x;
this.out.leftEyeY.value = keyPoints['leftEye'].y;
this.out.noseTipX.value = keyPoints['noseTip'].x;
this.out.noseTipY.value = keyPoints['noseTip'].y;
this.out.mouthCenterX.value = keyPoints['mouthCenter'].x;
this.out.mouthCenterY.value = keyPoints['mouthCenter'].y;
this.out.rightEarTragionX.value = keyPoints['rightEarTragion'].x;
this.out.rightEarTragionY.value = keyPoints['rightEarTragion'].y;
this.out.leftEarTragionX.value = keyPoints['leftEarTragion'].x;
this.out.leftEarTragionY.value = keyPoints['leftEarTragion'].y;
} }
async _update(){ async _update(){
await this.detectFeatures(); await this.detectFeatures();
} }
} }