Skip to content

Commit fdf2827

Browse files
testcases for allFaces, allFacesMtcnn
1 parent d098169 commit fdf2827

10 files changed

+243
-64
lines changed

test/data/facesFaceDescriptorsMtcnn.json

Lines changed: 6 additions & 0 deletions
Large diffs are not rendered by default.

test/data/facesFaceDescriptorsSsd.json

Lines changed: 6 additions & 0 deletions
Large diffs are not rendered by default.

test/data/facesFaceLandmarkPositions.json

Lines changed: 6 additions & 0 deletions
Large diffs are not rendered by default.

test/tests/e2e/allFaces.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import * as faceapi from '../../../src';
2+
import { describeWithNets, expectAllTensorsReleased, expectRectClose, expectPointClose } from '../../utils';
3+
import { expectedSsdBoxes } from './expectedResults';
4+
import { NetInput } from '../../../src/NetInput';
5+
import { toNetInput } from '../../../src';
6+
import * as tf from '@tensorflow/tfjs-core';
7+
import { Point } from '../../../src/Point';
8+
9+
describe('allFaces', () => {
10+
11+
let imgEl: HTMLImageElement
12+
let facesFaceLandmarkPositions: Point[][]
13+
let facesFaceDescriptors: number[][]
14+
15+
beforeAll(async () => {
16+
const img = await (await fetch('base/test/images/faces.jpg')).blob()
17+
imgEl = await faceapi.bufferToImage(img)
18+
facesFaceLandmarkPositions = await (await fetch('base/test/data/facesFaceLandmarkPositions.json')).json()
19+
facesFaceDescriptors = await (await fetch('base/test/data/facesFaceDescriptorsSsd.json')).json()
20+
})
21+
22+
describeWithNets('computes full face descriptions', { withAllFaces: true }, ({ allFaces }) => {
23+
24+
const expectedScores = [0.97, 0.88, 0.83, 0.82, 0.59, 0.52]
25+
const maxBoxDelta = 5
26+
const maxLandmarkPointsDelta = 1
27+
28+
it('scores > 0.8', async () => {
29+
const results = await allFaces(imgEl, 0.8)
30+
31+
expect(results.length).toEqual(4)
32+
results.forEach(({ detection, landmarks, descriptor }, i) => {
33+
expect(detection.getImageWidth()).toEqual(imgEl.width)
34+
expect(detection.getImageHeight()).toEqual(imgEl.height)
35+
expect(detection.getScore()).toBeCloseTo(expectedScores[i], 2)
36+
expectRectClose(detection.getBox(), expectedSsdBoxes[i], maxBoxDelta)
37+
landmarks.getPositions().forEach((pt, j) => expectPointClose(pt, facesFaceLandmarkPositions[i][j], maxLandmarkPointsDelta))
38+
expect(descriptor).toEqual(new Float32Array(facesFaceDescriptors[i]))
39+
})
40+
})
41+
42+
it('scores > 0.5', async () => {
43+
const results = await allFaces(imgEl, 0.5)
44+
45+
expect(results.length).toEqual(6)
46+
results.forEach(({ detection, landmarks, descriptor }, i) => {
47+
expect(detection.getImageWidth()).toEqual(imgEl.width)
48+
expect(detection.getImageHeight()).toEqual(imgEl.height)
49+
expect(detection.getScore()).toBeCloseTo(expectedScores[i], 2)
50+
expectRectClose(detection.getBox(), expectedSsdBoxes[i], maxBoxDelta)
51+
landmarks.getPositions().forEach((pt, j) => expectPointClose(pt, facesFaceLandmarkPositions[i][j], maxLandmarkPointsDelta))
52+
expect(descriptor).toEqual(new Float32Array(facesFaceDescriptors[i]))
53+
})
54+
})
55+
56+
})
57+
58+
describeWithNets('no memory leaks', { withAllFaces: true }, ({ allFaces }) => {
59+
60+
it('single image element', async () => {
61+
await expectAllTensorsReleased(async () => {
62+
await allFaces(imgEl)
63+
})
64+
})
65+
66+
it('single tf.Tensor3D', async () => {
67+
const tensor = tf.fromPixels(imgEl)
68+
69+
await expectAllTensorsReleased(async () => {
70+
const netInput = (new NetInput([tensor])).managed()
71+
await allFaces(netInput)
72+
})
73+
74+
tensor.dispose()
75+
})
76+
77+
it('single batch size 1 tf.Tensor4Ds', async () => {
78+
const tensor = tf.tidy(() => tf.fromPixels(imgEl).expandDims()) as tf.Tensor4D
79+
80+
await expectAllTensorsReleased(async () => {
81+
await allFaces(await toNetInput(tensor, true))
82+
})
83+
84+
tensor.dispose()
85+
})
86+
87+
})
88+
89+
90+
})

