Skip to content

Commit 0c75bc3

Browse files
refactor FaceDetection result
1 parent 1cba5ad commit 0c75bc3

File tree

9 files changed

+102
-75
lines changed

9 files changed

+102
-75
lines changed

examples/views/detectAndRecognizeFaces.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
descriptors.forEach((descriptor, i) => {
109109
const bestMatch = getBestMatch(trainDescriptorsByClass, descriptor)
110110
const text = `${bestMatch.distance < maxDistance ? bestMatch.className : 'unkown'} (${bestMatch.distance})`
111-
const { x, y, height: boxHeight } = detectionsForSize[i].box
111+
const { x, y, height: boxHeight } = detectionsForSize[i].getBox()
112112
faceapi.drawText(
113113
canvas.getContext('2d'),
114114
x,

src/Rect.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export class Rect {
2+
public x: number
3+
public y: number
4+
public width: number
5+
public height: number
6+
7+
constructor(x: number, y: number, width: number, height: number) {
8+
this.x = x
9+
this.y = y
10+
this.width = width
11+
this.height = height
12+
}
13+
}

src/extractFaceTensors.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as tf from '@tensorflow/tfjs-core';
22

3-
import { FaceDetectionResult } from './faceDetectionNet/FaceDetectionResult';
4-
import { NetInput } from './NetInput';
3+
import { FaceDetection } from './faceDetectionNet/FaceDetection';
54
import { getImageTensor } from './getImageTensor';
5+
import { NetInput } from './NetInput';
66
import { TNetInput } from './types';
77

88
/**
@@ -18,7 +18,7 @@ import { TNetInput } from './types';
1818
*/
1919
export function extractFaceTensors(
2020
image: tf.Tensor | NetInput | TNetInput,
21-
detections: FaceDetectionResult[]
21+
detections: FaceDetection[]
2222
): tf.Tensor4D[] {
2323
return tf.tidy(() => {
2424
const imgTensor = getImageTensor(image)
@@ -27,7 +27,7 @@ export function extractFaceTensors(
2727
const [batchSize, imgHeight, imgWidth, numChannels] = imgTensor.shape
2828

2929
const faceTensors = detections.map(det => {
30-
const { x, y, width, height } = det.forSize(imgWidth, imgHeight).box
30+
const { x, y, width, height } = det.forSize(imgWidth, imgHeight).getBox()
3131
return tf.slice(imgTensor, [0, y, x, 0], [1, height, width, numChannels])
3232
})
3333

src/extractFaces.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FaceDetectionResult } from './faceDetectionNet/FaceDetectionResult';
1+
import { FaceDetection } from './faceDetectionNet/FaceDetection';
22
import { createCanvas, getContext2dOrThrow } from './utils';
33

44
/**
@@ -10,12 +10,12 @@ import { createCanvas, getContext2dOrThrow } from './utils';
1010
*/
1111
export function extractFaces(
1212
image: HTMLCanvasElement,
13-
detections: FaceDetectionResult[]
13+
detections: FaceDetection[]
1414
): HTMLCanvasElement[] {
1515
const ctx = getContext2dOrThrow(image)
1616

1717
return detections.map(det => {
18-
const { x, y, width, height } = det.forSize(image.width, image.height).box
18+
const { x, y, width, height } = det.forSize(image.width, image.height).getBox()
1919

2020
const faceImg = createCanvas({ width, height })
2121
getContext2dOrThrow(faceImg)

src/faceDetectionNet/FaceDetection.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Rect } from '../Rect';
2+
import { Dimensions } from '../types';
3+
4+
export class FaceDetection {
5+
private _score: number
6+
private _box: Rect
7+
private _imageWidth: number
8+
private _imageHeight: number
9+
10+
constructor(
11+
score: number,
12+
relativeBox: Rect,
13+
imageDims: Dimensions
14+
) {
15+
const { width, height } = imageDims
16+
this._imageWidth = width
17+
this._imageHeight = height
18+
this._score = score
19+
this._box = new Rect(
20+
Math.floor(relativeBox.x * width),
21+
Math.floor(relativeBox.y * height),
22+
Math.floor(relativeBox.width * width),
23+
Math.floor(relativeBox.height * height)
24+
)
25+
}
26+
27+
public getScore() {
28+
return this._score
29+
}
30+
31+
public getBox() {
32+
return this._box
33+
}
34+
35+
public getRelativeBox() {
36+
return new Rect(
37+
this._box.x / this._imageWidth,
38+
this._box.y / this._imageHeight,
39+
this._box.width / this._imageWidth,
40+
this._box.height / this._imageHeight
41+
)
42+
}
43+
44+
public forSize(width: number, height: number): FaceDetection {
45+
return new FaceDetection(
46+
this._score,
47+
this.getRelativeBox(),
48+
{ width, height}
49+
)
50+
}
51+
}

src/faceDetectionNet/FaceDetectionResult.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/faceDetectionNet/index.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import * as tf from '@tensorflow/tfjs-core';
33
import { getImageTensor } from '../getImageTensor';
44
import { NetInput } from '../NetInput';
55
import { padToSquare } from '../padToSquare';
6-
import { TNetInput } from '../types';
6+
import { TNetInput, Dimensions } from '../types';
77
import { extractParams } from './extractParams';
8-
import { FaceDetectionResult } from './FaceDetectionResult';
8+
import { FaceDetection } from './FaceDetection';
99
import { mobileNetV1 } from './mobileNetV1';
1010
import { nonMaxSuppression } from './nonMaxSuppression';
1111
import { outputLayer } from './outputLayer';
1212
import { predictionLayer } from './predictionLayer';
1313
import { resizeLayer } from './resizeLayer';
14+
import { Rect } from '../Rect';
1415

1516
export function faceDetectionNet(weights: Float32Array) {
1617
const params = extractParams(weights)
@@ -40,9 +41,10 @@ export function faceDetectionNet(weights: Float32Array) {
4041
input: tf.Tensor | NetInput,
4142
minConfidence: number = 0.8,
4243
maxResults: number = 100,
43-
): Promise<FaceDetectionResult[]> {
44+
): Promise<FaceDetection[]> {
4445

4546
let paddedHeightRelative = 1, paddedWidthRelative = 1
47+
let imageDimensions: Dimensions | undefined
4648

4749
const {
4850
boxes: _boxes,
@@ -51,6 +53,7 @@ export function faceDetectionNet(weights: Float32Array) {
5153

5254
let imgTensor = getImageTensor(input)
5355
const [height, width] = imgTensor.shape.slice(1)
56+
imageDimensions = { width, height }
5457

5558
imgTensor = padToSquare(imgTensor)
5659
paddedHeightRelative = imgTensor.shape[1] / height
@@ -80,13 +83,26 @@ export function faceDetectionNet(weights: Float32Array) {
8083
)
8184

8285
const results = indices
83-
.map(idx => new FaceDetectionResult(
84-
scoresData[idx],
85-
boxes.get(idx, 0) * paddedHeightRelative,
86-
boxes.get(idx, 1) * paddedWidthRelative,
87-
boxes.get(idx, 2) * paddedHeightRelative,
88-
boxes.get(idx, 3) * paddedWidthRelative
89-
))
86+
.map(idx => {
87+
const [top, bottom] = [
88+
Math.max(0, boxes.get(idx, 0)),
89+
Math.min(1.0, boxes.get(idx, 2))
90+
].map(val => val * paddedHeightRelative)
91+
const [left, right] = [
92+
Math.max(0, boxes.get(idx, 1)),
93+
Math.min(1.0, boxes.get(idx, 3))
94+
].map(val => val * paddedWidthRelative)
95+
return new FaceDetection(
96+
scoresData[idx],
97+
new Rect(
98+
left,
99+
top,
100+
right - left,
101+
bottom - top
102+
),
103+
imageDimensions as Dimensions
104+
)
105+
})
90106

91107
boxes.dispose()
92108
scores.dispose()

src/faceDetectionNet/types.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,4 @@ export namespace FaceDetectionNet {
6262
prediction_layer_params: PredictionLayerParams,
6363
output_layer_params: OutputLayerParams
6464
}
65-
66-
export type Detection = {
67-
score: number
68-
box: {
69-
x: number,
70-
y: number,
71-
width: number,
72-
height: number
73-
}
74-
}
75-
7665
}

src/utils.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FaceDetectionNet } from './faceDetectionNet/types';
1+
import { FaceDetection } from './faceDetectionNet/FaceDetection';
22
import { FaceLandmarks } from './faceLandmarkNet/FaceLandmarks';
33
import { Dimensions, DrawBoxOptions, DrawLandmarksOptions, DrawOptions, DrawTextOptions } from './types';
44

@@ -115,7 +115,7 @@ export function drawText(
115115

116116
export function drawDetection(
117117
canvasArg: string | HTMLCanvasElement,
118-
detection: FaceDetectionNet.Detection | FaceDetectionNet.Detection[],
118+
detection: FaceDetection | FaceDetection[],
119119
options?: DrawBoxOptions & DrawTextOptions & { withScore: boolean }
120120
) {
121121
const canvas = getElement(canvasArg)
@@ -128,17 +128,12 @@ export function drawDetection(
128128
: [detection]
129129

130130
detectionArray.forEach((det) => {
131-
const {
132-
score,
133-
box
134-
} = det
135-
136131
const {
137132
x,
138133
y,
139134
width,
140135
height
141-
} = box
136+
} = det.getBox()
142137

143138
const drawOptions = Object.assign(
144139
getDefaultDrawOptions(),
@@ -161,7 +156,7 @@ export function drawDetection(
161156
ctx,
162157
x,
163158
y,
164-
`${round(score)}`,
159+
`${round(det.getScore())}`,
165160
drawOptions
166161
)
167162
}

0 commit comments

Comments
 (0)