Commit 1416877d by vincent

new face recognition example

parent 174e0c28
...@@ -57,14 +57,18 @@ function renderNavBar(navbarId, exampleUri) { ...@@ -57,14 +57,18 @@ function renderNavBar(navbarId, exampleUri) {
name: 'BBT Face Landmark Detection' name: 'BBT Face Landmark Detection'
}, },
{ {
uri: 'bbt_face_recognition',
name: 'BBT Face Recognition'
},
{
uri: 'bbt_face_similarity', uri: 'bbt_face_similarity',
name: 'BBT Face Similarity' name: 'BBT Face Similarity'
}, },
{ {
uri: 'bbt_face_matching',
name: 'BBT Face Matching'
},
{
uri: 'bbt_face_recognition',
name: 'BBT Face Recognition'
},
{
uri: 'batch_face_landmarks', uri: 'batch_face_landmarks',
name: 'Batch Face Landmark Detection' name: 'Batch Face Landmark Detection'
}, },
......
...@@ -61,13 +61,7 @@ ...@@ -61,13 +61,7 @@
margin-bottom: 10px; margin-bottom: 10px;
} }
#overlay { #overlay, .overlay {
position: absolute;
top: 0;
left: 0;
}
.overlay {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
......
...@@ -19,8 +19,9 @@ app.get('/', (req, res) => res.redirect('/face_detection')) ...@@ -19,8 +19,9 @@ app.get('/', (req, res) => res.redirect('/face_detection'))
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')))
app.get('/bbt_face_landmark_detection', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceLandmarkDetection.html'))) app.get('/bbt_face_landmark_detection', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceLandmarkDetection.html')))
app.get('/bbt_face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceRecognition.html')))
app.get('/bbt_face_similarity', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceSimilarity.html'))) app.get('/bbt_face_similarity', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceSimilarity.html')))
app.get('/bbt_face_matching', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceMatching.html')))
app.get('/bbt_face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'bbtFaceRecognition.html')))
app.get('/face_detection', (req, res) => res.sendFile(path.join(viewsDir, 'faceDetection.html'))) app.get('/face_detection', (req, res) => res.sendFile(path.join(viewsDir, 'faceDetection.html')))
app.get('/face_extraction', (req, res) => res.sendFile(path.join(viewsDir, 'faceExtraction.html'))) app.get('/face_extraction', (req, res) => res.sendFile(path.join(viewsDir, 'faceExtraction.html')))
app.get('/face_landmark_detection', (req, res) => res.sendFile(path.join(viewsDir, 'faceLandmarkDetection.html'))) app.get('/face_landmark_detection', (req, res) => res.sendFile(path.join(viewsDir, 'faceLandmarkDetection.html')))
......
<!DOCTYPE html>
<html>
<head>
<script src="face-api.js"></script>
<script src="js/commons.js"></script>
<script src="js/bbt.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>
<div class="row center-content" id="loader">
<input disabled value="" id="status" type="text" class="bold">
<div class="progress">
<div class="indeterminate"></div>
</div>
</div>
<div class="row center-content">
<img id="face" src=""/>
</div>
<div class="row">
<label for="prediction">Prediction:</label>
<input disabled value="-" id="prediction" type="text" class="bold">
</div>
<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 class="row">
<button
class="waves-effect waves-light btn"
id="stop"
onclick="onToggleStop()"
>
Stop
</button>
<button
class="waves-effect waves-light btn"
onclick="onSlower()"
>
<i class="material-icons left">-</i> Slower
</button>
<button
class="waves-effect waves-light btn"
onclick="onFaster()"
>
<i class="material-icons left">+</i> Faster
</button>
</div>
<div class="row">
<label for="interval">Interval:</label>
<input disabled value="2000" id="interval" type="text" class="bold">
</div>
</div>
</div>
<script>
let interval = 2000
let isStop = false
let faceMatcher = null
let currImageIdx = 2, currClassIdx = 0
let to = null
function onSlower() {
interval = Math.min(interval + 100, 2000)
$('#interval').val(interval)
}
function onFaster() {
interval = Math.max(interval - 100, 0)
$('#interval').val(interval)
}
function onToggleStop() {
clearTimeout(to)
isStop = !isStop
document.getElementById('stop').innerHTML = isStop ? 'Continue' : 'Stop'
setStatusText(isStop ? 'stopped' : 'running face recognition:')
if (!isStop) {
runFaceRecognition()
}
}
function setStatusText(text) {
$('#status').val(text)
}
function displayTimeStats(timeInMs) {
$('#time').val(`${timeInMs} ms`)
$('#fps').val(`${faceapi.round(1000 / timeInMs)}`)
}
function displayImage(src) {
getImg().src = src
}
async function runFaceRecognition() {
async function next() {
const input = await faceapi.fetchImage(getFaceImageUri(classes[currClassIdx], currImageIdx))
const imgEl = $('#face').get(0)
imgEl.src = input.src
const ts = Date.now()
const descriptor = await faceapi.computeFaceDescriptor(input)
displayTimeStats(Date.now() - ts)
const bestMatch = faceMatcher.findBestMatch(descriptor)
$('#prediction').val(bestMatch.toString())
currImageIdx = currClassIdx === (classes.length - 1)
? currImageIdx + 1
: currImageIdx
currClassIdx = (currClassIdx + 1) % classes.length
currImageIdx = (currImageIdx % 6) || 2
to = setTimeout(next, interval)
}
await next(0, 0)
}
async function run() {
try {
setStatusText('loading model file...')
await faceapi.loadFaceRecognitionModel('/')
setStatusText('computing initial descriptors...')
faceMatcher = await createBbtFaceMatcher(1)
$('#loader').hide()
runFaceRecognition()
} catch (err) {
console.error(err)
}
}
$(document).ready(function() {
renderNavBar('#navbar', 'bbt_face_matching')
run()
})
</script>
</body>
</html>
\ No newline at end of file
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
<head> <head>
<script src="face-api.js"></script> <script src="face-api.js"></script>
<script src="js/commons.js"></script> <script src="js/commons.js"></script>
<script src="js/drawing.js"></script>
<script src="js/faceDetectionControls.js"></script>
<script src="js/imageSelectionControls.js"></script>
<script src="js/bbt.js"></script> <script src="js/bbt.js"></script>
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
...@@ -12,144 +15,182 @@ ...@@ -12,144 +15,182 @@
<body> <body>
<div id="navbar"></div> <div id="navbar"></div>
<div class="center-content page-container"> <div class="center-content page-container">
<div>
<div class="row center-content" id="loader"> <div class="progress" id="loader">
<input disabled value="" id="status" type="text" class="bold"> <div class="indeterminate"></div>
<div class="progress"> </div>
<div class="indeterminate"></div> <div style="position: relative" class="margin">
</div> <img id="inputImg" src="" style="max-width: 800px;" />
</div> <canvas id="overlay" />
<div class="row center-content"> </div>
<img id="face" src=""/>
</div> <div class="row side-by-side">
<div class="row">
<label for="prediction">Prediction:</label> <!-- face_detector_selection_control -->
<input disabled value="-" id="prediction" type="text" class="bold"> <div id="face_detector_selection_control" class="row input-field" style="margin-right: 20px;">
</div> <select id="selectFaceDetector">
<div class="row"> <option value="ssd_mobilenetv1">SSD Mobilenet V1</option>
<label for="time">Time:</label> <option value="tiny_face_detector">Tiny Face Detector</option>
<input disabled value="-" id="time" type="text" class="bold"> <option value="mtcnn">MTCNN</option>
</div> </select>
<div class="row"> <label>Select Face Detector</label>
<label for="fps">Estimated Fps:</label>
<input disabled value="-" id="fps" type="text" class="bold">
</div> </div>
<div class="row"> <!-- face_detector_selection_control -->
<!-- image_selection_control -->
<div id="image_selection_control"></div>
<div id="selectList"></div>
<div class="row">
<label for="imgUrlInput">Get image from URL:</label>
<input id="imgUrlInput" type="text" class="bold">
</div>
<button <button
class="waves-effect waves-light btn" class="waves-effect waves-light btn"
id="stop" onclick="loadImageFromUrl()"
onclick="onToggleStop()"
> >
Stop Ok
</button> </button>
<div id="image_selection_control"></div>
<!-- image_selection_control -->
</div>
<!-- ssd_mobilenetv1_controls -->
<span id="ssd_mobilenetv1_controls">
<div class="row side-by-side">
<div class="row">
<label for="minConfidence">Min Confidence:</label>
<input disabled value="0.7" id="minConfidence" type="text" class="bold">
</div>
<button <button
class="waves-effect waves-light btn" class="waves-effect waves-light btn"
onclick="onSlower()" onclick="onDecreaseMinConfidence()"
> >
<i class="material-icons left">-</i> Slower <i class="material-icons left">-</i>
</button> </button>
<button <button
class="waves-effect waves-light btn" class="waves-effect waves-light btn"
onclick="onFaster()" onclick="onIncreaseMinConfidence()"
> >
<i class="material-icons left">+</i> Faster <i class="material-icons left">+</i>
</button> </button>
</div> </div>
<div class="row"> </span>
<label for="interval">Interval:</label> <!-- ssd_mobilenetv1_controls -->
<input disabled value="2000" id="interval" type="text" class="bold">
<!-- tiny_face_detector_controls -->
<span id="tiny_face_detector_controls">
<div class="row side-by-side">
<div class="row input-field" style="margin-right: 20px;">
<select id="inputSize">
<option value="" disabled selected>Input Size:</option>
<option value="160">160 x 160</option>
<option value="224">224 x 224</option>
<option value="320">320 x 320</option>
<option value="416">416 x 416</option>
<option value="512">512 x 512</option>
<option value="608">608 x 608</option>
</select>
<label>Input Size</label>
</div>
<div class="row">
<label for="scoreThreshold">Score Threshold:</label>
<input disabled value="0.5" id="scoreThreshold" type="text" class="bold">
</div>
<button
class="waves-effect waves-light btn"
onclick="onDecreaseScoreThreshold()"
>
<i class="material-icons left">-</i>
</button>
<button
class="waves-effect waves-light btn"
onclick="onIncreaseScoreThreshold()"
>
<i class="material-icons left">+</i>
</button>
</div> </div>
</div> </span>
</div> <!-- tiny_face_detector_controls -->
<!-- mtcnn_controls -->
<span id="mtcnn_controls">
<div class="row side-by-side">
<div class="row">
<label for="minFaceSize">Minimum Face Size:</label>
<input disabled value="20" 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>
</span>
<!-- mtcnn_controls -->
<script> </body>
let interval = 2000
let isStop = false <script>
let faceMatcher = null let faceMatcher = null
let currImageIdx = 2, currClassIdx = 0
let to = null
function onSlower() {
interval = Math.min(interval + 100, 2000)
$('#interval').val(interval)
}
function onFaster() {
interval = Math.max(interval - 100, 0)
$('#interval').val(interval)
}
function onToggleStop() { async function updateResults() {
clearTimeout(to) if (!isFaceDetectionModelLoaded()) {
isStop = !isStop return
document.getElementById('stop').innerHTML = isStop ? 'Continue' : 'Stop'
setStatusText(isStop ? 'stopped' : 'running face recognition:')
if (!isStop) {
runFaceRecognition()
} }
}
function setStatusText(text) { const inputImgEl = $('#inputImg').get(0)
$('#status').val(text)
}
function displayTimeStats(timeInMs) { const options = getFaceDetectorOptions()
$('#time').val(`${timeInMs} ms`) const results = await faceapi
$('#fps').val(`${faceapi.round(1000 / timeInMs)}`) .detectAllFaces(inputImgEl, options)
} .withFaceLandmarks()
.withFaceDescriptors()
function displayImage(src) { drawFaceRecognitionResults(results)
getImg().src = src
} }
async function runFaceRecognition() { function drawFaceRecognitionResults(results) {
async function next() { const canvas = $('#overlay').get(0)
const input = await faceapi.fetchImage(getFaceImageUri(classes[currClassIdx], currImageIdx)) // resize detection and landmarks in case displayed image is smaller than
const imgEl = $('#face').get(0) // original size
imgEl.src = input.src resizedResults = resizeCanvasAndResults($('#inputImg').get(0), canvas, results)
const ts = Date.now() const boxesWithText = resizedResults.map(({ detection, descriptor }) =>
const descriptor = await faceapi.computeFaceDescriptor(input) new faceapi.BoxWithText(
displayTimeStats(Date.now() - ts) detection.box,
faceMatcher.findBestMatch(descriptor).toString()
const bestMatch = faceMatcher.findBestMatch(descriptor) )
$('#prediction').val(bestMatch.toString()) )
faceapi.drawDetection(canvas, boxesWithText)
currImageIdx = currClassIdx === (classes.length - 1)
? currImageIdx + 1
: currImageIdx
currClassIdx = (currClassIdx + 1) % classes.length
currImageIdx = (currImageIdx % 6) || 2
to = setTimeout(next, interval)
}
await next(0, 0)
} }
async function run() { async function run() {
try { // load face detection, face landmark model and face recognition models
setStatusText('loading model file...') await changeFaceDetector(selectedFaceDetector)
await faceapi.loadFaceLandmarkModel('/')
await faceapi.loadFaceRecognitionModel('/') await faceapi.loadFaceRecognitionModel('/')
setStatusText('computing initial descriptors...')
faceMatcher = await createBbtFaceMatcher(1) // initialize face matcher with 1 reference descriptor per bbt character
$('#loader').hide() faceMatcher = await createBbtFaceMatcher(1)
runFaceRecognition() // start processing image
} catch (err) { updateResults()
console.error(err)
}
} }
$(document).ready(function() { $(document).ready(function() {
renderNavBar('#navbar', 'bbt_face_recognition') renderNavBar('#navbar', 'bbt_face_recognition')
initImageSelectionControls()
initFaceDetectionControls()
run() run()
}) })
</script> </script>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -37,19 +37,17 @@ ...@@ -37,19 +37,17 @@
<!-- face_detector_selection_control --> <!-- face_detector_selection_control -->
<!-- image_selection_control --> <!-- image_selection_control -->
<div id="image_selection_control"></div> <div id="selectList"></div>
<div id="selectList"></div> <div class="row">
<div class="row"> <label for="imgUrlInput">Get image from URL:</label>
<label for="imgUrlInput">Get image from URL:</label> <input id="imgUrlInput" type="text" class="bold">
<input id="imgUrlInput" type="text" class="bold"> </div>
</div> <button
<button class="waves-effect waves-light btn"
class="waves-effect waves-light btn" onclick="loadImageFromUrl()"
onclick="loadImageFromUrl()" >
> Ok
Ok </button>
</button>
<div id="image_selection_control"></div>
<!-- image_selection_control --> <!-- image_selection_control -->
</div> </div>
......
...@@ -35,19 +35,17 @@ ...@@ -35,19 +35,17 @@
<!-- face_detector_selection_control --> <!-- face_detector_selection_control -->
<!-- image_selection_control --> <!-- image_selection_control -->
<div id="image_selection_control"></div> <div id="selectList"></div>
<div id="selectList"></div> <div class="row">
<div class="row"> <label for="imgUrlInput">Get image from URL:</label>
<label for="imgUrlInput">Get image from URL:</label> <input id="imgUrlInput" type="text" class="bold">
<input id="imgUrlInput" type="text" class="bold"> </div>
</div> <button
<button class="waves-effect waves-light btn"
class="waves-effect waves-light btn" onclick="loadImageFromUrl()"
onclick="loadImageFromUrl()" >
> Ok
Ok </button>
</button>
<div id="image_selection_control"></div>
<!-- image_selection_control --> <!-- image_selection_control -->
</div> </div>
......
...@@ -37,19 +37,17 @@ ...@@ -37,19 +37,17 @@
<!-- face_detector_selection_control --> <!-- face_detector_selection_control -->
<!-- image_selection_control --> <!-- image_selection_control -->
<div id="image_selection_control"></div> <div id="selectList"></div>
<div id="selectList"></div> <div class="row">
<div class="row"> <label for="imgUrlInput">Get image from URL:</label>
<label for="imgUrlInput">Get image from URL:</label> <input id="imgUrlInput" type="text" class="bold">
<input id="imgUrlInput" type="text" class="bold"> </div>
</div> <button
<button class="waves-effect waves-light btn"
class="waves-effect waves-light btn" onclick="loadImageFromUrl()"
onclick="loadImageFromUrl()" >
> Ok
Ok </button>
</button>
<div id="image_selection_control"></div>
<!-- image_selection_control --> <!-- image_selection_control -->
</div> </div>
......
import { ISsdMobilenetv1Options } from './types'; import { ISsdMobilenetv1Options } from './types';
export class SsdMobilenetv1Options { export class SsdMobilenetv1Options {
protected _name: string = 'MtcnnOptions' protected _name: string = 'SsdMobilenetv1Options'
private _minConfidence: number private _minConfidence: number
private _maxResults: number private _maxResults: number
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
<script> <script>
tf = faceapi.tf tf = faceapi.tf
const uncompressedWeightsUri = `face_landmark_68_model.weights` const uncompressedWeightsUri = `tiny_face_detector_model.weights`
const net = new faceapi.FaceLandmark68LargeNet() const net = new faceapi.TinyFaceDetector()
async function load() { async function load() {
await net.load(new Float32Array(await (await fetch(uncompressedWeightsUri)).arrayBuffer())) await net.load(new Float32Array(await (await fetch(uncompressedWeightsUri)).arrayBuffer()))
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
return net.getParamList().map(({ path, tensor }) => ({ name: path, tensor })) return net.getParamList().map(({ path, tensor }) => ({ name: path, tensor }))
} }
const modelName = 'face_landmark_68' const modelName = 'tiny_face_detector'
function makeShards(weightArray) { function makeShards(weightArray) {
const maxLength = 4096 * 1024 const maxLength = 4096 * 1024
......
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