test/tests/e2e/allFacesMtcnn.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as faceapi from '../../../src';
2+
import { FaceLandmarks5 } from '../../../src/mtcnn/FaceLandmarks5';
3+
import { NetInput } from '../../../src/NetInput';
4+
import { describeWithNets, expectAllTensorsReleased } from '../../utils';
5+
import { expectMtcnnResults } from './expectedResults';
6+
7+
describe('allFacesMtcnn', () => {
8+
9+
let imgEl: HTMLImageElement
10+
let facesFaceDescriptors: number[][]
11+
12+
beforeAll(async () => {
13+
const img = await (await fetch('base/test/images/faces.jpg')).blob()
14+
imgEl = await faceapi.bufferToImage(img)
15+
facesFaceDescriptors = await (await fetch('base/test/data/facesFaceDescriptorsMtcnn.json')).json()
16+
})
17+
18+
describeWithNets('computes full face descriptions', { withAllFacesMtcnn: true }, ({ allFacesMtcnn }) => {
19+
20+
it('minFaceSize = 20', async () => {
21+
const forwardParams = {
22+
minFaceSize: 20
23+
}
24+
25+
const results = await allFacesMtcnn(imgEl, forwardParams)
26+
expect(results.length).toEqual(6)
27+
28+
const mtcnnResult = results.map(res => ({
29+
faceDetection: res.detection,
30+
faceLandmarks: res.landmarks as FaceLandmarks5
31+
}))
32+
expectMtcnnResults(mtcnnResult, [0, 1, 2, 3, 4, 5], 1, 1)
33+
results.forEach(({ descriptor }, i) => {
34+
expect(descriptor).toEqual(new Float32Array(facesFaceDescriptors[i]))
35+
})
36+
})
37+
38+
})
39+
40+
describeWithNets('no memory leaks', { withAllFacesMtcnn: true }, ({ allFacesMtcnn }) => {
41+
42+
it('single image element', async () => {
43+
await expectAllTensorsReleased(async () => {
44+
await allFacesMtcnn(imgEl)
45+
})
46+
})
47+
48+
})
49+
50+
51+
})

test/tests/e2e/expectedResults.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as faceapi from '../../../src';
2+
import { FaceLandmarks5 } from '../../../src/mtcnn/FaceLandmarks5';
3+
import { Point } from '../../../src/Point';
4+
import { expectMaxDelta, expectPointClose, expectRectClose } from '../../utils';
5+
6+
export const expectedSsdBoxes = [
7+
{ x: 48, y: 253, width: 104, height: 129 },
8+
{ x: 260, y: 227, width: 76, height: 117 },
9+
{ x: 466, y: 165, width: 88, height: 130 },
10+
{ x: 234, y: 36, width: 84, height: 119 },
11+
{ x: 577, y: 65, width: 84, height: 105 },
12+
{ x: 84, y: 14, width: 79, height: 132 }
13+
]
14+
15+
export const expectedMtcnnBoxes = [
16+
{ x: 70, y: 21, width: 112, height: 112 },
17+
{ x: 36, y: 250, width: 133, height: 132 },
18+
{ x: 221, y: 43, width: 112, height: 111 },
19+
{ x: 247, y: 231, width: 106, height: 107 },
20+
{ x: 566, y: 67, width: 104, height: 104 },
21+
{ x: 451, y: 176, width: 122, height: 122 }
22+
]
23+
24+
export const expectedMtcnnFaceLandmarks = [
25+
[new Point(117, 58), new Point(156, 63), new Point(141, 86), new Point(109, 98), new Point(147, 104)],
26+
[new Point(82, 292), new Point(134, 304), new Point(104, 330), new Point(72, 342), new Point(120, 353)],
27+
[new Point(261, 82), new Point(306, 83), new Point(282, 113), new Point(257, 124), new Point(306, 126)],
28+
[new Point(277, 273), new Point(318, 273), new Point(295, 300), new Point(279, 311), new Point(316, 313)],
29+
[new Point(607, 110), new Point(645, 115), new Point(626, 138), new Point(601, 144), new Point(639, 150)],
30+
[new Point(489, 224), new Point(534, 223), new Point(507, 250), new Point(493, 271), new Point(530, 270)]
31+
]
32+
33+
34+
export function expectMtcnnResults(
35+
results: { faceDetection: faceapi.FaceDetection, faceLandmarks: faceapi.FaceLandmarks5 }[],
36+
boxOrder: number[],
37+
maxBoxDelta: number,
38+
maxLandmarkPointsDelta: number
39+
) {
40+
results.forEach((result, i) => {
41+
const { faceDetection, faceLandmarks } = result
42+
expect(faceDetection instanceof faceapi.FaceDetection).toBe(true)
43+
expect(faceLandmarks instanceof faceapi.FaceLandmarks5).toBe(true)
44+
expectRectClose(faceDetection.getBox(), expectedMtcnnBoxes[boxOrder[i]], maxBoxDelta)
45+
faceLandmarks.getPositions().forEach((pt, j) => expectPointClose(pt, expectedMtcnnFaceLandmarks[boxOrder[i]][j], maxLandmarkPointsDelta))
46+
expectMaxDelta(faceDetection.getScore(), 0.99, 0.01)
47+
})
48+
}

