Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
face
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Кубота
face
Commits
5ff0c5c5
Commit
5ff0c5c5
authored
May 07, 2019
by
vincent
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
test cases for AgeGenderNet + fixed memory leaks and batch inputs for AgeGenderNet
parent
5be059ae
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
238 additions
and
5 deletions
+238
-5
AgeGenderNet.ts
src/ageGenderNet/AgeGenderNet.ts
+9
-3
ageGenderNet.test.ts
test/tests/ageGenderNet/ageGenderNet.test.ts
+214
-0
faceExpressionNet.test.ts
test/tests/faceExpressionNet/faceExpressionNet.test.ts
+2
-2
utils.ts
test/utils.ts
+13
-0
No files found.
src/ageGenderNet/AgeGenderNet.ts
View file @
5ff0c5c5
...
@@ -42,8 +42,10 @@ export class AgeGenderNet extends NeuralNetwork<NetParams> {
...
@@ -42,8 +42,10 @@ export class AgeGenderNet extends NeuralNetwork<NetParams> {
}
}
public
forwardInput
(
input
:
NetInput
|
tf
.
Tensor4D
):
NetOutput
{
public
forwardInput
(
input
:
NetInput
|
tf
.
Tensor4D
):
NetOutput
{
const
{
age
,
gender
}
=
this
.
runNet
(
input
)
return
tf
.
tidy
(()
=>
{
return
tf
.
tidy
(()
=>
({
age
,
gender
:
tf
.
softmax
(
gender
)
}))
const
{
age
,
gender
}
=
this
.
runNet
(
input
)
return
{
age
,
gender
:
tf
.
softmax
(
gender
)
}
})
}
}
public
async
forward
(
input
:
TNetInput
):
Promise
<
NetOutput
>
{
public
async
forward
(
input
:
TNetInput
):
Promise
<
NetOutput
>
{
...
@@ -64,14 +66,18 @@ export class AgeGenderNet extends NeuralNetwork<NetParams> {
...
@@ -64,14 +66,18 @@ export class AgeGenderNet extends NeuralNetwork<NetParams> {
const
predictionsByBatch
=
await
Promise
.
all
(
const
predictionsByBatch
=
await
Promise
.
all
(
ageAndGenderTensors
.
map
(
async
({
ageTensor
,
genderTensor
})
=>
{
ageAndGenderTensors
.
map
(
async
({
ageTensor
,
genderTensor
})
=>
{
const
age
=
(
await
ageTensor
.
data
())[
0
]
const
age
=
(
await
ageTensor
.
data
())[
0
]
const
probMale
=
(
await
out
.
gende
r
.
data
())[
0
]
const
probMale
=
(
await
genderTenso
r
.
data
())[
0
]
const
isMale
=
probMale
>
0.5
const
isMale
=
probMale
>
0.5
const
gender
=
isMale
?
Gender
.
MALE
:
Gender
.
FEMALE
const
gender
=
isMale
?
Gender
.
MALE
:
Gender
.
FEMALE
const
genderProbability
=
isMale
?
probMale
:
(
1
-
probMale
)
const
genderProbability
=
isMale
?
probMale
:
(
1
-
probMale
)
ageTensor
.
dispose
()
genderTensor
.
dispose
()
return
{
age
,
gender
,
genderProbability
}
return
{
age
,
gender
,
genderProbability
}
})
})
)
)
out
.
age
.
dispose
()
out
.
gender
.
dispose
()
return
netInput
.
isBatchInput
return
netInput
.
isBatchInput
?
predictionsByBatch
?
predictionsByBatch
...
...
test/tests/ageGenderNet/ageGenderNet.test.ts
0 → 100644
View file @
5ff0c5c5
import
*
as
tf
from
'@tensorflow/tfjs-core'
;
import
{
createCanvasFromMedia
,
NetInput
,
toNetInput
}
from
'../../../src'
;
import
{
AgeAndGenderPrediction
}
from
'../../../src/ageGenderNet/types'
;
import
{
loadImage
}
from
'../../env'
;
import
{
describeWithBackend
,
describeWithNets
,
expectAllTensorsReleased
}
from
'../../utils'
;
function
expectResultsAngry
(
result
:
AgeAndGenderPrediction
)
{
expect
(
result
.
age
).
toBeGreaterThanOrEqual
(
38
)
expect
(
result
.
age
).
toBeLessThanOrEqual
(
42
)
expect
(
result
.
gender
).
toEqual
(
'male'
)
expect
(
result
.
genderProbability
).
toBeGreaterThanOrEqual
(
0.9
)
}
function
expectResultsSurprised
(
result
:
AgeAndGenderPrediction
)
{
expect
(
result
.
age
).
toBeGreaterThanOrEqual
(
24
)
expect
(
result
.
age
).
toBeLessThanOrEqual
(
28
)
expect
(
result
.
gender
).
toEqual
(
'female'
)
expect
(
result
.
genderProbability
).
toBeGreaterThanOrEqual
(
0.8
)
}
describeWithBackend
(
'ageGenderNet'
,
()
=>
{
let
imgElAngry
:
HTMLImageElement
let
imgElSurprised
:
HTMLImageElement
beforeAll
(
async
()
=>
{
imgElAngry
=
await
loadImage
(
'test/images/angry_cropped.jpg'
)
imgElSurprised
=
await
loadImage
(
'test/images/surprised_cropped.jpg'
)
})
describeWithNets
(
'quantized weights'
,
{
withAgeGenderNet
:
{
quantized
:
true
}
},
({
ageGenderNet
})
=>
{
it
(
'recognizes age and gender'
,
async
()
=>
{
const
result
=
await
ageGenderNet
.
predictAgeAndGender
(
imgElAngry
)
as
AgeAndGenderPrediction
expectResultsAngry
(
result
)
})
})
describeWithNets
(
'batch inputs'
,
{
withAgeGenderNet
:
{
quantized
:
true
}
},
({
ageGenderNet
})
=>
{
it
(
'recognizes age and gender for batch of image elements'
,
async
()
=>
{
const
inputs
=
[
imgElAngry
,
imgElSurprised
]
const
results
=
await
ageGenderNet
.
predictAgeAndGender
(
inputs
)
as
AgeAndGenderPrediction
[]
expect
(
Array
.
isArray
(
results
)).
toBe
(
true
)
expect
(
results
.
length
).
toEqual
(
2
)
const
[
resultAngry
,
resultSurprised
]
=
results
expectResultsAngry
(
resultAngry
)
expectResultsSurprised
(
resultSurprised
)
})
it
(
'computes age and gender for batch of tf.Tensor3D'
,
async
()
=>
{
const
inputs
=
[
imgElAngry
,
imgElSurprised
].
map
(
el
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
el
)))
const
results
=
await
ageGenderNet
.
predictAgeAndGender
(
inputs
)
as
AgeAndGenderPrediction
[]
expect
(
Array
.
isArray
(
results
)).
toBe
(
true
)
expect
(
results
.
length
).
toEqual
(
2
)
const
[
resultAngry
,
resultSurprised
]
=
results
expectResultsAngry
(
resultAngry
)
expectResultsSurprised
(
resultSurprised
)
})
it
(
'computes age and gender for batch of mixed inputs'
,
async
()
=>
{
const
inputs
=
[
imgElAngry
,
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
imgElSurprised
))]
const
results
=
await
ageGenderNet
.
predictAgeAndGender
(
inputs
)
as
AgeAndGenderPrediction
[]
expect
(
Array
.
isArray
(
results
)).
toBe
(
true
)
expect
(
results
.
length
).
toEqual
(
2
)
const
[
resultAngry
,
resultSurprised
]
=
results
expectResultsAngry
(
resultAngry
)
expectResultsSurprised
(
resultSurprised
)
})
})
describeWithNets
(
'no memory leaks'
,
{
withAgeGenderNet
:
{
quantized
:
true
}
},
({
ageGenderNet
})
=>
{
describe
(
'forwardInput'
,
()
=>
{
it
(
'single image element'
,
async
()
=>
{
await
expectAllTensorsReleased
(
async
()
=>
{
const
netInput
=
new
NetInput
([
imgElAngry
])
const
{
age
,
gender
}
=
await
ageGenderNet
.
forwardInput
(
netInput
)
age
.
dispose
()
gender
.
dispose
()
})
})
it
(
'multiple image elements'
,
async
()
=>
{
await
expectAllTensorsReleased
(
async
()
=>
{
const
netInput
=
new
NetInput
([
imgElAngry
,
imgElAngry
])
const
{
age
,
gender
}
=
await
ageGenderNet
.
forwardInput
(
netInput
)
age
.
dispose
()
gender
.
dispose
()
})
})
it
(
'single tf.Tensor3D'
,
async
()
=>
{
const
tensor
=
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
imgElAngry
))
await
expectAllTensorsReleased
(
async
()
=>
{
const
{
age
,
gender
}
=
await
ageGenderNet
.
forwardInput
(
await
toNetInput
(
tensor
))
age
.
dispose
()
gender
.
dispose
()
})
tensor
.
dispose
()
})
it
(
'multiple tf.Tensor3Ds'
,
async
()
=>
{
const
tensors
=
[
imgElAngry
,
imgElAngry
,
imgElAngry
].
map
(
el
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
el
)))
await
expectAllTensorsReleased
(
async
()
=>
{
const
{
age
,
gender
}
=
await
ageGenderNet
.
forwardInput
(
await
toNetInput
(
tensors
))
age
.
dispose
()
gender
.
dispose
()
})
tensors
.
forEach
(
t
=>
t
.
dispose
())
})
it
(
'single batch size 1 tf.Tensor4Ds'
,
async
()
=>
{
const
tensor
=
tf
.
tidy
(()
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
imgElAngry
)).
expandDims
())
as
tf
.
Tensor4D
await
expectAllTensorsReleased
(
async
()
=>
{
const
{
age
,
gender
}
=
await
ageGenderNet
.
forwardInput
(
await
toNetInput
(
tensor
))
age
.
dispose
()
gender
.
dispose
()
})
tensor
.
dispose
()
})
it
(
'multiple batch size 1 tf.Tensor4Ds'
,
async
()
=>
{
const
tensors
=
[
imgElAngry
,
imgElAngry
,
imgElAngry
]
.
map
(
el
=>
tf
.
tidy
(()
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
el
)).
expandDims
()))
as
tf
.
Tensor4D
[]
await
expectAllTensorsReleased
(
async
()
=>
{
const
{
age
,
gender
}
=
await
ageGenderNet
.
forwardInput
(
await
toNetInput
(
tensors
))
age
.
dispose
()
gender
.
dispose
()
})
tensors
.
forEach
(
t
=>
t
.
dispose
())
})
})
describe
(
'predictExpressions'
,
()
=>
{
it
(
'single image element'
,
async
()
=>
{
await
expectAllTensorsReleased
(
async
()
=>
{
await
ageGenderNet
.
predictAgeAndGender
(
imgElAngry
)
})
})
it
(
'multiple image elements'
,
async
()
=>
{
await
expectAllTensorsReleased
(
async
()
=>
{
await
ageGenderNet
.
predictAgeAndGender
([
imgElAngry
,
imgElAngry
,
imgElAngry
])
})
})
it
(
'single tf.Tensor3D'
,
async
()
=>
{
const
tensor
=
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
imgElAngry
))
await
expectAllTensorsReleased
(
async
()
=>
{
await
ageGenderNet
.
predictAgeAndGender
(
tensor
)
})
tensor
.
dispose
()
})
it
(
'multiple tf.Tensor3Ds'
,
async
()
=>
{
const
tensors
=
[
imgElAngry
,
imgElAngry
,
imgElAngry
].
map
(
el
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
el
)))
await
expectAllTensorsReleased
(
async
()
=>
{
await
ageGenderNet
.
predictAgeAndGender
(
tensors
)
})
tensors
.
forEach
(
t
=>
t
.
dispose
())
})
it
(
'single batch size 1 tf.Tensor4Ds'
,
async
()
=>
{
const
tensor
=
tf
.
tidy
(()
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
imgElAngry
)).
expandDims
())
as
tf
.
Tensor4D
await
expectAllTensorsReleased
(
async
()
=>
{
await
ageGenderNet
.
predictAgeAndGender
(
tensor
)
})
tensor
.
dispose
()
})
it
(
'multiple batch size 1 tf.Tensor4Ds'
,
async
()
=>
{
const
tensors
=
[
imgElAngry
,
imgElAngry
,
imgElAngry
]
.
map
(
el
=>
tf
.
tidy
(()
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
el
)).
expandDims
()))
as
tf
.
Tensor4D
[]
await
expectAllTensorsReleased
(
async
()
=>
{
await
ageGenderNet
.
predictAgeAndGender
(
tensors
)
})
tensors
.
forEach
(
t
=>
t
.
dispose
())
})
})
})
})
test/tests/faceExpressionNet/faceExpressionNet.test.ts
View file @
5ff0c5c5
...
@@ -41,7 +41,7 @@ describeWithBackend('faceExpressionNet', () => {
...
@@ -41,7 +41,7 @@ describeWithBackend('faceExpressionNet', () => {
expect
(
resultSurprised
.
surprised
).
toBeGreaterThan
(
0.95
)
expect
(
resultSurprised
.
surprised
).
toBeGreaterThan
(
0.95
)
})
})
it
(
'computes face
landmark
s for batch of tf.Tensor3D'
,
async
()
=>
{
it
(
'computes face
expression
s for batch of tf.Tensor3D'
,
async
()
=>
{
const
inputs
=
[
imgElAngry
,
imgElSurprised
].
map
(
el
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
el
)))
const
inputs
=
[
imgElAngry
,
imgElSurprised
].
map
(
el
=>
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
el
)))
const
results
=
await
faceExpressionNet
.
predictExpressions
(
inputs
)
as
FaceExpressions
[]
const
results
=
await
faceExpressionNet
.
predictExpressions
(
inputs
)
as
FaceExpressions
[]
...
@@ -55,7 +55,7 @@ describeWithBackend('faceExpressionNet', () => {
...
@@ -55,7 +55,7 @@ describeWithBackend('faceExpressionNet', () => {
expect
(
resultSurprised
.
surprised
).
toBeGreaterThan
(
0.95
)
expect
(
resultSurprised
.
surprised
).
toBeGreaterThan
(
0.95
)
})
})
it
(
'computes face
landmark
s for batch of mixed inputs'
,
async
()
=>
{
it
(
'computes face
expression
s for batch of mixed inputs'
,
async
()
=>
{
const
inputs
=
[
imgElAngry
,
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
imgElSurprised
))]
const
inputs
=
[
imgElAngry
,
tf
.
browser
.
fromPixels
(
createCanvasFromMedia
(
imgElSurprised
))]
const
results
=
await
faceExpressionNet
.
predictExpressions
(
inputs
)
as
FaceExpressions
[]
const
results
=
await
faceExpressionNet
.
predictExpressions
(
inputs
)
as
FaceExpressions
[]
...
...
test/utils.ts
View file @
5ff0c5c5
...
@@ -2,6 +2,7 @@ import * as tf from '@tensorflow/tfjs-core';
...
@@ -2,6 +2,7 @@ import * as tf from '@tensorflow/tfjs-core';
import
*
as
faceapi
from
'../src'
;
import
*
as
faceapi
from
'../src'
;
import
{
FaceRecognitionNet
,
IPoint
,
IRect
,
Mtcnn
,
TinyYolov2
}
from
'../src/'
;
import
{
FaceRecognitionNet
,
IPoint
,
IRect
,
Mtcnn
,
TinyYolov2
}
from
'../src/'
;
import
{
AgeGenderNet
}
from
'../src/ageGenderNet/AgeGenderNet'
;
import
{
FaceDetection
}
from
'../src/classes/FaceDetection'
;
import
{
FaceDetection
}
from
'../src/classes/FaceDetection'
;
import
{
FaceLandmarks
}
from
'../src/classes/FaceLandmarks'
;
import
{
FaceLandmarks
}
from
'../src/classes/FaceLandmarks'
;
import
{
FaceExpressionNet
}
from
'../src/faceExpressionNet/FaceExpressionNet'
;
import
{
FaceExpressionNet
}
from
'../src/faceExpressionNet/FaceExpressionNet'
;
...
@@ -114,6 +115,7 @@ export type InjectNetArgs = {
...
@@ -114,6 +115,7 @@ export type InjectNetArgs = {
faceRecognitionNet
:
FaceRecognitionNet
faceRecognitionNet
:
FaceRecognitionNet
mtcnn
:
Mtcnn
mtcnn
:
Mtcnn
faceExpressionNet
:
FaceExpressionNet
faceExpressionNet
:
FaceExpressionNet
ageGenderNet
:
AgeGenderNet
tinyYolov2
:
TinyYolov2
tinyYolov2
:
TinyYolov2
}
}
...
@@ -129,6 +131,7 @@ export type DescribeWithNetsOptions = {
...
@@ -129,6 +131,7 @@ export type DescribeWithNetsOptions = {
withFaceRecognitionNet
?:
WithNetOptions
withFaceRecognitionNet
?:
WithNetOptions
withMtcnn
?:
WithNetOptions
withMtcnn
?:
WithNetOptions
withFaceExpressionNet
?:
WithNetOptions
withFaceExpressionNet
?:
WithNetOptions
withAgeGenderNet
?:
WithNetOptions
withTinyYolov2
?:
WithTinyYolov2Options
withTinyYolov2
?:
WithTinyYolov2Options
}
}
...
@@ -176,6 +179,7 @@ export function describeWithNets(
...
@@ -176,6 +179,7 @@ export function describeWithNets(
faceRecognitionNet
,
faceRecognitionNet
,
mtcnn
,
mtcnn
,
faceExpressionNet
,
faceExpressionNet
,
ageGenderNet
,
tinyYolov2
tinyYolov2
}
=
faceapi
.
nets
}
=
faceapi
.
nets
...
@@ -192,6 +196,7 @@ export function describeWithNets(
...
@@ -192,6 +196,7 @@ export function describeWithNets(
withFaceRecognitionNet
,
withFaceRecognitionNet
,
withMtcnn
,
withMtcnn
,
withFaceExpressionNet
,
withFaceExpressionNet
,
withAgeGenderNet
,
withTinyYolov2
withTinyYolov2
}
=
options
}
=
options
...
@@ -244,6 +249,13 @@ export function describeWithNets(
...
@@ -244,6 +249,13 @@ export function describeWithNets(
)
)
}
}
if
(
withAgeGenderNet
)
{
await
initNet
<
AgeGenderNet
>
(
ageGenderNet
,
!!
withAgeGenderNet
&&
!
withAgeGenderNet
.
quantized
&&
'age_gender_model.weights'
)
}
if
(
withTinyYolov2
||
withAllFacesTinyYolov2
)
{
if
(
withTinyYolov2
||
withAllFacesTinyYolov2
)
{
await
initNet
<
TinyYolov2
>
(
await
initNet
<
TinyYolov2
>
(
tinyYolov2
,
tinyYolov2
,
...
@@ -273,6 +285,7 @@ export function describeWithNets(
...
@@ -273,6 +285,7 @@ export function describeWithNets(
faceRecognitionNet
,
faceRecognitionNet
,
mtcnn
,
mtcnn
,
faceExpressionNet
,
faceExpressionNet
,
ageGenderNet
,
tinyYolov2
tinyYolov2
})
})
})
})
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment