Skip to content

Commit e0b5301

Browse files
examples for face extraction
1 parent cdd2c49 commit e0b5301

File tree

7 files changed

+264
-34
lines changed

7 files changed

+264
-34
lines changed

examples/public/commons.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,35 @@ async function initFaceRecognitionNet() {
2424
return facerecognition.faceRecognitionNet(weights)
2525
}
2626

27+
// fetch first image of each class and compute their descriptors
28+
async function initTrainDescriptorsByClass(net) {
29+
return Promise.all(classes.map(
30+
async className => {
31+
const img = await facerecognition.bufferToImage(
32+
await fetchImage(getFaceImageUri(className, 1))
33+
)
34+
const descriptor = await net.computeFaceDescriptor(img)
35+
return {
36+
descriptor,
37+
className
38+
}
39+
}
40+
))
41+
}
42+
43+
function getBestMatch(allDescriptors, queryDescriptor) {
44+
return allDescriptors
45+
.map(
46+
({ descriptor, className }) => ({
47+
distance: facerecognition.round(
48+
facerecognition.euclideanDistance(descriptor, queryDescriptor)
49+
),
50+
className
51+
})
52+
)
53+
.reduce((best, curr) => best.distance < curr.distance ? best : curr)
54+
}
55+
2756
function renderNavBar(navbarId, exampleUri) {
2857
const examples = [
2958
{
@@ -41,6 +70,14 @@ function renderNavBar(navbarId, exampleUri) {
4170
{
4271
uri: 'face_similarity',
4372
name: 'Face Similarity'
73+
},
74+
{
75+
uri: 'detect_and_draw_faces',
76+
name: 'Detect and Draw Faces'
77+
},
78+
{
79+
uri: 'detect_and_recognize_faces',
80+
name: 'Detect and Recognize Faces'
4481
}
4582
]
4683

examples/public/styles.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,8 @@
3737
position: absolute;
3838
top: 0;
3939
left: 0;
40+
}
41+
42+
#facesContainer canvas {
43+
margin: 10px;
4044
}

examples/server.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ app.get('/face_detection', (req, res) => res.sendFile(path.join(viewsDir, 'faceD
1515
app.get('/face_detection_video', (req, res) => res.sendFile(path.join(viewsDir, 'faceDetectionVideo.html')))
1616
app.get('/face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'faceRecognition.html')))
1717
app.get('/face_similarity', (req, res) => res.sendFile(path.join(viewsDir, 'faceSimilarity.html')))
18+
app.get('/detect_and_draw_faces', (req, res) => res.sendFile(path.join(viewsDir, 'detectAndDrawFaces.html')))
19+
app.get('/detect_and_recognize_faces', (req, res) => res.sendFile(path.join(viewsDir, 'detectAndRecognizeFaces.html')))
1820

1921
app.listen(3000, () => console.log('Listening on port 3000!'))
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="face-recognition.js"></script>
5+
<script src="axios.min.js"></script>
6+
<script src="commons.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 class="center-content page-container">
14+
<div id="navbar"></div>
15+
<div class="progress" id="loader">
16+
<div class="indeterminate"></div>
17+
</div>
18+
<div style="position: relative" class="margin">
19+
<img id="inputImg" src="" style="max-width: 800px;" />
20+
<canvas id="overlay" />
21+
</div>
22+
<div id="facesContainer"></div>
23+
<div class="row side-by-side">
24+
<div id="selectList"></div>
25+
<div class="row">
26+
<label for="minConfidence">Min Confidence:</label>
27+
<input disabled value="0.7" id="minConfidence" type="text" class="bold">
28+
</div>
29+
<button
30+
class="waves-effect waves-light btn"
31+
onclick="onDecreaseThreshold()"
32+
>
33+
<i class="material-icons left">-</i>
34+
</button>
35+
<button
36+
class="waves-effect waves-light btn"
37+
onclick="onIncreaseThreshold()"
38+
>
39+
<i class="material-icons left">+</i>
40+
</button>
41+
</div>
42+
</div>
43+
44+
<script>
45+
let minConfidence = 0.7
46+
let net
47+
48+
function onIncreaseThreshold() {
49+
minConfidence = Math.min(facerecognition.round(minConfidence + 0.1), 1.0)
50+
$('#minConfidence').val(minConfidence)
51+
updateResults()
52+
}
53+
54+
function onDecreaseThreshold() {
55+
minConfidence = Math.max(facerecognition.round(minConfidence - 0.1), 0.1)
56+
$('#minConfidence').val(minConfidence)
57+
updateResults()
58+
}
59+
60+
async function updateResults() {
61+
const inputImgEl = $('#inputImg').get(0)
62+
const { width, height } = inputImgEl
63+
const canvas = $('#overlay').get(0)
64+
canvas.width = width
65+
canvas.height = height
66+
67+
const input = new facerecognition.NetInput(inputImgEl)
68+
const detections = await net.locateFaces(input, minConfidence)
69+
facerecognition.drawDetection('overlay', detections.map(det => det.forSize(width, height)))
70+
71+
const faceImages = await facerecognition.extractFaces(input.canvases[0], detections)
72+
$('#facesContainer').empty()
73+
faceImages.forEach(canvas => $('#facesContainer').append(canvas))
74+
}
75+
76+
async function onSelectionChanged(uri) {
77+
const imgBuf = await fetchImage(uri)
78+
$(`#inputImg`).get(0).src = (await facerecognition.bufferToImage(imgBuf)).src
79+
updateResults()
80+
}
81+
82+
async function run() {
83+
net = await initFaceDetectionNet()
84+
$('#loader').hide()
85+
onSelectionChanged($('#selectList select').val())
86+
}
87+
88+
$(document).ready(function() {
89+
renderNavBar('#navbar', 'detect_and_draw_faces')
90+
renderImageSelectList(
91+
'#selectList',
92+
async (uri) => {
93+
await onSelectionChanged(uri)
94+
},
95+
'bbt1.jpg'
96+
)
97+
run()
98+
})
99+
</script>
100+
</body>
101+
</html>
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="face-recognition.js"></script>
5+
<script src="axios.min.js"></script>
6+
<script src="commons.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 class="center-content page-container">
14+
<div id="navbar"></div>
15+
<div class="progress" id="loader">
16+
<div class="indeterminate"></div>
17+
</div>
18+
<div style="position: relative" class="margin">
19+
<img id="inputImg" src="" style="max-width: 800px;" />
20+
<canvas id="overlay" />
21+
</div>
22+
<div class="row side-by-side">
23+
<div id="selectList"></div>
24+
<div class="row">
25+
<label for="minConfidence">Min Confidence:</label>
26+
<input disabled value="0.7" id="minConfidence" type="text" class="bold">
27+
</div>
28+
<button
29+
class="waves-effect waves-light btn"
30+
onclick="onDecreaseThreshold()"
31+
>
32+
<i class="material-icons left">-</i>
33+
</button>
34+
<button
35+
class="waves-effect waves-light btn"
36+
onclick="onIncreaseThreshold()"
37+
>
38+
<i class="material-icons left">+</i>
39+
</button>
40+
</div>
41+
</div>
42+
43+
<script>
44+
const threshold = 0.6
45+
let minConfidence = 0.7
46+
let detectionNet, recognitionNet
47+
let trainDescriptorsByClass = []
48+
49+
function onIncreaseThreshold() {
50+
minConfidence = Math.min(facerecognition.round(minConfidence + 0.1), 1.0)
51+
$('#minConfidence').val(minConfidence)
52+
updateResults()
53+
}
54+
55+
function onDecreaseThreshold() {
56+
minConfidence = Math.max(facerecognition.round(minConfidence - 0.1), 0.1)
57+
$('#minConfidence').val(minConfidence)
58+
updateResults()
59+
}
60+
61+
async function updateResults() {
62+
const inputImgEl = $('#inputImg').get(0)
63+
const { width, height } = inputImgEl
64+
const canvas = $('#overlay').get(0)
65+
canvas.width = width
66+
canvas.height = height
67+
68+
const input = new facerecognition.NetInput(inputImgEl)
69+
const detections = await detectionNet.locateFaces(input, minConfidence)
70+
71+
const detectionsForSize = detections.map(det => det.forSize(width, height))
72+
facerecognition.drawDetection('overlay', detectionsForSize, { withScore: false })
73+
74+
const faceTensors = await facerecognition.extractFaceTensors(input, detections)
75+
const descriptors = await Promise.all(faceTensors.map(t => recognitionNet.computeFaceDescriptor(t)))
76+
77+
// free memory for face image tensors after we computed their descriptors
78+
faceTensors.forEach(t => t.dispose())
79+
80+
descriptors.forEach((descriptor, i) => {
81+
const bestMatch = getBestMatch(trainDescriptorsByClass, descriptor)
82+
const text = `${bestMatch.distance < threshold ? bestMatch.className : 'unkown'} (${bestMatch.distance})`
83+
const { x, y } = detectionsForSize[i].box
84+
facerecognition.drawText(canvas.getContext('2d'), x, y, text, facerecognition.getDefaultDrawOptions())
85+
})
86+
}
87+
88+
async function onSelectionChanged(uri) {
89+
const imgBuf = await fetchImage(uri)
90+
$(`#inputImg`).get(0).src = (await facerecognition.bufferToImage(imgBuf)).src
91+
updateResults()
92+
}
93+
94+
async function run() {
95+
detectionNet = await initFaceDetectionNet()
96+
recognitionNet = await initFaceRecognitionNet()
97+
trainDescriptorsByClass = await initTrainDescriptorsByClass(recognitionNet)
98+
$('#loader').hide()
99+
onSelectionChanged($('#selectList select').val())
100+
}
101+
102+
$(document).ready(function() {
103+
renderNavBar('#navbar', 'detect_and_recognize_faces')
104+
renderImageSelectList(
105+
'#selectList',
106+
async (uri) => {
107+
await onSelectionChanged(uri)
108+
},
109+
'bbt1.jpg'
110+
)
111+
run()
112+
})
113+
</script>
114+
</body>
115+
</html>

examples/views/faceDetection.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@
5757
}
5858

5959
async function updateResults() {
60-
const input = new facerecognition.NetInput('inputImg')
61-
const { width, height } = input
60+
const inputImgEl = $('#inputImg').get(0)
61+
const { width, height } = inputImgEl
6262
const canvas = $('#overlay').get(0)
6363
canvas.width = width
6464
canvas.height = height
6565

66+
const input = new facerecognition.NetInput(inputImgEl)
6667
result = await net.locateFaces(input, minConfidence)
6768
facerecognition.drawDetection('overlay', result.map(det => det.forSize(width, height)))
6869
}

examples/views/faceRecognition.html

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -108,30 +108,6 @@
108108
getImg().src = src
109109
}
110110

111-
async function loadTrainingImages() {
112-
return await Promise.all(classes.map(
113-
async className => ({
114-
img: await facerecognition.bufferToImage(
115-
await fetchImage(getFaceImageUri(className, 1))
116-
),
117-
className
118-
})
119-
))
120-
}
121-
122-
function getBestMatch(queryDescriptor) {
123-
return trainDescriptorsByClass
124-
.map(
125-
({ descriptor, className }) => ({
126-
distance: facerecognition.round(
127-
facerecognition.euclideanDistance(descriptor, queryDescriptor)
128-
),
129-
className
130-
})
131-
)
132-
.reduce((best, curr) => best.distance < curr.distance ? best : curr)
133-
}
134-
135111
async function runFaceRecognition() {
136112
async function next() {
137113
const imgBuf = await fetchImage(getFaceImageUri(classes[currClassIdx], currImageIdx))
@@ -143,7 +119,7 @@
143119
const descriptor = await net.computeFaceDescriptor(input)
144120
displayTimeStats(Date.now() - ts)
145121

146-
const bestMatch = getBestMatch(descriptor)
122+
const bestMatch = getBestMatch(trainDescriptorsByClass, descriptor)
147123
$('#prediction').val(`${bestMatch.distance < threshold ? bestMatch.className : 'unkown'} (${bestMatch.distance})`)
148124

149125
currImageIdx = currClassIdx === (classes.length - 1)
@@ -164,13 +140,7 @@
164140
net = await initFaceRecognitionNet()
165141
setStatusText('computing initial descriptors...')
166142

167-
const trainImgs = await loadTrainingImages()
168-
trainDescriptorsByClass = await Promise.all(trainImgs.map(
169-
async ({ className, img }) => ({
170-
descriptor: await net.computeFaceDescriptor(img),
171-
className
172-
})
173-
))
143+
trainDescriptorsByClass = await initTrainDescriptorsByClass(net)
174144
$('#loader').hide()
175145

176146
runFaceRecognition()

0 commit comments

Comments
 (0)