Commit f0d19464 by vincent

face similarity example

parent 5d99dc01
const classes = ['amy', 'bernadette', 'howard', 'leonard', 'penny', 'raj', 'sheldon', 'stuart']
function getImageUri(className, idx) {
return `/${className}/${className}${idx}.png`
}
async function initNet() {
const res = await axios.get('face_recognition_model.weights', { responseType: 'arraybuffer' })
const weights = new Float32Array(res.data)
return facerecognition.faceRecognitionNet(weights)
}
function bufferToImgSrc(buf) {
return new Promise((resolve, reject) => {
const reader = new window.FileReader()
reader.onload = () => resolve(reader.result)
reader.onerror = reject
reader.readAsDataURL(buf)
})
}
function imgSrcToData(src) {
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()
img.onload = function() {
ctx.drawImage(img, 0, 0)
resolve(ctx.getImageData(0, 0, 150, 150))
}
img.onerror = reject
img.src = src
})
}
async function bufferToImageData(buf) {
return imgSrcToData(await bufferToImgSrc(buf))
}
async function fetchImage(uri) {
return (await axios.get(uri, { responseType: 'blob' })).data
}
function round(num) {
return Math.floor(num * 100) / 100
}
\ No newline at end of file
.page-container {
position: fixed;
left: 0;
right: 0;
margin: auto;
margin-top: 20px;
}
.center-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.side-by-side {
display: flex;
justify-content: center;
align-items: center;
}
.bold {
font-weight: bold;
}
.margin {
margin: 20px;
}
img {
width: 150px;
height: 150px;
}
\ No newline at end of file
......@@ -3,12 +3,16 @@ const path = require('path')
const app = express()
const viewsDir = path.join(__dirname, 'views')
app.use(express.static(path.join(__dirname, '../images')))
app.use(express.static(path.join(__dirname, '../../weights')))
app.use(express.static(path.join(__dirname, '../../dist')))
app.use(express.static(path.join(__dirname, './node_modules/axios/dist')))
app.use(express.static(path.join(__dirname, 'views')))
app.use(express.static(viewsDir))
app.use(express.static(path.join(__dirname, './public')))
app.get('/', (req, res) => res.sendFile('./index.html'))
app.get('/', (req, res) => res.redirect('/faceRecognition'))
app.get('/faceRecognition', (req, res) => res.sendFile(path.join(viewsDir, 'faceRecognition.html')))
app.get('/faceSimilarity', (req, res) => res.sendFile(path.join(viewsDir, 'faceSimilarity.html')))
app.listen(3000, () => console.log('Listening on port 3000!'))
\ No newline at end of file
......@@ -3,25 +3,28 @@
<head>
<script src="face-recognition.min.js"></script>
<script src="axios.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
<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>
<style>
.center-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.bold {
font-weight: bold;
}
</style>
<body>
<div class="center-content col s12">
<div class="center-content page-container">
<div class="row">
<a
class="waves-effect waves-light btn"
href="faceSimilarity.html"
>
Go To Face Similarity Example
</a>
</div>
<div>
<div class="row center-content">
<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=""/>
......@@ -67,7 +70,6 @@
</div>
<script>
const classes = ['amy', 'bernadette', 'howard', 'leonard', 'penny', 'raj', 'sheldon', 'stuart']
// for 150 x 150 sized face images 0.6 is a good threshold to
// judge whether two face descriptors are similar or not
const threshold = 0.6
......@@ -81,12 +83,12 @@
function onSlower() {
interval = Math.max(interval + 100, 0)
document.getElementById('interval').value = interval
$('#interval').val(interval)
}
function onFaster() {
interval = Math.min(interval - 100, 2000)
document.getElementById('interval').value = interval
$('#interval').val(interval)
}
function onToggleStop() {
......@@ -99,74 +101,19 @@
}
}
function round(num) {
return Math.floor(num * 100) / 100
}
function getImageUri(className, idx) {
return `/${className}/${className}${idx}.png`
}
function setStatusText(text) {
document.getElementById('status').value = text
}
function setPrediction(result) {
document.getElementById('prediction').value = result
$('#status').val(text)
}
function displayTimeStats(timeInMs) {
document.getElementById('time').value = `${timeInMs} ms`
document.getElementById('fps').value = `${round(1000 / timeInMs)}`
}
function getImg() {
return document.getElementById('face')
$('#time').val(`${timeInMs} ms`)
$('#fps').val(`${round(1000 / timeInMs)}`)
}
function displayImage(src) {
getImg().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)
})
}
function imgSrcToData(src) {
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()
img.onload = function() {
ctx.drawImage(img, 0, 0)
resolve(ctx.getImageData(0, 0, 150, 150))
}
img.onerror = reject
img.src = src
})
}
async function bufferToImageData(buf) {
return imgSrcToData(await bufferToImgSrc(buf))
}
async function fetchModelWeights() {
const res = await axios.get('face_recognition_model.weights', { responseType: 'arraybuffer' })
return new Float32Array(res.data)
}
async function fetchImage(uri) {
return (await axios.get(uri, { responseType: 'blob' })).data
}
async function loadTrainingData(cb) {
return await Promise.all(classes.map(
async className => ({
......@@ -192,9 +139,10 @@
async function runFaceRecognition() {
async function next() {
const imgBuf = await fetchImage(getImageUri(classes[currClassIdx], currImageIdx))
displayImage(await bufferToImgSrc(imgBuf))
const imgEl = $('#face').get(0)
imgEl.src = await bufferToImgSrc(imgBuf)
const imageData = await imgSrcToData(getImg().src)
const imageData = await imgSrcToData(imgEl.src)
const ts = Date.now()
const result = await net.forward(imageData)
......@@ -202,7 +150,7 @@
const descriptor = await result.data()
const bestMatch = getBestMatch(descriptor)
setPrediction(`${bestMatch.distance < threshold ? bestMatch.className : 'unkown'} (${bestMatch.distance})`)
$('#prediction').val(`${bestMatch.distance < threshold ? bestMatch.className : 'unkown'} (${bestMatch.distance})`)
currImageIdx = currClassIdx === (classes.length - 1)
? currImageIdx + 1
......@@ -219,8 +167,7 @@
try {
setStatusText('loading model file...')
const weights = await fetchModelWeights()
net = facerecognition.faceRecognitionNet(weights)
net = await initNet()
setStatusText('computing initial descriptors...')
const trainImgDatas = await loadTrainingData()
......@@ -230,8 +177,8 @@
className
})
))
$('#loader').hide()
setStatusText('running face recognition:')
runFaceRecognition()
} catch (err) {
console.error(err)
......
<!DOCTYPE html>
<html>
<head>
<script src="face-recognition.min.js"></script>
<script src="axios.min.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 class="center-content page-container">
<div class="row">
<a
class="waves-effect waves-light btn"
href="faceRecognition"
>
Go To Face Recognition Example
</a>
</div>
<div>
<div class="progress" id="loader">
<div class="indeterminate"></div>
</div>
<div class="row side-by-side">
<div class="center-content">
<img id="face1" src="" class="margin"/>
<div id="selectList1" class="input-field"></div>
</div>
<div class="center-content">
<img id="face2" src="" class="margin"/>
<div id="selectList2" class="input-field"></div>
</div>
</div>
<div class="row">
<label for="distance">Distance:</label>
<input disabled value="-" id="distance" type="text" class="bold">
</div>
</div>
</div>
<script>
const threshold = 0.6
let net, descriptors = { desc1: null, desc2: null }
function renderSelectList(parent) {
const select = document.createElement('select')
parent.appendChild(select)
classes.forEach((className) => {
const optgroup = document.createElement('optgroup')
optgroup.label = className
select.appendChild(optgroup)
const indices = [1, 2, 3, 4, 5]
indices.forEach((imageIdx) => {
const option = document.createElement('option')
option.innerHTML = `${className} ${imageIdx}`
option.value = getImageUri(className, imageIdx)
optgroup.appendChild(option)
})
})
}
function updateResult() {
const distance = round(facerecognition.euclideanDistance(descriptors.desc1, descriptors.desc2))
let text = distance
let bgColor = '#ffffff'
if (distance > threshold) {
text += ' (no match)'
bgColor = '#ce7575'
}
$('#distance').val(text)
$('#distance').css('background-color', bgColor)
}
async function computeDescriptorFromSrc(imgEl) {
return net.computeFaceDescriptor(await imgSrcToData(imgEl.src))
}
async function onSelectionChanged(which) {
const selectElSelector = `#selectList${which} select`
const imgElSelector = `#face${which}`
const whichDesc = `desc${which}`
const uri = $(selectElSelector).val()
const imgEl = $(imgElSelector).get(0)
const imgBuf = await fetchImage(uri)
imgEl.src = await bufferToImgSrc(imgBuf)
descriptors[whichDesc] = await computeDescriptorFromSrc($(imgElSelector).get(0))
}
async function run() {
$('#selectList1 select').val(getImageUri('sheldon', 1))
$('#selectList1 select').material_select()
$('#selectList2 select').val(getImageUri('howard', 1))
$('#selectList2 select').material_select()
net = await initNet()
$('#loader').hide()
await onSelectionChanged(1)
await onSelectionChanged(2)
updateResult()
}
$(document).ready(function() {
renderSelectList($('#selectList1').get(0))
renderSelectList($('#selectList2').get(0))
$('select').material_select()
$('#selectList1 select').on('change', async () => {
await onSelectionChanged(1)
updateResult()
})
$('#selectList2 select').on('change', async () => {
await onSelectionChanged(2)
updateResult()
})
run()
})
</script>
</body>
</html>
\ No newline at end of file
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