Skip to content

Commit 37609c2

Browse files
move task composition test cases from tinyFaceDetector.test to globalApi test suite + add test cases for withAgeAndGender()
1 parent 5ff0c5c commit 37609c2

9 files changed

+501
-125
lines changed

src/globalApi/PredictAgeAndGenderTask.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import { ComputeAllFaceDescriptorsTask, ComputeSingleFaceDescriptorTask } from '
1111
import { extractAllFacesAndComputeResults, extractSingleFaceAndComputeResult } from './extractFacesAndComputeResults';
1212
import { nets } from './nets';
1313
import {
14+
PredictAllFaceExpressionsTask,
1415
PredictAllFaceExpressionsWithFaceAlignmentTask,
16+
PredictSingleFaceExpressionsTask,
1517
PredictSingleFaceExpressionsWithFaceAlignmentTask,
1618
} from './PredictFaceExpressionsTask';
1719

@@ -47,6 +49,10 @@ export class PredictAllAgeAndGenderTask<
4749
return extendWithAge(extendWithGender(parentResult, gender, genderProbability), age)
4850
})
4951
}
52+
53+
withFaceExpressions() {
54+
return new PredictAllFaceExpressionsTask(this, this.input)
55+
}
5056
}
5157

5258
export class PredictSingleAgeAndGenderTask<
@@ -69,6 +75,10 @@ export class PredictSingleAgeAndGenderTask<
6975

7076
return extendWithAge(extendWithGender(parentResult, gender, genderProbability), age)
7177
}
78+
79+
withFaceExpressions() {
80+
return new PredictSingleFaceExpressionsTask(this, this.input)
81+
}
7282
}
7383

7484
export class PredictAllAgeAndGenderWithFaceAlignmentTask<

src/globalApi/PredictFaceExpressionsTask.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import { ComputeAllFaceDescriptorsTask, ComputeSingleFaceDescriptorTask } from '
1010
import { extractAllFacesAndComputeResults, extractSingleFaceAndComputeResult } from './extractFacesAndComputeResults';
1111
import { nets } from './nets';
1212
import {
13+
PredictAllAgeAndGenderTask,
1314
PredictAllAgeAndGenderWithFaceAlignmentTask,
15+
PredictSingleAgeAndGenderTask,
1416
PredictSingleAgeAndGenderWithFaceAlignmentTask,
1517
} from './PredictAgeAndGenderTask';
1618

@@ -45,6 +47,10 @@ export class PredictAllFaceExpressionsTask<
4547
(parentResult, i) => extendWithFaceExpressions<TSource>(parentResult, faceExpressionsByFace[i])
4648
)
4749
}
50+
51+
withAgeAndGender() {
52+
return new PredictAllAgeAndGenderTask(this, this.input)
53+
}
4854
}
4955

5056
export class PredictSingleFaceExpressionsTask<
@@ -67,6 +73,10 @@ export class PredictSingleFaceExpressionsTask<
6773

6874
return extendWithFaceExpressions(parentResult, faceExpressions)
6975
}
76+
77+
withAgeAndGender() {
78+
return new PredictSingleAgeAndGenderTask(this, this.input)
79+
}
7080
}
7181

7282
export class PredictAllFaceExpressionsWithFaceAlignmentTask<

test/tests/tinyFaceDetector/expectedBoxes.ts renamed to test/expectedTinyFaceDetectorBoxes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { IRect } from '../../../src';
2-
import { sortBoxes } from '../../utils';
1+
import { IRect } from '../src';
2+
import { sortBoxes } from './utils';
33