test/tests/e2e/faceDetectionNet.test.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import * as faceapi from '../../../src';
22
import { describeWithNets, expectAllTensorsReleased, expectRectClose } from '../../utils';
3-
4-
const expectedBoxes = [
5-
{ x: 48, y: 253, width: 104, height: 129 },
6-
{ x: 260, y: 227, width: 76, height: 117 },
7-
{ x: 466, y: 165, width: 88, height: 130 },
8-
{ x: 234, y: 36, width: 84, height: 119 },
9-
{ x: 577, y: 65, width: 84, height: 105 },
10-
{ x: 84, y: 14, width: 79, height: 132 }
11-
]
3+
import { expectedSsdBoxes } from './expectedResults';
124

135
describe('faceDetectionNet', () => {
146

@@ -32,7 +24,7 @@ describe('faceDetectionNet', () => {
3224
expect(det.getImageWidth()).toEqual(imgEl.width)
3325
expect(det.getImageHeight()).toEqual(imgEl.height)
3426
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
35-
expectRectClose(det.getBox(), expectedBoxes[i], maxBoxDelta)
27+
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
3628
})
3729
})
3830

@@ -44,7 +36,7 @@ describe('faceDetectionNet', () => {
4436
expect(det.getImageWidth()).toEqual(imgEl.width)
4537
expect(det.getImageHeight()).toEqual(imgEl.height)
4638
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
47-
expectRectClose(det.getBox(), expectedBoxes[i], maxBoxDelta)
39+
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
4840
})
4941
})
5042

@@ -63,7 +55,7 @@ describe('faceDetectionNet', () => {
6355
expect(det.getImageWidth()).toEqual(imgEl.width)
6456
expect(det.getImageHeight()).toEqual(imgEl.height)
6557
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
66-
expectRectClose(det.getBox(), expectedBoxes[i], maxBoxDelta)
58+
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
6759
})
6860
})
6961

@@ -75,7 +67,7 @@ describe('faceDetectionNet', () => {
7567
expect(det.getImageWidth()).toEqual(imgEl.width)
7668
expect(det.getImageHeight()).toEqual(imgEl.height)
7769
expect(det.getScore()).toBeCloseTo(expectedScores[i], 2)
78-
expectRectClose(det.getBox(), expectedBoxes[i], maxBoxDelta)
70+
expectRectClose(det.getBox(), expectedSsdBoxes[i], maxBoxDelta)
7971
})
8072
})
8173

test/tests/e2e/faceLandmarkNet.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as faceapi from '../../../src';
44
import { isTensor3D } from '../../../src/commons/isTensor';
55
import { Point } from '../../../src/Point';
66
import { Dimensions, TMediaElement } from '../../../src/types';
7-
import { expectMaxDelta, expectAllTensorsReleased, tensor3D, describeWithNets } from '../../utils';
7+
import { expectMaxDelta, expectAllTensorsReleased, describeWithNets } from '../../utils';
88
import { NetInput } from '../../../src/NetInput';
99
import { toNetInput } from '../../../src';
1010

