@@ -15,7 +15,7 @@ function fromData(input: number[]): tf.Tensor4D {
15
15
throw new Error ( `invalid input size: ${ dim } x${ dim } x3 (array length: ${ input . length } )` )
16
16
}
17
17
18
- return tf . tensor4d ( input as number [ ] , [ 1 , 580 , 580 , 3 ] )
18
+ return tf . tensor4d ( input as number [ ] , [ 1 , dim , dim , 3 ] )
19
19
}
20
20
21
21
function fromImageData ( input : ImageData [ ] ) {
@@ -31,24 +31,30 @@ function fromImageData(input: ImageData[]) {
31
31
return tf . cast ( tf . concat ( imgTensors , 0 ) , 'float32' )
32
32
}
33
33
34
+ function getImgTensor ( input : ImageData | ImageData [ ] | number [ ] ) {
35
+ return tf . tidy ( ( ) => {
36
+
37
+ const imgDataArray = input instanceof ImageData
38
+ ? [ input ]
39
+ : (
40
+ input [ 0 ] instanceof ImageData
41
+ ? input as ImageData [ ]
42
+ : null
43
+ )
44
+
45
+ return imgDataArray !== null
46
+ ? fromImageData ( imgDataArray )
47
+ : fromData ( input as number [ ] )
48
+
49
+ } )
50
+ }
51
+
34
52
export function faceDetectionNet ( weights : Float32Array ) {
35
53
const params = extractParams ( weights )
36
54
37
- async function forward ( input : ImageData | ImageData [ ] | number [ ] ) {
55
+ function forwardTensor ( imgTensor : tf . Tensor4D ) {
38
56
return tf . tidy ( ( ) => {
39
57
40
- const imgDataArray = input instanceof ImageData
41
- ? [ input ]
42
- : (
43
- input [ 0 ] instanceof ImageData
44
- ? input as ImageData [ ]
45
- : null
46
- )
47
-
48
- const imgTensor = imgDataArray !== null
49
- ? fromImageData ( imgDataArray )
50
- : fromData ( input as number [ ] )
51
-
52
58
const resized = resizeLayer ( imgTensor ) as tf . Tensor4D
53
59
const features = mobileNetV1 ( resized , params . mobilenetv1_params )
54
60
@@ -57,14 +63,54 @@ export function faceDetectionNet(weights: Float32Array) {
57
63
classPredictions
58
64
} = predictionLayer ( features . out , features . conv11 , params . prediction_layer_params )
59
65
60
- const decoded = outputLayer ( boxPredictions , classPredictions , params . output_layer_params )
66
+ return outputLayer ( boxPredictions , classPredictions , params . output_layer_params )
67
+ } )
68
+ }
69
+
70
+ // TODO debug output
71
+ function forward ( input : ImageData | ImageData [ ] | number [ ] ) {
72
+ return tf . tidy (
73
+ ( ) => forwardTensor ( getImgTensor ( input ) )
74
+ )
75
+ }
61
76
62
- return decoded
77
+ async function locateFaces (
78
+ input : ImageData | ImageData [ ] | number [ ] ,
79
+ minConfidence : number = 0.8
80
+ ) {
81
+ const imgTensor = getImgTensor ( input )
82
+
83
+ const [ _ , height , width ] = imgTensor . shape
84
+
85
+ const {
86
+ boxes : _boxes ,
87
+ scores : _scores
88
+ } = forwardTensor ( imgTensor )
89
+
90
+ // TODO batches
91
+ const boxes = _boxes [ 0 ]
92
+ const scores = _scores [ 0 ]
93
+
94
+ // TODO find a better way to filter by minConfidence
95
+ const data = await scores . data ( )
96
+
97
+ return Array . from ( data )
98
+ . map ( ( score , idx ) => ( { score, idx } ) )
99
+ . filter ( ( { score } ) => minConfidence < score )
100
+ . map ( ( { score, idx } ) => ( {
101
+ score,
102
+ box : {
103
+ left : Math . max ( 0 , width * boxes . get ( idx , 0 ) ) ,
104
+ right : Math . min ( width , width * boxes . get ( idx , 1 ) ) ,
105
+ top : Math . max ( 0 , height * boxes . get ( idx , 2 ) ) ,
106
+ bottom : Math . min ( height , height * boxes . get ( idx , 3 ) )
107
+ }
108
+ } ) )
63
109
64
- } )
65
110
}
66
111
67
112
return {
68
- forward
113
+ forward,
114
+ locateFaces
69
115
}
70
116
}
0 commit comments