44
export const expectedTinyFaceDetectorBoxes: IRect[] = sortBoxes([
55
{ x: 29, y: 264, width: 139, height: 137 },

test/tests/globalApi/consts.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { TinyFaceDetectorOptions } from '../../../src';
2+
3+
export const withNetArgs = {
4+
withAllFacesTinyFaceDetector: true,
5+
withFaceExpressionNet: { quantized: true },
6+
withAgeGenderNet: { quantized: true }
7+
}
8+
9+
export const expectedScores = [0.7, 0.82, 0.93, 0.86, 0.79, 0.84]
10+
11+
export const deltas = {
12+
maxScoreDelta: 0.05,
13+
maxBoxDelta: 5,
14+
maxLandmarksDelta: 10,
15+
maxDescriptorDelta: 0.2
16+
}
17+
18+
export const faceDetectorOptions = new TinyFaceDetectorOptions({
19+
inputSize: 416
20+
})
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import * as faceapi from '../../../src';
2+
import { WithAge } from '../../../src/factories/WithAge';
3+
import { WithFaceExpressions } from '../../../src/factories/WithFaceExpressions';
4+
import { WithGender } from '../../../src/factories/WithGender';
5+
import { loadImage } from '../../env';
6+
import { expectedTinyFaceDetectorBoxes } from '../../expectedTinyFaceDetectorBoxes';
7+
import { expectFaceDetectionsWithLandmarks } from '../../expectFaceDetectionsWithLandmarks';
8+
import { expectFullFaceDescriptions } from '../../expectFullFaceDescriptions';
9+
import {
10+
assembleExpectedFullFaceDescriptions,
11+
describeWithBackend,
12+
describeWithNets,
13+
expectAllTensorsReleased,
14+
ExpectedFullFaceDescription,
15+
} from '../../utils';
16+
import { deltas, expectedScores, faceDetectorOptions, withNetArgs } from './consts';
17+
18+
function expectFaceExpressions(results: WithFaceExpressions<{}>[]) {
19+
results.forEach((result, i) => {
20+
const { happy, neutral } = result.expressions
21+
22+
const happyProb = i === 4 ? 0 : 0.95
23+
const neutralProb = i === 4 ? 0.4 : 0
24+
25+
expect(happy).toBeGreaterThanOrEqual(happyProb)
26+
expect(neutral).toBeGreaterThanOrEqual(neutralProb)
27+
})
28+
}
29+
30+
const ages = [41, 26, 37, 27, 31, 34]
31+
const agesUnaligned = [37, 30, 22, 26, 36, 33]
32+
const genders = ['male', 'female', 'female', 'male', 'male', 'female']
33+
34+
function expectAgesAndGender(results: WithAge<WithGender<{}>>[], aligned = true) {
35+
results.forEach((result, i) => {
36+
const { age, gender, genderProbability } = result
37+
38+
expect(Math.round(age)).toEqual(aligned ? ages[i] : agesUnaligned[i])
39+
expect(gender).toEqual(genders[i])
40+
expect(genderProbability).toBeGreaterThanOrEqual(i === 5 ? 0.7 : 0.9)
41+
})
42+
}
43+
44+
describeWithBackend('globalApi', () => {
45+
46+
let imgEl: HTMLImageElement
47+
let expectedFullFaceDescriptions: ExpectedFullFaceDescription[]
48+
49+
beforeAll(async () => {
50+
imgEl = await loadImage('test/images/faces.jpg')
51+
expectedFullFaceDescriptions = await assembleExpectedFullFaceDescriptions(expectedTinyFaceDetectorBoxes)
52+
})
53+
54+
describeWithNets('detectAllFaces', withNetArgs, () => {
55+
56+
describe('without face alignment', () => {
57+
58+
it('detectAllFaces.withFaceExpressions()', async () => {
59+
const results = await faceapi
60+
.detectAllFaces(imgEl, faceDetectorOptions)
61+
.withFaceExpressions()
62+
63+
expect(results.length).toEqual(6)
64+
expectFaceExpressions(results)
65+
})
66+
67+
it('detectAllFaces.withAgeAndGender()', async () => {
68+
const results = await faceapi
69+
.detectAllFaces(imgEl, faceDetectorOptions)
70+
.withAgeAndGender()
71+
72+
expect(results.length).toEqual(6)
73+
expectAgesAndGender(results, false)
74+
})
75+
76+
it('detectAllFaces.withFaceExpressions().withAgeAndGender()', async () => {
77+
const results = await faceapi
78+
.detectAllFaces(imgEl, faceDetectorOptions)
79+
.withFaceExpressions()
80+
.withAgeAndGender()
81+
82+
expect(results.length).toEqual(6)
83+
expectFaceExpressions(results)
84+
expectAgesAndGender(results, false)
85+
})
86+
87+
it('detectAllFaces.withAgeAndGender().withFaceExpressions()', async () => {
88+
const results = await faceapi
89+
.detectAllFaces(imgEl, faceDetectorOptions)
90+
.withAgeAndGender()
91+
.withFaceExpressions()
92+
93+
expect(results.length).toEqual(6)
94+
expectFaceExpressions(results)
95+
expectAgesAndGender(results, false)
96+
})
97+
98+
})
99+
100+
describe('with face alignment', () => {
101+
102+
it('detectAllFaces.withFaceLandmarks().withFaceExpressions()', async () => {
103+
const results = await faceapi
104+
.detectAllFaces(imgEl, faceDetectorOptions)
105+
.withFaceLandmarks()
106+
.withFaceExpressions()
107+
108+
expect(results.length).toEqual(6)
109+
expectFaceExpressions(results)
110+
expectFaceDetectionsWithLandmarks(results, expectedFullFaceDescriptions, expectedScores, deltas)
111+
})
112+
113+
it('detectAllFaces.withFaceLandmarks().withAgeAndGender()', async () => {
114+
const results = await faceapi
115+
.detectAllFaces(imgEl, faceDetectorOptions)
116+
.withFaceLandmarks()
117+
.withAgeAndGender()
118+
119+
expect(results.length).toEqual(6)
120+
expectAgesAndGender(results)
121+
expectFaceDetectionsWithLandmarks(results, expectedFullFaceDescriptions, expectedScores, deltas)
122+
})
123+
124+
it('detectAllFaces.withFaceLandmarks().withFaceDescriptors()', async () => {
125+
const results = await faceapi
126+
.detectAllFaces(imgEl, faceDetectorOptions)
127+
.withFaceLandmarks()
128+
.withFaceDescriptors()
129+
130+
expect(results.length).toEqual(6)
131+
expectFullFaceDescriptions(results, expectedFullFaceDescriptions, expectedScores, deltas)
132+
})
133+
134+
it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withAgeAndGender()', async () => {
135+
const results = await faceapi
136+
.detectAllFaces(imgEl, faceDetectorOptions)
137+
.withFaceLandmarks()
138+
.withFaceExpressions()
139+
.withAgeAndGender()
140+
141+
expect(results.length).toEqual(6)
142+
expectFaceExpressions(results)
143+
expectAgesAndGender(results)
144+
expectFaceDetectionsWithLandmarks(results, expectedFullFaceDescriptions, expectedScores, deltas)
145+
})
146+
147+
it('detectAllFaces.withFaceLandmarks().withAgeAndGender().withFaceExpressions()', async () => {
148+
const results = await faceapi
149+
.detectAllFaces(imgEl, faceDetectorOptions)
150+
.withFaceLandmarks()
151+
.withAgeAndGender()
152+
.withFaceExpressions()
153+
154+
expect(results.length).toEqual(6)
155+
expectFaceExpressions(results)
156+
expectAgesAndGender(results)
157+
expectFaceDetectionsWithLandmarks(results, expectedFullFaceDescriptions, expectedScores, deltas)
158+
})
159+
160+
it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withFaceDescriptors()', async () => {
161+
const results = await faceapi
162+
.detectAllFaces(imgEl, faceDetectorOptions)
163+
.withFaceLandmarks()
164+
.withFaceExpressions()
165+
.withFaceDescriptors()
166+
167+
expect(results.length).toEqual(6)
168+
expectFaceExpressions(results)
169+
expectFullFaceDescriptions(results, expectedFullFaceDescriptions, expectedScores, deltas)
170+
})
171+
172+
it('detectAllFaces.withFaceLandmarks().withAgeAndGender().withFaceDescriptors()', async () => {
173+
const results = await faceapi
174+
.detectAllFaces(imgEl, faceDetectorOptions)
175+
.withFaceLandmarks()
176+
.withAgeAndGender()
177+
.withFaceDescriptors()
178+
179+
expect(results.length).toEqual(6)
180+
expectAgesAndGender(results)
181+
expectFullFaceDescriptions(results, expectedFullFaceDescriptions, expectedScores, deltas)
182+
})
183+
184+
it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withAgeAndGender().withFaceDescriptors()', async () => {
185+
const results = await faceapi
186+
.detectAllFaces(imgEl, faceDetectorOptions)
187+
.withFaceLandmarks()
188+
.withFaceExpressions()
189+
.withAgeAndGender()
190+
.withFaceDescriptors()
191+
192+
expect(results.length).toEqual(6)
193+
expectFaceExpressions(results)
194+
expectAgesAndGender(results)
195+
expectFullFaceDescriptions(results, expectedFullFaceDescriptions, expectedScores, deltas)
196+
})
197+
198+
})
199+
200+
describe('no memory leaks', () => {
201+
202+
it('detectAllFaces.withFaceLandmarks().withFaceDescriptors()', async () => {
203+
await expectAllTensorsReleased(async () => {
204+
await faceapi
205+
.detectAllFaces(imgEl, faceDetectorOptions)
206+
.withFaceLandmarks()
207+
.withFaceDescriptors()
208+
})
209+
})
210+
211+
it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withAgeAndGender().withFaceDescriptors()', async () => {
212+
await expectAllTensorsReleased(async () => {
213+
await faceapi
214+
.detectAllFaces(imgEl, faceDetectorOptions)
215+
.withFaceLandmarks()
216+
.withFaceExpressions()
217+
.withAgeAndGender()
218+
.withFaceDescriptors()
219+
})
220+
})
221+
222+
})
223+
224+
})
225+
})

0 commit comments

Comments
 (0)