test/tests/e2e/mtcnn.test.ts

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,7 @@
11
import * as faceapi from '../../../src';
2-
import { MtcnnResult } from '../../../src/mtcnn/types';
3-
import { Point } from '../../../src/Point';
4-
import { describeWithNets, expectAllTensorsReleased, expectPointClose, expectRectClose, expectMaxDelta } from '../../utils';
5-
6-
const expectedBoxes = [
7-
{ x: 70, y: 21, width: 112, height: 112 },
8-
{ x: 36, y: 250, width: 133, height: 132 },
9-
{ x: 221, y: 43, width: 112, height: 111 },
10-
{ x: 247, y: 231, width: 106, height: 107 },
11-
{ x: 566, y: 67, width: 104, height: 104 },
12-
{ x: 451, y: 176, width: 122, height: 122 }
13-
]
14-
15-
const expectedFaceLandmarks = [
16-
[new Point(117, 58), new Point(156, 63), new Point(141, 86), new Point(109, 98), new Point(147, 104)],
17-
[new Point(82, 292), new Point(134, 304), new Point(104, 330), new Point(72, 342), new Point(120, 353)],
18-
[new Point(261, 82), new Point(306, 83), new Point(282, 113), new Point(257, 124), new Point(306, 126)],
19-
[new Point(277, 273), new Point(318, 273), new Point(295, 300), new Point(279, 311), new Point(316, 313)],
20-
[new Point(607, 110), new Point(645, 115), new Point(626, 138), new Point(601, 144), new Point(639, 150)],
21-
[new Point(489, 224), new Point(534, 223), new Point(507, 250), new Point(493, 271), new Point(530, 270)]
22-
]
2+
import { describeWithNets, expectAllTensorsReleased } from '../../utils';
3+
import { expectMtcnnResults } from './expectedResults';
4+
235

246
describe('mtcnn', () => {
257

@@ -32,21 +14,6 @@ describe('mtcnn', () => {
3214

3315
describeWithNets('uncompressed weights', { withMtcnn: { quantized: false } }, ({ mtcnn }) => {
3416

35-
function expectResults(
36-
results: MtcnnResult[],
37-
boxOrder: number[],
38-
maxBoxDelta: number,
39-
maxLandmarkPointsDelta: number
40-
) {
41-
results.forEach((result, i) => {
42-
const { faceDetection, faceLandmarks } = result
43-
expect(faceDetection instanceof faceapi.FaceDetection).toBe(true)
44-
expect(faceLandmarks instanceof faceapi.FaceLandmarks5).toBe(true)
45-
expectRectClose(faceDetection.getBox(), expectedBoxes[boxOrder[i]], maxBoxDelta)
46-
faceLandmarks.getPositions().forEach((pt, j) => expectPointClose(pt, expectedFaceLandmarks[boxOrder[i]][j], maxLandmarkPointsDelta))
47-
expectMaxDelta(faceDetection.getScore(), 0.99, 0.01)
48-
})
49-
}
5017

5118
it('minFaceSize = 20, finds all faces', async () => {
5219
const forwardParams = {
@@ -55,7 +22,7 @@ describe('mtcnn', () => {
5522

5623
const results = await mtcnn.forward(imgEl, forwardParams)
5724
expect(results.length).toEqual(6)
58-
expectResults(results, [0, 1, 2, 3, 4, 5], 1, 1)
25+
expectMtcnnResults(results, [0, 1, 2, 3, 4, 5], 1, 1)
5926
})
6027

6128
it('minFaceSize = 80, finds all faces', async () => {
@@ -66,7 +33,7 @@ describe('mtcnn', () => {
6633
const results = await mtcnn.forward(imgEl, forwardParams)
6734

6835
expect(results.length).toEqual(6)
69-
expectResults(results, [0, 5, 3, 1, 2, 4], 12, 12)
36+
expectMtcnnResults(results, [0, 5, 3, 1, 2, 4], 12, 12)
7037
})
7138

7239
it('all optional params passed, finds all faces', async () => {
@@ -79,7 +46,7 @@ describe('mtcnn', () => {
7946

8047
const results = await mtcnn.forward(imgEl, forwardParams)
8148
expect(results.length).toEqual(6)
82-
expectResults(results, [5, 1, 4, 3, 2, 0], 6, 10)
49+
expectMtcnnResults(results, [5, 1, 4, 3, 2, 0], 6, 10)
8350
})
8451

8552
it('scale steps passed, finds all faces', async () => {
@@ -89,7 +56,7 @@ describe('mtcnn', () => {
8956

9057
const results = await mtcnn.forward(imgEl, forwardParams)
9158
expect(results.length).toEqual(6)
92-
expectResults(results, [5, 1, 3, 0, 2, 4], 7, 15)
59+
expectMtcnnResults(results, [5, 1, 3, 0, 2, 4], 7, 15)
9360
})
9461

9562
})

0 commit comments

Comments
 (0)