in blazeface/src/face.ts [347:436]
async estimateFaces(
input: tf.Tensor3D|ImageData|HTMLVideoElement|HTMLImageElement|
HTMLCanvasElement,
returnTensors = false, flipHorizontal = false,
annotateBoxes = true): Promise<NormalizedFace[]> {
const [, width] = getInputTensorDimensions(input);
const image = tf.tidy(() => {
if (!(input instanceof tf.Tensor)) {
input = tf.browser.fromPixels(input);
}
return tf.expandDims(tf.cast((input as tf.Tensor), 'float32'), 0);
});
const {boxes, scaleFactor} = await this.getBoundingBoxes(
image as tf.Tensor4D, returnTensors, annotateBoxes);
image.dispose();
if (returnTensors) {
return boxes.map((face: BlazeFacePrediction|Box) => {
const scaledBox =
scaleBoxFromPrediction(face, scaleFactor as tf.Tensor1D);
let normalizedFace: NormalizedFace = {
topLeft: tf.slice(scaledBox, [0], [2]) as tf.Tensor1D,
bottomRight: tf.slice(scaledBox, [2], [2]) as tf.Tensor1D
};
if (annotateBoxes) {
const {landmarks, probability, anchor} = face as {
landmarks: tf.Tensor2D,
probability: tf.Tensor1D,
anchor: tf.Tensor2D | [number, number]
};
const normalizedLandmarks: tf.Tensor2D =
tf.mul(tf.add(landmarks, anchor), scaleFactor);
normalizedFace.landmarks = normalizedLandmarks;
normalizedFace.probability = probability;
}
if (flipHorizontal) {
normalizedFace = flipFaceHorizontal(normalizedFace, width);
}
return normalizedFace;
});
}
return Promise.all(boxes.map(async (face: BlazeFacePrediction) => {
const scaledBox =
scaleBoxFromPrediction(face, scaleFactor as [number, number]);
let normalizedFace: NormalizedFace;
if (!annotateBoxes) {
const boxData = await scaledBox.array();
normalizedFace = {
topLeft: (boxData as number[]).slice(0, 2) as [number, number],
bottomRight: (boxData as number[]).slice(2) as [number, number]
};
} else {
const [landmarkData, boxData, probabilityData] =
await Promise.all([face.landmarks, scaledBox, face.probability].map(
async d => d.array()));
const anchor = face.anchor as [number, number];
const [scaleFactorX, scaleFactorY] = scaleFactor as [number, number];
const scaledLandmarks =
(landmarkData as Array<[number, number]>)
.map(landmark => ([
(landmark[0] + anchor[0]) * scaleFactorX,
(landmark[1] + anchor[1]) * scaleFactorY
]));
normalizedFace = {
topLeft: (boxData as number[]).slice(0, 2) as [number, number],
bottomRight: (boxData as number[]).slice(2) as [number, number],
landmarks: scaledLandmarks,
probability: probabilityData as number
};
disposeBox(face.box);
face.landmarks.dispose();
face.probability.dispose();
}
scaledBox.dispose();
if (flipHorizontal) {
normalizedFace = flipFaceHorizontal(normalizedFace, width);
}
return normalizedFace;
}));
}