Skip to content

Commit 907ca4b

Browse files
Merge branch 'age-gender' of https://github.com/justadudewhohacks/face-api.js into age-gender
2 parents d2056b0 + aecac6c commit 907ca4b

File tree

3 files changed

+232
-0
lines changed

3 files changed

+232
-0
lines changed

examples/examples-browser/public/js/commons.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ function renderNavBar(navbarId, exampleUri) {
6464
uri: 'webcam_face_expression_recognition',
6565
name: 'Webcam Face Expression Recognition'
6666
},
67+
{
68+
uri: 'webcam_age_and_gender_recognition',
69+
name: 'Webcam Age and Gender Recognition'
70+
},
6771
{
6872
uri: 'bbt_face_landmark_detection',
6973
name: 'BBT Face Landmark Detection'

examples/examples-browser/server.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ app.get('/video_face_tracking', (req, res) => res.sendFile(path.join(viewsDir, '
2626
app.get('/webcam_face_detection', (req, res) => res.sendFile(path.join(viewsDir, 'webcamFaceDetection.html')))
2727
app.get('/webcam_face_landmark_detection', (req, res) => res.sendFile(path.join(viewsDir, 'webcamFaceLandmarkDetection.html')))
2828
app.get('/webcam_face_expression_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'webcamFaceExpressionRecognition.html')))
29+
app.get('/webcam_age_and_gender_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'webcamAgeAndGenderRecognition.html')))
2930
app.get('/bbt_face_landmark_detection', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceLandmarkDetection.html')))
3031
app.get('/bbt_face_similarity', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceSimilarity.html')))
3132
app.get('/bbt_face_matching', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceMatching.html')))
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="face-api.js"></script>
5+
<script src="js/commons.js"></script>
6+
<script src="js/faceDetectionControls.js"></script>
7+
<link rel="stylesheet" href="styles.css">
8+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
9+
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
10+
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
11+
</head>
12+
<body>
13+
<div id="navbar"></div>
14+
<div class="center-content page-container">
15+
16+
<div class="progress" id="loader">
17+
<div class="indeterminate"></div>
18+
</div>
19+
<div style="position: relative" class="margin">
20+
<video onloadedmetadata="onPlay(this)" id="inputVideo" autoplay muted></video>
21+
<canvas id="overlay" />
22+
</div>
23+
24+
<div class="row side-by-side">
25+
26+
<!-- face_detector_selection_control -->
27+
<div id="face_detector_selection_control" class="row input-field" style="margin-right: 20px;">
28+
<select id="selectFaceDetector">
29+
<option value="ssd_mobilenetv1">SSD Mobilenet V1</option>
30+
<option value="tiny_face_detector">Tiny Face Detector</option>
31+
<option value="mtcnn">MTCNN</option>
32+
</select>
33+
<label>Select Face Detector</label>
34+
</div>
35+
<!-- face_detector_selection_control -->
36+
37+
<!-- check boxes -->
38+
<div class="row" style="width: 220px;">
39+
<input type="checkbox" id="hideBoundingBoxesCheckbox" onchange="onChangeHideBoundingBoxes(event)" />
40+
<label for="hideBoundingBoxesCheckbox">Hide Bounding Boxes</label>
41+
</div>
42+
<!-- check boxes -->
43+
44+
<!-- fps_meter -->
45+
<div id="fps_meter" class="row side-by-side">
46+
<div>
47+
<label for="time">Time:</label>
48+
<input disabled value="-" id="time" type="text" class="bold">
49+
<label for="fps">Estimated Fps:</label>
50+
<input disabled value="-" id="fps" type="text" class="bold">
51+
</div>
52+
</div>
53+
<!-- fps_meter -->
54+
55+
</div>
56+
57+
58+
<!-- ssd_mobilenetv1_controls -->
59+
<span id="ssd_mobilenetv1_controls">
60+
<div class="row side-by-side">
61+
<div class="row">
62+
<label for="minConfidence">Min Confidence:</label>
63+
<input disabled value="0.5" id="minConfidence" type="text" class="bold">
64+
</div>
65+
<button
66+
class="waves-effect waves-light btn"
67+
onclick="onDecreaseMinConfidence()"
68+
>
69+
<i class="material-icons left">-</i>
70+
</button>
71+
<button
72+
class="waves-effect waves-light btn"
73+
onclick="onIncreaseMinConfidence()"
74+
>
75+
<i class="material-icons left">+</i>
76+
</button>
77+
</div>
78+
</span>
79+
<!-- ssd_mobilenetv1_controls -->
80+
81+
<!-- tiny_face_detector_controls -->
82+
<span id="tiny_face_detector_controls">
83+
<div class="row side-by-side">
84+
<div class="row input-field" style="margin-right: 20px;">
85+
<select id="inputSize">
86+
<option value="" disabled selected>Input Size:</option>
87+
<option value="128">128 x 128</option>
88+
<option value="160">160 x 160</option>
89+
<option value="224">224 x 224</option>
90+
<option value="320">320 x 320</option>
91+
<option value="416">416 x 416</option>
92+
<option value="512">512 x 512</option>
93+
<option value="608">608 x 608</option>
94+
</select>
95+
<label>Input Size</label>
96+
</div>
97+
<div class="row">
98+
<label for="scoreThreshold">Score Threshold:</label>
99+
<input disabled value="0.5" id="scoreThreshold" type="text" class="bold">
100+
</div>
101+
<button
102+
class="waves-effect waves-light btn"
103+
onclick="onDecreaseScoreThreshold()"
104+
>
105+
<i class="material-icons left">-</i>
106+
</button>
107+
<button
108+
class="waves-effect waves-light btn"
109+
onclick="onIncreaseScoreThreshold()"
110+
>
111+
<i class="material-icons left">+</i>
112+
</button>
113+
</div>
114+
</span>
115+
<!-- tiny_face_detector_controls -->
116+
117+
<!-- mtcnn_controls -->
118+
<span id="mtcnn_controls">
119+
<div class="row side-by-side">
120+
<div class="row">
121+
<label for="minFaceSize">Minimum Face Size:</label>
122+
<input disabled value="20" id="minFaceSize" type="text" class="bold">
123+
</div>
124+
<button
125+
class="waves-effect waves-light btn"
126+
onclick="onDecreaseMinFaceSize()"
127+
>
128+
<i class="material-icons left">-</i>
129+
</button>
130+
<button
131+
class="waves-effect waves-light btn"
132+
onclick="onIncreaseMinFaceSize()"
133+
>
134+
<i class="material-icons left">+</i>
135+
</button>
136+
</div>
137+
</span>
138+
<!-- mtcnn_controls -->
139+
140+
</body>
141+
142+
<script>
143+
let forwardTimes = []
144+
let predictedAges = []
145+
let withBoxes = true
146+
147+
function onChangeHideBoundingBoxes(e) {
148+
withBoxes = !$(e.target).prop('checked')
149+
}
150+
151+
function updateTimeStats(timeInMs) {
152+
forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30)
153+
const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length
154+
$('#time').val(`${Math.round(avgTimeInMs)} ms`)
155+
$('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`)
156+
}
157+
158+
function interpolateAgePredictions(age) {
159+
predictedAges = [age].concat(predictedAges).slice(0, 30)
160+
const avgPredictedAge = predictedAges.reduce((total, a) => total + a) / predictedAges.length
161+
return avgPredictedAge
162+
}
163+
164+
async function onPlay() {
165+
const videoEl = $('#inputVideo').get(0)
166+
167+
if(videoEl.paused || videoEl.ended || !isFaceDetectionModelLoaded())
168+
return setTimeout(() => onPlay())
169+
170+
171+
const options = getFaceDetectorOptions()
172+
173+
const ts = Date.now()
174+
175+
const result = await faceapi.detectSingleFace(videoEl, options)
176+
.withAgeAndGender()
177+
178+
updateTimeStats(Date.now() - ts)
179+
180+
if (result) {
181+
const canvas = $('#overlay').get(0)
182+
const dims = faceapi.matchDimensions(canvas, videoEl, true)
183+
184+
const resizedResult = faceapi.resizeResults(result, dims)
185+
if (withBoxes) {
186+
faceapi.draw.drawDetections(canvas, resizedResult)
187+
}
188+
const { age, gender, genderProbability } = resizedResult
189+
190+
// interpolate gender predictions over last 30 frames
191+
// to make the displayed age more stable
192+
const interpolatedAge = interpolateAgePredictions(age)
193+
new faceapi.draw.DrawTextField(
194+
[
195+
`${faceapi.round(interpolatedAge, 0)} years`,
196+
`${gender} (${faceapi.round(genderProbability)})`
197+
],
198+
result.detection.box.bottomLeft
199+
).draw(canvas)
200+
}
201+
202+
setTimeout(() => onPlay())
203+
}
204+
205+
async function run() {
206+
// load face detection and face expression recognition models
207+
await changeFaceDetector(TINY_FACE_DETECTOR)
208+
await faceapi.nets.ageGenderNet.load('/')
209+
changeInputSize(224)
210+
211+
// try to access users webcam and stream the images
212+
// to the video element
213+
const stream = await navigator.mediaDevices.getUserMedia({ video: {} })
214+
const videoEl = $('#inputVideo').get(0)
215+
videoEl.srcObject = stream
216+
}
217+
218+
function updateResults() {}
219+
220+
$(document).ready(function() {
221+
renderNavBar('#navbar', 'webcam_age_and_gender_recognition')
222+
initFaceDetectionControls()
223+
run()
224+
})
225+
</script>
226+
</body>
227+
</html>

0 commit comments

Comments
 (0)