Commit 3f1826ab by vincent

added examples for mtcnn face detection + added getters to FaceDetection

parent 2b794bc3
...@@ -123,6 +123,10 @@ function renderNavBar(navbarId, exampleUri) { ...@@ -123,6 +123,10 @@ function renderNavBar(navbarId, exampleUri) {
name: 'MTCNN Face Detection Video' name: 'MTCNN Face Detection Video'
}, },
{ {
uri: 'mtcnn_face_detection_webcam',
name: 'MTCNN Face Detection Webcam'
},
{
uri: 'batch_face_landmarks', uri: 'batch_face_landmarks',
name: 'Batch Face Landmarks' name: 'Batch Face Landmarks'
}, },
...@@ -159,7 +163,7 @@ function renderNavBar(navbarId, exampleUri) { ...@@ -159,7 +163,7 @@ function renderNavBar(navbarId, exampleUri) {
githubLink.id = 'github-link' githubLink.id = 'github-link'
githubLink.href = 'https://github.com/justadudewhohacks/face-api.js' githubLink.href = 'https://github.com/justadudewhohacks/face-api.js'
const h5 = document.createElement('h5') const h5 = document.createElement('h5')
h5.innerHTML = 'face-api.js'//'If you like this project, feel free to leave a star on github :)' h5.innerHTML = 'face-api.js'
githubLink.appendChild(h5) githubLink.appendChild(h5)
const githubLinkIcon = document.createElement('img') const githubLinkIcon = document.createElement('img')
githubLinkIcon.src = 'github_link_icon.png' githubLinkIcon.src = 'github_link_icon.png'
...@@ -176,7 +180,10 @@ function renderNavBar(navbarId, exampleUri) { ...@@ -176,7 +180,10 @@ function renderNavBar(navbarId, exampleUri) {
const a = document.createElement('a') const a = document.createElement('a')
a.classList.add('waves-effect', 'waves-light') a.classList.add('waves-effect', 'waves-light')
a.href = ex.uri a.href = ex.uri
a.innerHTML = ex.name const span = document.createElement('span')
span.innerHTML = ex.name
span.style.whiteSpace = 'nowrap'
a.appendChild(span)
li.appendChild(a) li.appendChild(a)
menuContent.appendChild(li) menuContent.appendChild(li)
}) })
......
...@@ -26,6 +26,7 @@ app.get('/face_alignment', (req, res) => res.sendFile(path.join(viewsDir, 'faceA ...@@ -26,6 +26,7 @@ app.get('/face_alignment', (req, res) => res.sendFile(path.join(viewsDir, 'faceA
app.get('/detect_and_recognize_faces', (req, res) => res.sendFile(path.join(viewsDir, 'detectAndRecognizeFaces.html'))) app.get('/detect_and_recognize_faces', (req, res) => res.sendFile(path.join(viewsDir, 'detectAndRecognizeFaces.html')))
app.get('/mtcnn_face_detection', (req, res) => res.sendFile(path.join(viewsDir, 'mtcnnFaceDetection.html'))) app.get('/mtcnn_face_detection', (req, res) => res.sendFile(path.join(viewsDir, 'mtcnnFaceDetection.html')))
app.get('/mtcnn_face_detection_video', (req, res) => res.sendFile(path.join(viewsDir, 'mtcnnFaceDetectionVideo.html'))) app.get('/mtcnn_face_detection_video', (req, res) => res.sendFile(path.join(viewsDir, 'mtcnnFaceDetectionVideo.html')))
app.get('/mtcnn_face_detection_webcam', (req, res) => res.sendFile(path.join(viewsDir, 'mtcnnFaceDetectionWebcam.html')))
app.get('/batch_face_landmarks', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceLandmarks.html'))) app.get('/batch_face_landmarks', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceLandmarks.html')))
app.get('/batch_face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceRecognition.html'))) app.get('/batch_face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceRecognition.html')))
......
...@@ -52,10 +52,10 @@ ...@@ -52,10 +52,10 @@
</div> </div>
<script> <script>
let minFaceSize = 50 //20 // let minFaceSize = 50
let scaleFactor = 0.709 // 0.2 let scaleFactor = 0.709
let maxNumScales = 10 // 0.2 let maxNumScales = 10
let stage1Threshold = 0.6 let stage1Threshold = 0.7
let stage2Threshold = 0.7 let stage2Threshold = 0.7
let stage3Threshold = 0.7 let stage3Threshold = 0.7
let mtcnn let mtcnn
...@@ -85,13 +85,15 @@ ...@@ -85,13 +85,15 @@
canvas.width = width canvas.width = width
canvas.height = height canvas.height = height
const scoreThresholds = [stage1Threshold, stage2Threshold, stage3Threshold] const mtcnnParams = {
const { results, stats } = await mtcnn.forwardWithStats(inputImgEl, minFaceSize, scaleFactor, maxNumScales, scoreThresholds) minFaceSize,
scaleFactor,
console.log(stats) maxNumScales,
scoreThresholds: [stage1Threshold, stage2Threshold, stage3Threshold]
}
const results = await faceapi.mtcnn(inputImgEl, mtcnnParams)
if (results) { if (results) {
const { faceDetection, faceLandmarks } = results
results.forEach(({ faceDetection, faceLandmarks }) => { results.forEach(({ faceDetection, faceLandmarks }) => {
faceapi.drawDetection('overlay', faceDetection.forSize(width, height)) faceapi.drawDetection('overlay', faceDetection.forSize(width, height))
faceapi.drawLandmarks('overlay', faceLandmarks.forSize(width, height), { lineWidth: 4, color: 'red' }) faceapi.drawLandmarks('overlay', faceLandmarks.forSize(width, height), { lineWidth: 4, color: 'red' })
...@@ -106,8 +108,7 @@ ...@@ -106,8 +108,7 @@
} }
async function run() { async function run() {
const weightsBuffer = await (await fetch('/uncompressed/mtcnn_model.weights')).arrayBuffer() await faceapi.loadMtcnnModel('/')
mtcnn = faceapi.mtcnn(new Float32Array(weightsBuffer))
$('#loader').hide() $('#loader').hide()
onSelectionChanged($('#selectList select').val()) onSelectionChanged($('#selectList select').val())
} }
......
...@@ -20,18 +20,18 @@ ...@@ -20,18 +20,18 @@
</div> </div>
<div class="row side-by-side"> <div class="row side-by-side">
<div class="row"> <div class="row">
<label for="minConfidence">Min Confidence:</label> <label for="minFaceSize">Minimum Face Size:</label>
<input disabled value="0.7" id="minConfidence" type="text" class="bold"> <input disabled value="80" id="minFaceSize" type="text" class="bold">
</div> </div>
<button <button
class="waves-effect waves-light btn" class="waves-effect waves-light btn"
onclick="onDecreaseThreshold()" onclick="onDecreaseMinFaceSize()"
> >
<i class="material-icons left">-</i> <i class="material-icons left">-</i>
</button> </button>
<button <button
class="waves-effect waves-light btn" class="waves-effect waves-light btn"
onclick="onIncreaseThreshold()" onclick="onIncreaseMinFaceSize()"
> >
<i class="material-icons left">+</i> <i class="material-icons left">+</i>
</button> </button>
...@@ -49,29 +49,26 @@ ...@@ -49,29 +49,26 @@
</div> </div>
<script> <script>
let modelLoaded = false
let minFaceSize = 80 let minFaceSize = 80
let scaleFactor = 0.709
let maxNumScales = 10
let minConfidence = 0.9 let minConfidence = 0.9
let stage1Threshold = 0.7 let forwardTimes = []
let stage2Threshold = 0.7
let stage3Threshold = 0.7
let modelLoaded = false
let result
function onIncreaseThreshold() { function onIncreaseMinFaceSize() {
minConfidence = Math.min(faceapi.round(minConfidence + 0.1), 1.0) minFaceSize = Math.min(faceapi.round(minFaceSize + 20), 200)
$('#minConfidence').val(minConfidence) $('#minFaceSize').val(minFaceSize)
} }
function onDecreaseThreshold() { function onDecreaseMinFaceSize() {
minConfidence = Math.max(faceapi.round(minConfidence - 0.1), 0.1) minFaceSize = Math.max(faceapi.round(minFaceSize - 20), 20)
$('#minConfidence').val(minConfidence) $('#minFaceSize').val(minFaceSize)
} }
function displayTimeStats(timeInMs) { function updateTimeStats(timeInMs) {
$('#time').val(`${timeInMs} ms`) forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30)
$('#fps').val(`${faceapi.round(1000 / timeInMs)}`) const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length
$('#time').val(`${Math.round(avgTimeInMs)} ms`)
$('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`)
} }
async function onPlay(videoEl) { async function onPlay(videoEl) {
...@@ -83,13 +80,15 @@ ...@@ -83,13 +80,15 @@
canvas.width = width canvas.width = width
canvas.height = height canvas.height = height
const scoreThresholds = [stage1Threshold, stage2Threshold, stage3Threshold] const mtcnnParams = {
minFaceSize
}
const ts = Date.now() const ts = Date.now()
const { results, stats } = await mtcnn.forwardWithStats(videoEl, minFaceSize, scaleFactor, maxNumScales, scoreThresholds) const results = await faceapi.mtcnn(videoEl, mtcnnParams)
displayTimeStats(Date.now() - ts) updateTimeStats(Date.now() - ts)
if (results) { if (results) {
const { faceDetection, faceLandmarks } = results
results.forEach(({ faceDetection, faceLandmarks }) => { results.forEach(({ faceDetection, faceLandmarks }) => {
if (faceDetection.score < minConfidence) { if (faceDetection.score < minConfidence) {
return return
...@@ -102,8 +101,7 @@ ...@@ -102,8 +101,7 @@
} }
async function run() { async function run() {
const weightsBuffer = await (await fetch('/uncompressed/mtcnn_model.weights')).arrayBuffer() await faceapi.loadMtcnnModel('/')
mtcnn = faceapi.mtcnn(new Float32Array(weightsBuffer))
modelLoaded = true modelLoaded = true
onPlay($('#inputVideo').get(0)) onPlay($('#inputVideo').get(0))
$('#loader').hide() $('#loader').hide()
......
<!DOCTYPE html>
<html>
<head>
<script src="face-api.js"></script>
<script src="commons.js"></script>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
</head>
<body>
<div id="navbar"></div>
<div class="center-content page-container">
<div class="progress" id="loader">
<div class="indeterminate"></div>
</div>
<div style="position: relative" class="margin">
<video onplay="onPlay(this)" id="inputVideo" autoplay muted></video>
<canvas id="overlay" />
</div>
<div class="row side-by-side">
<div class="row">
<label for="minFaceSize">Minimum Face Size:</label>
<input disabled value="200" id="minFaceSize" type="text" class="bold">
</div>
<button
class="waves-effect waves-light btn"
onclick="onDecreaseMinFaceSize()"
>
<i class="material-icons left">-</i>
</button>
<button
class="waves-effect waves-light btn"
onclick="onIncreaseMinFaceSize()"
>
<i class="material-icons left">+</i>
</button>
</div>
<div class="row side-by-side">
<div class="row">
<label for="time">Time:</label>
<input disabled value="-" id="time" type="text" class="bold">
</div>
<div class="row">
<label for="fps">Estimated Fps:</label>
<input disabled value="-" id="fps" type="text" class="bold">
</div>
</div>
</div>
<script>
let modelLoaded = false
let minFaceSize = 200
let minConfidence = 0.9
let forwardTimes = []
function onIncreaseMinFaceSize() {
minFaceSize = Math.min(faceapi.round(minFaceSize + 50), 300)
$('#minFaceSize').val(minFaceSize)
}
function onDecreaseMinFaceSize() {
minFaceSize = Math.max(faceapi.round(minFaceSize - 50), 50)
$('#minFaceSize').val(minFaceSize)
}
function updateTimeStats(timeInMs) {
forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30)
const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length
$('#time').val(`${Math.round(avgTimeInMs)} ms`)
$('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`)
}
async function onPlay(videoEl) {
if(videoEl.paused || videoEl.ended || !modelLoaded)
return false
const { width, height } = faceapi.getMediaDimensions(videoEl)
const canvas = $('#overlay').get(0)
canvas.width = width
canvas.height = height
const mtcnnParams = {
minFaceSize
}
const c = faceapi.createCanvas({ width: width , height: height })
c.getContext('2d').drawImage(videoEl, 0, 0)
const { results, stats } = await faceapi.nets.mtcnn.forwardWithStats(c, mtcnnParams)
updateTimeStats(stats.total)
if (results) {
results.forEach(({ faceDetection, faceLandmarks }) => {
if (faceDetection.score < minConfidence) {
return
}
faceapi.drawDetection('overlay', faceDetection.forSize(width, height))
faceapi.drawLandmarks('overlay', faceLandmarks.forSize(width, height), { lineWidth: 4, color: 'red' })
})
}
setTimeout(() => onPlay(videoEl))
}
async function run() {
await faceapi.loadMtcnnModel('/')
modelLoaded = true
const videoEl = $('#inputVideo').get(0)
navigator.getUserMedia(
{ video: {} },
stream => videoEl.srcObject = stream,
err => console.error(err)
)
$('#loader').hide()
}
$(document).ready(function() {
renderNavBar('#navbar', 'mtcnn_face_detection_webcam')
run()
})
</script>
</body>
</html>
\ No newline at end of file
...@@ -24,23 +24,23 @@ export class FaceDetection { ...@@ -24,23 +24,23 @@ export class FaceDetection {
) )
} }
public getScore() { public get score(): number {
return this._score return this._score
} }
public getBox() { public get box(): Rect {
return this._box return this._box
} }
public getImageWidth() { public get imageWidth(): number {
return this._imageWidth return this._imageWidth
} }
public getImageHeight() { public get imageHeight(): number {
return this._imageHeight return this._imageHeight
} }
public getRelativeBox() { public get relativeBox(): Rect {
return new Rect( return new Rect(
this._box.x / this._imageWidth, this._box.x / this._imageWidth,
this._box.y / this._imageHeight, this._box.y / this._imageHeight,
...@@ -49,6 +49,26 @@ export class FaceDetection { ...@@ -49,6 +49,26 @@ export class FaceDetection {
) )
} }
public getScore() {
return this.score
}
public getBox() {
return this.box
}
public getImageWidth() {
return this.imageWidth
}
public getImageHeight() {
return this.imageHeight
}
public getRelativeBox() {
return this.relativeBox
}
public forSize(width: number, height: number): FaceDetection { public forSize(width: number, height: number): FaceDetection {
return new FaceDetection( return new FaceDetection(
this._score, this._score,
......
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