face feature position - face bounding box
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user