Commit 20f129e2 by vincent

face detection example

parent 331e323a
...@@ -8,47 +8,134 @@ function getFaceImageUri(className, idx) { ...@@ -8,47 +8,134 @@ function getFaceImageUri(className, idx) {
return `images/${className}/${className}${idx}.png` return `images/${className}/${className}${idx}.png`
} }
async function initNet() { async function fetchImage(uri) {
return (await axios.get(uri, { responseType: 'blob' })).data
}
function round(num) {
return Math.floor(num * 100) / 100
}
function getElement(arg) {
if (typeof arg === 'string') {
return document.getElementById(arg)
}
return arg
}
async function initFaceDetectionNet() {
const res = await axios.get('face_detection_model.weights', { responseType: 'arraybuffer' })
const weights = new Float32Array(res.data)
return facerecognition.faceDetectionNet(weights)
}
async function initFaceRecognitionNet() {
const res = await axios.get('face_recognition_model.weights', { responseType: 'arraybuffer' }) const res = await axios.get('face_recognition_model.weights', { responseType: 'arraybuffer' })
const weights = new Float32Array(res.data) const weights = new Float32Array(res.data)
return facerecognition.faceRecognitionNet(weights) return facerecognition.faceRecognitionNet(weights)
} }
function bufferToImgSrc(buf) { function drawImgToCanvas(canvasArg, imgArg) {
return new Promise((resolve, reject) => { const canvas = getElement(canvasArg)
const reader = new window.FileReader() const img = getElement(imgArg)
reader.onload = () => resolve(reader.result) canvas.width = img.width
reader.onerror = reject canvas.height = img.height
reader.readAsDataURL(buf) const ctx = canvas.getContext('2d')
}) ctx.drawImage(img, 0, 0, img.width, img.height)
return ctx
} }
function imgSrcToData(src) { function imgSrcToImageData(src) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas')
canvas.width = 150
canvas.height = 150
const ctx = canvas.getContext('2d')
const img = new Image() const img = new Image()
img.onload = function() { img.onload = function() {
ctx.drawImage(img, 0, 0) const ctx = drawImgToCanvas(document.createElement('canvas'), img)
resolve(ctx.getImageData(0, 0, 150, 150)) resolve(ctx.getImageData(0, 0, img.width, img.height))
} }
img.onerror = reject img.onerror = reject
img.src = src img.src = src
}) })
} }
function bufferToImgSrc(buf) {
return new Promise((resolve, reject) => {
const reader = new window.FileReader()
reader.onload = () => resolve(reader.result)
reader.onerror = reject
reader.readAsDataURL(buf)
})
}
async function bufferToImageData(buf) { async function bufferToImageData(buf) {
return imgSrcToData(await bufferToImgSrc(buf)) return imgSrcToImageData(await bufferToImgSrc(buf))
} }
async function fetchImage(uri) { function drawBox(canvasArg, x, y, w, h, lineWidth = 2, color = 'blue') {
return (await axios.get(uri, { responseType: 'blob' })).data const canvas = getElement(canvasArg)
const ctx = canvas.getContext('2d')
ctx.strokeStyle = color
ctx.lineWidth = lineWidth
ctx.strokeRect(x, y, w, h)
} }
function round(num) { function drawText(canvasArg, x, y, text, fontSize = 20, fontStyle = 'Georgia', color = 'blue') {
return Math.floor(num * 100) / 100 const canvas = getElement(canvasArg)
const ctx = canvas.getContext('2d')
ctx.fillStyle = color
ctx.font = fontSize + 'px ' + fontStyle
ctx.fillText(text, x, y)
}
function drawDetection(canvasArg, detection, options = {}) {
const canvas = getElement(canvasArg)
const detectionArray = Array.isArray(detection)
? detection
: [detection]
detectionArray.forEach((det) => {
const {
score,
box
} = det
const {
left,
right,
top,
bottom
} = box
const {
color,
lineWidth = 2,
fontSize = 20,
fontStyle,
withScore = true
} = options
const padText = 2 + lineWidth
drawBox(
canvas,
left,
top,
right - left,
bottom - top,
lineWidth,
color
)
if (withScore) {
drawText(
canvas,
left + padText,
top + (fontSize * 0.6) + padText,
round(score),
fontSize,
fontStyle,
color
)
}
})
} }
function renderNavBar(navbarId, exampleUri) { function renderNavBar(navbarId, exampleUri) {
......
.page-container { .page-container {
position: fixed;
left: 0; left: 0;
right: 0; right: 0;
margin: auto; margin: auto;
...@@ -29,9 +28,4 @@ ...@@ -29,9 +28,4 @@
.margin { .margin {
margin: 20px; margin: 20px;
}
img {
width: 150px;
height: 150px;
} }
\ No newline at end of file
...@@ -12,21 +12,70 @@ ...@@ -12,21 +12,70 @@
<body> <body>
<div class="center-content page-container"> <div class="center-content page-container">
<div id="navbar"></div> <div id="navbar"></div>
<div class="center-content"> <div class="progress" id="loader">
<img id="img" src="" class="margin"/> <div class="indeterminate"></div>
<div id="selectList" class="input-field"></div> </div>
<div style="position: relative" class="margin">
<img id="img" src="" />
<canvas id="overlay" style="position: absolute; top: 0; left: 0;" />
</div>
<div class="row side-by-side">
<div id="selectList"></div>
<div class="row">
<label for="minConfidence">Min Confidence:</label>
<input disabled value="0.7" id="minConfidence" type="text" class="bold">
</div>
<button
class="waves-effect waves-light btn"
onclick="onDecreaseThreshold()"
>
<i class="material-icons left">-</i>
</button>
<button
class="waves-effect waves-light btn"
onclick="onIncreaseThreshold()"
>
<i class="material-icons left">+</i>
</button>
</div> </div>
</div> </div>
<script> <script>
let minConfidence = 0.7
let net, result
function onIncreaseThreshold() {
minConfidence = Math.min(round(minConfidence + 0.1), 1.0)
$('#minConfidence').val(minConfidence)
updateResults()
}
function onDecreaseThreshold() {
minConfidence = Math.max(round(minConfidence - 0.1), 0.1)
$('#minConfidence').val(minConfidence)
updateResults()
}
async function updateResults() {
result = await net.locateFaces(await imgSrcToImageData($(`#img`).get(0).src), minConfidence)
drawImgToCanvas('overlay', 'img')
drawDetection('overlay', result)
}
async function onSelectionChanged(uri) { async function onSelectionChanged(uri) {
const imgBuf = await fetchImage(uri) const imgBuf = await fetchImage(uri)
const imgEl = $(`#img`).get(0) $(`#img`).get(0).src = await bufferToImgSrc(imgBuf)
imgEl.src = await bufferToImgSrc(imgBuf) updateResults()
}
async function run() {
net = await initFaceDetectionNet()
$('#loader').hide()
onSelectionChanged($('#selectList select').val())
} }
$(document).ready(function() { $(document).ready(function() {
renderNavBar('#navbar') renderNavBar('#navbar', 'face_detection')
renderImageSelectList( renderImageSelectList(
'#selectList', '#selectList',
async (uri) => { async (uri) => {
...@@ -34,7 +83,7 @@ ...@@ -34,7 +83,7 @@
}, },
1 1
) )
onSelectionChanged($('#selectList select').val()) run()
}) })
</script> </script>
</body> </body>
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
class="waves-effect waves-light btn" class="waves-effect waves-light btn"
onclick="onSlower()" onclick="onSlower()"
> >
<i class="material-icons left">-</i> Slower <i class="material-icons left">-</i> Slower
</button> </button>
<button <button
class="waves-effect waves-light btn" class="waves-effect waves-light btn"
...@@ -71,17 +71,17 @@ ...@@ -71,17 +71,17 @@
let isStop = false let isStop = false
let trainDescriptorsByClass = [] let trainDescriptorsByClass = []
let net = {} let net
let currImageIdx = 2, currClassIdx = 0 let currImageIdx = 2, currClassIdx = 0
let to = null let to = null
function onSlower() { function onSlower() {
interval = Math.max(interval + 100, 0) interval = Math.min(interval + 100, 2000)
$('#interval').val(interval) $('#interval').val(interval)
} }
function onFaster() { function onFaster() {
interval = Math.min(interval - 100, 2000) interval = Math.max(interval - 100, 0)
$('#interval').val(interval) $('#interval').val(interval)
} }
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
const imgEl = $('#face').get(0) const imgEl = $('#face').get(0)
imgEl.src = await bufferToImgSrc(imgBuf) imgEl.src = await bufferToImgSrc(imgBuf)
const imageData = await imgSrcToData(imgEl.src) const imageData = await imgSrcToImageData(imgEl.src)
const ts = Date.now() const ts = Date.now()
const result = await net.forward(imageData) const result = await net.forward(imageData)
...@@ -161,7 +161,7 @@ ...@@ -161,7 +161,7 @@
try { try {
setStatusText('loading model file...') setStatusText('loading model file...')
net = await initNet() net = await initFaceRecognitionNet()
setStatusText('computing initial descriptors...') setStatusText('computing initial descriptors...')
const trainImgDatas = await loadTrainingData() const trainImgDatas = await loadTrainingData()
......
...@@ -20,11 +20,11 @@ ...@@ -20,11 +20,11 @@
<div class="row side-by-side"> <div class="row side-by-side">
<div class="center-content"> <div class="center-content">
<img id="face1" src="" class="margin"/> <img id="face1" src="" class="margin"/>
<div id="selectList1" class="input-field"></div> <div id="selectList1"></div>
</div> </div>
<div class="center-content"> <div class="center-content">
<img id="face2" src="" class="margin"/> <img id="face2" src="" class="margin"/>
<div id="selectList2" class="input-field"></div> <div id="selectList2"></div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
} }
async function computeDescriptorFromSrc(imgEl) { async function computeDescriptorFromSrc(imgEl) {
return net.computeFaceDescriptor(await imgSrcToData(imgEl.src)) return net.computeFaceDescriptor(await imgSrcToImageData(imgEl.src))
} }
async function onSelectionChanged(which, uri) { async function onSelectionChanged(which, uri) {
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
} }
async function run() { async function run() {
net = await initNet() net = await initFaceRecognitionNet()
$('#loader').hide() $('#loader').hide()
await onSelectionChanged(1, $('#selectList1 select').val()) await onSelectionChanged(1, $('#selectList1 select').val())
await onSelectionChanged(2, $('#selectList2 select').val()) await onSelectionChanged(2, $('#selectList2 select').val())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment