人脸识别技术一直备受追捧,但训练步骤是其生产就绪的一大障碍。为了绕过这个障碍,让向介绍face-api.js,这是一个基于TensorFlow.js实现的JavaScript人脸识别库。
face-api.js功能强大且易于使用,它只向展示了配置所需的内容。它实现了几种卷积神经网络(CNN),但隐藏了编写神经网络以解决面部检测、识别和地标检测的所有底层层。可以从这里获取库和模型。
在深入任何代码之前,需要设置一个服务器。如果一直在关注,会看到通过直接在浏览器中工作而保持了简单,而没有设置服务器。这种方法直到现在都有效,但如果尝试将face-api.js模型直接服务到浏览器,那么会遇到错误,因为HTML5要求从Web服务器提供网页和图像。
设置服务器的一个简单方法是设置Chrome的Web服务器。启动服务器并按照如下设置:
现在可以通过访问http://127.0.0.1:8887来访问应用程序。
一旦设置了服务器,创建一个HTML文档并导入face-api库:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script type="application/x-javascript" src="face-api.js"></script>
</head>
<body>
<h1>使用face-api.js进行实时情绪检测</h1>
<video autoplay muted id="video" width="224" height="224" style="margin: auto;"></video>
<div id="prediction">加载中</div>
<script type="text/javascript" defer src="index.js"></script>
</body>
</html>
还需要包括一个视频标签和处理应用程序逻辑的JavaScript文件。这是文档的最终外观。
让继续到JavaScript文件并定义一些重要的变量:
const video = document.getElementById("video");
const text = document.getElementById("prediction");
现在设置函数来开始视频:
function startVideo() {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({ video: true }, function(stream) {
var video = document.querySelector('video');
video.srcObject = stream;
video.onloadedmetadata = function(e) {
video.play();
};
}, function(err) {
console.log(err.name);
});
} else {
document.body.innerText = "getUserMedia not supported";
console.log("getUserMedia not supported");
}
}
使用face-api.js进行预测。与其下载所有模型,可以像这样从URI加载它们:
let url = "https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/";
faceapi.nets.tinyFaceDetector.loadFromUri(url + 'tiny_face_detector_model-weights_manifest.json'),
faceapi.nets.faceLandmark68Net.loadFromUri(url + 'face_landmark_68_model-weights_manifest.json'),
faceapi.nets.faceRecognitionNet.loadFromUri(url + 'face_recognition_model-weights_manifest.json'),
faceapi.nets.faceExpressionNet.loadFromUri(url + 'face_expression_model-weights_manifest.json')
face-api.js将情绪分类为七个类别:快乐、悲伤、愤怒、厌恶、恐惧、中性和惊讶。由于对两个主要类别感兴趣,即中性和不高兴,将top_prediction设置为中性。一个人通常被认为是不高兴的,当他们是悲伤的、愤怒的或厌恶的,所以如果这些情绪中的任何一个的预测分数大于中性的预测分数,那么将显示预测的情绪为不高兴。让以字符串格式获取顶级预测:
function prediction_string(obj) {
let top_prediction = "neutral";
let maxVal = 0;
var str = top_prediction;
if (!obj) return str;
obj = obj.expressions;
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
if (obj[p] > maxVal) {
maxVal = obj[p];
top_prediction = p;
if (p === obj.sad || obj.disgusted || obj.angry){
top_prediction = "grumpy";
}
}
}
}
return top_prediction;
}
最后,需要从face-api模型中获取预测:
const predictions = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
.withFaceExpressions();
代码整合在一起看起来像这样:
const video = document.getElementById("video");
const canvas = document.getElementById("canvas");
const text = document.getElementById("prediction");
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
faceapi.nets.faceRecognitionNet.loadFromUri('/models'),
faceapi.nets.faceExpressionNet.loadFromUri('/models'),
]).then(startVideo);
function prediction_string(obj) {
let top_prediction = "neutral";
let maxVal = 0;
var str = top_prediction;
if (!obj) return str;
obj = obj.expressions;
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
if (obj[p] > maxVal) {
maxVal = obj[p];
top_prediction = p;
if (p === obj.sad || obj.disgusted || obj.angry){
top_prediction = "grumpy";
}
}
}
}
return top_prediction;
}
function startVideo() {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({ video: true }, function(stream) {
var video = document.querySelector('video');
video.srcObject = stream;
video.onloadedmetadata = function(e) {
video.play();
};
}, function(err) {
console.log(err.name);
});
} else {
document.body.innerText = "getUserMedia not supported";
console.log("getUserMedia not supported");
}
}
video.addEventListener("play", () => {
let visitedMsg = true;
setInterval(async () => {
const predictions = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
.withFaceExpressions();
if (visitedMsg) {
text.innerText = "Your expression";
visitedMsg = false;
}
text.innerHTML = prediction_string(predictions[0]);
}, 100);
});