Skip to content

Commit 2f50aa1

Browse files
added allFaceTinyYolov2 + tiny yolov2 face recognition example
1 parent 3e8ae6c commit 2f50aa1

12 files changed

+405
-38
lines changed

examples/public/commons.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ function renderNavBar(navbarId, exampleUri) {
146146
uri: 'tiny_yolov2_face_detection_webcam',
147147
name: 'Tiny Yolov2 Face Detection Webcam'
148148
},
149+
{
150+
uri: 'tiny_yolov2_face_recognition',
151+
name: 'Tiny Yolov2 Face Recognition'
152+
},
149153
{
150154
uri: 'batch_face_landmarks',
151155
name: 'Batch Face Landmarks'

examples/server.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ app.get('/mtcnn_face_recognition_webcam', (req, res) => res.sendFile(path.join(v
3232
app.get('/tiny_yolov2_face_detection', (req, res) => res.sendFile(path.join(viewsDir, 'tinyYolov2FaceDetection.html')))
3333
app.get('/tiny_yolov2_face_detection_video', (req, res) => res.sendFile(path.join(viewsDir, 'tinyYolov2FaceDetectionVideo.html')))
3434
app.get('/tiny_yolov2_face_detection_webcam', (req, res) => res.sendFile(path.join(viewsDir, 'tinyYolov2FaceDetectionWebcam.html')))
35+
app.get('/tiny_yolov2_face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'tinyYolov2FaceRecognition.html')))
3536
app.get('/batch_face_landmarks', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceLandmarks.html')))
3637
app.get('/batch_face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceRecognition.html')))
3738

examples/views/tinyYolov2FaceDetectionWebcam.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
}
129129

130130
$(document).ready(function() {
131-
renderNavBar('#navbar', 'tiny_yolov2_face_detection_video')
131+
renderNavBar('#navbar', 'tiny_yolov2_face_detection_webcam')
132132

133133
const sizeTypeSelect = $('#sizeType')
134134
sizeTypeSelect.val(sizeType)
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="face-api.js"></script>
5+
<script src="commons.js"></script>
6+
<link rel="stylesheet" href="styles.css">
7+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
8+
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
9+
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
10+
</head>
11+
<body>
12+
<div id="navbar"></div>
13+
<div class="center-content page-container">
14+
<div class="progress" id="loader">
15+
<div class="indeterminate"></div>
16+
</div>
17+
<div style="position: relative" class="margin">
18+
<img id="inputImg" src="" style="max-width: 800px;" />
19+
<canvas id="overlay" />
20+
</div>
21+
<div class="row side-by-side">
22+
<div id="selectList"></div>
23+
<div class="row">
24+
<label for="imgUrlInput">Get image from URL:</label>
25+
<input id="imgUrlInput" type="text" class="bold">
26+
</div>
27+
<button
28+
class="waves-effect waves-light btn"
29+
onclick="loadImageFromUrl()"
30+
>
31+
Ok
32+
</button>
33+
<p>
34+
<input type="checkbox" id="useBatchProcessing" onchange="onChangeUseBatchProcessing(event)" />
35+
<label for="useBatchProcessing">Use Batch Processing</label>
36+
</p>
37+
</div>
38+
<div class="row side-by-side">
39+
<div class="row input-field" style="margin-right: 20px;">
40+
<select id="sizeType">
41+
<option value="" disabled selected>Input Size:</option>
42+
<option value="xs">XS: 224 x 224</option>
43+
<option value="sm">SM: 320 x 320</option>
44+
<option value="md">MD: 416 x 416</option>
45+
<option value="lg">LG: 608 x 608</option>
46+
</select>
47+
<label>Input Size</label>
48+
</div>
49+
<div class="row">
50+
<label for="scoreThreshold">Score Threshold:</label>
51+
<input disabled value="0.5" id="scoreThreshold" type="text" class="bold">
52+
</div>
53+
<button
54+
class="waves-effect waves-light btn"
55+
onclick="onDecreaseThreshold()"
56+
>
57+
<i class="material-icons left">-</i>
58+
</button>
59+
<button
60+
class="waves-effect waves-light btn"
61+
onclick="onIncreaseThreshold()"
62+
>
63+
<i class="material-icons left">+</i>
64+
</button>
65+
</div>
66+
<div class="row side-by-side">
67+
<div class="row">
68+
<label for="maxDistance">Max Descriptor Distance:</label>
69+
<input disabled value="0.6" id="maxDistance" type="text" class="bold">
70+
</div>
71+
<button
72+
class="waves-effect waves-light btn button-sm"
73+
onclick="onDecreaseMaxDistance()"
74+
>
75+
<i class="material-icons left">-</i>
76+
</button>
77+
<button
78+
class="waves-effect waves-light btn button-sm"
79+
onclick="onIncreaseMaxDistance()"
80+
>
81+
<i class="material-icons left">+</i>
82+
</button>
83+
</div>
84+
</div>
85+
86+
<script>
87+
let maxDistance = 0.6
88+
let useBatchProcessing = false
89+
let trainDescriptorsByClass = []
90+
let scoreThreshold = 0.5
91+
let sizeType = 'lg'
92+
93+
function onIncreaseThreshold() {
94+
scoreThreshold = Math.min(faceapi.round(scoreThreshold + 0.1), 1.0)
95+
$('#scoreThreshold').val(scoreThreshold)
96+
updateResults()
97+
}
98+
99+
function onDecreaseThreshold() {
100+
scoreThreshold = Math.max(faceapi.round(scoreThreshold - 0.1), 0.1)
101+
$('#scoreThreshold').val(scoreThreshold)
102+
updateResults()
103+
}
104+
105+
function onSizeTypeChanged(e, c) {
106+
sizeType = e.target.value
107+
$('#sizeType').val(sizeType)
108+
updateResults()
109+
}
110+
111+
function onChangeUseBatchProcessing(e) {
112+
useBatchProcessing = $(e.target).prop('checked')
113+
}
114+
115+
function onIncreaseMaxDistance() {
116+
maxDistance = Math.min(faceapi.round(maxDistance + 0.1), 1.0)
117+
$('#maxDistance').val(maxDistance)
118+
updateResults()
119+
}
120+
121+
function onDecreaseMaxDistance() {
122+
maxDistance = Math.max(faceapi.round(maxDistance - 0.1), 0.1)
123+
$('#maxDistance').val(maxDistance)
124+
updateResults()
125+
}
126+
127+
async function loadImageFromUrl(url) {
128+
const img = await requestExternalImage($('#imgUrlInput').val())
129+
$('#inputImg').get(0).src = img.src
130+
updateResults()
131+
}
132+
133+
async function updateResults() {
134+
const inputImgEl = $('#inputImg').get(0)
135+
const { width, height } = inputImgEl
136+
const canvas = $('#overlay').get(0)
137+
canvas.width = width
138+
canvas.height = height
139+
140+
const forwardParams = {
141+
inputSize: sizeType,
142+
scoreThreshold
143+
}
144+
145+
const fullFaceDescriptions = (await faceapi.allFacesTinyYolov2(inputImgEl, forwardParams, useBatchProcessing))
146+
.map(fd => fd.forSize(width, height))
147+
148+
fullFaceDescriptions.forEach(({ detection, descriptor }) => {
149+
faceapi.drawDetection('overlay', [detection], { withScore: false })
150+
const bestMatch = getBestMatch(trainDescriptorsByClass, descriptor)
151+
const text = `${bestMatch.distance < maxDistance ? bestMatch.className : 'unkown'} (${bestMatch.distance})`
152+
const { x, y, height: boxHeight } = detection.getBox()
153+
faceapi.drawText(
154+
canvas.getContext('2d'),
155+
x,
156+
y + boxHeight,
157+
text,
158+
Object.assign(faceapi.getDefaultDrawOptions(), { color: 'red', fontSize: 16 })
159+
)
160+
})
161+
}
162+
163+
async function onSelectionChanged(uri) {
164+
const imgBuf = await fetchImage(uri)
165+
$(`#inputImg`).get(0).src = (await faceapi.bufferToImage(imgBuf)).src
166+
updateResults()
167+
}
168+
169+
async function run() {
170+
await faceapi.loadTinyYolov2Model('/')
171+
await faceapi.loadFaceLandmarkModel('/')
172+
await faceapi.loadFaceRecognitionModel('/')
173+
trainDescriptorsByClass = await initTrainDescriptorsByClass(faceapi.recognitionNet, 1)
174+
$('#loader').hide()
175+
onSelectionChanged($('#selectList select').val())
176+
}
177+
178+
$(document).ready(function() {
179+
renderNavBar('#navbar', 'tiny_yolov2_face_recognition')
180+
renderImageSelectList(
181+
'#selectList',
182+
async (uri) => {
183+
await onSelectionChanged(uri)
184+
},
185+
'bbt1.jpg'
186+
)
187+
188+
const sizeTypeSelect = $('#sizeType')
189+
sizeTypeSelect.val(sizeType)
190+
sizeTypeSelect.on('change', onSizeTypeChanged)
191+
sizeTypeSelect.material_select()
192+
run()
193+
})
194+
</script>
195+
</body>
196+
</html>

src/NetInput.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export class NetInput {
166166
}
167167

168168
/**
169-
* By setting the isManaged flag, all newly created tensors will be automatically
169+
* By setting the isManaged flag, all newly created tensors will be
170170
* automatically disposed after the batch tensor has been created
171171
*/
172172
public managed() {

src/allFacesFactory.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { TinyYolov2 } from '.';
12
import { extractFaceTensors } from './extractFaceTensors';
3+
import { FaceDetection } from './FaceDetection';
24
import { FaceDetectionNet } from './faceDetectionNet/FaceDetectionNet';
35
import { FaceLandmarkNet } from './faceLandmarkNet/FaceLandmarkNet';
46
import { FaceLandmarks68 } from './faceLandmarkNet/FaceLandmarks68';
@@ -7,6 +9,7 @@ import { FullFaceDescription } from './FullFaceDescription';
79
import { Mtcnn } from './mtcnn/Mtcnn';
810
import { MtcnnForwardParams } from './mtcnn/types';
911
import { Rect } from './Rect';
12+
import { TinyYolov2ForwardParams } from './tinyYolov2/types';
1013
import { TNetInput } from './types';
1114

1215
function computeDescriptorsFactory(
@@ -27,20 +30,19 @@ function computeDescriptorsFactory(
2730
}
2831
}
2932

30-
export function allFacesFactory(
31-
detectionNet: FaceDetectionNet,
33+
function allFacesFactory(
34+
detectFaces: (input: TNetInput) => Promise<FaceDetection[]>,
3235
landmarkNet: FaceLandmarkNet,
3336
recognitionNet: FaceRecognitionNet
3437
) {
3538
const computeDescriptors = computeDescriptorsFactory(recognitionNet)
3639

3740
return async function(
3841
input: TNetInput,
39-
minConfidence: number = 0.8,
4042
useBatchProcessing: boolean = false
4143
): Promise<FullFaceDescription[]> {
4244

43-
const detections = await detectionNet.locateFaces(input, minConfidence)
45+
const detections = await detectFaces(input)
4446
const faceTensors = await extractFaceTensors(input, detections)
4547

4648
const faceLandmarksByFace = useBatchProcessing
@@ -68,6 +70,38 @@ export function allFacesFactory(
6870
}
6971
}
7072

73+
export function allFacesSsdMobilenetv1Factory(
74+
ssdMobilenetv1: FaceDetectionNet,
75+
landmarkNet: FaceLandmarkNet,
76+
recognitionNet: FaceRecognitionNet
77+
) {
78+
return async function(
79+
input: TNetInput,
80+
minConfidence: number = 0.8,
81+
useBatchProcessing: boolean = false
82+
): Promise<FullFaceDescription[]> {
83+
const detectFaces = (input: TNetInput) => ssdMobilenetv1.locateFaces(input, minConfidence)
84+
const allFaces = allFacesFactory(detectFaces, landmarkNet, recognitionNet)
85+
return allFaces(input, useBatchProcessing)
86+
}
87+
}
88+
89+
export function allFacesTinyYolov2Factory(
90+
tinyYolov2: TinyYolov2,
91+
landmarkNet: FaceLandmarkNet,
92+
recognitionNet: FaceRecognitionNet
93+
) {
94+
return async function(
95+
input: TNetInput,
96+
forwardParams: TinyYolov2ForwardParams = {},
97+
useBatchProcessing: boolean = false
98+
): Promise<FullFaceDescription[]> {
99+
const detectFaces = (input: TNetInput) => tinyYolov2.locateFaces(input, forwardParams)
100+
const allFaces = allFacesFactory(detectFaces, landmarkNet, recognitionNet)
101+
return allFaces(input, useBatchProcessing)
102+
}
103+
}
104+
71105
export function allFacesMtcnnFactory(
72106
mtcnn: Mtcnn,
73107
recognitionNet: FaceRecognitionNet

0 commit comments

Comments
 (0)