OpenCV—Node.js教程系列:Node.js+OpenCV面部识别
2017年12月15日 由 xiaoshan.xiang 发表
11822
0
最近我将OpenCV普通发布版本设计的面部识别算法添加到了opencv4nodejs,它是一个npm包,允许你在Node.js应用程序中使用OpenCV。今天,我们将看一下在OpenCVs的面部模块中实现的Fisher -、Eigen -和LBPH facerecognizer,并构建一个简单的Node.js面部识别的例子。在我的github repo上可以找到示例的源代码。我们不要再浪费时间了,开始吧!
OpenCV普通发布版本地址:
https://docs.opencv.org/3.1.0/d3/d81/tutorial_contrib_root.html
opencv4nodejs地址:
https://github.com/justadudewhohacks/opencv4nodejs
OpenCVs面部模块中的实现地址:
https://docs.opencv.org/2.4/modules/contrib/doc/facerec/facerec_tutorial.html
github repo地址:
https://github.com/justadudewhohacks/opencv4nodejs/tree/master/examples
1 .工作准备图像数据
在我们对识别器进行训练之前,必须收集一些面部图像数据。如果你像我一样对
行尸走肉(美国电视剧)感到兴奋,那么你很可能对我们的测试对象很熟悉。我收集了Daryl,Rick和邪恶少年Negan的图像,每人4张,总共12张。
简单地从网络中选取一些图像,我们必须提取以每个图像中显示的字符为中心的子图像。因此,我们将使用OpenCV的CascadeClassifier类来检测人物的面部:
const classifier = new cv.CascadeClassifier(cv.HAAR_FRONTALFACE_ALT2);
const getFaceImage = (grayImg) => {
const faceRects = classifier.detectMultiScale(grayImg).objects;
if (!faceRects.length) {
throw new Error('failed to detect faces');
}
return grayImg.getRegion(faceRects[0]);
};
CascadeClassifier可以用于目标检测,它是从一个包含训练模型表示的
xml文件中创建的。OpenCV为不同的使用案例提供了一些预先训练的模型,如面部检测、人眼检测、全身检测等。为了检测到面部,我们将使用
HAAR_FRONTALFACE_ALT2模型。给定一个灰度图像,
detectMultiScale将返回图像中潜在面部的边界矩形。我们可以简单地获取第一个最佳检测结果,并返回矩形覆盖的子图像。
图像上标上
daryl ,rick ,negan ,n从1 - 4。我们将阅读这些图片并将它们分成一组训练和测试样本如下:
const basePath = '../data/face-recognition';
const imgsPath = path.resolve(basePath, 'imgs');
const nameMappings = ['daryl', 'rick', 'negan'];
const imgFiles = fs.readdirSync(imgsPath);
const images = imgFiles
// get absolute file path
.map(file => path.resolve(imgsPath, file))
// read image
.map(filePath => cv.imread(filePath))
// face recognizer works with gray scale images
.map(img => img.bgrToGray())
// detect and extract face
.map(getFaceImage)
// face images must be equally sized
.map(faceImg => faceImg.resize(80, 80));
const isImageFour = (_, i) => imgFiles[i].includes('4');
const isNotImageFour = (_, i) => !isImageFour(_, i);
// use images 1 - 3 for training
const trainImages = images.filter(isNotImageFour);
// use images 4 for testing
const testImages = images.filter(isImageFour);
// make labels
const labels = imgFiles
.filter(isNotImageFour)
.map(file => nameMappings.findIndex(name => file.includes(name)));
将给出以下图像:
调整图像的大小是必要的,因为识别器希望图像的大小相等。我们将使用每个角色的前3张图像进行训练,第4张用来测试识别器(第19 - 24行)。最后,我们必须标记数据(第26 - 28行)。为了训练识别器,我们需要给它提供一个图像数组(
训练图像)和一个将相应标签保存为数字(
标签)的数组。数据应该是这样的:
训练图像:
[Rick1, Rick2, Rick3, Daryl1, Daryl2, Daryl3, Negan1, Negan2, Negan3]
标签:
[0, 0, 0, 1, 1, 1, 2, 2, 2]
2.训练识别器
现在我们已经准备好了数据,我们将初始化识别器并对它们进行训练:
const eigen = new cv.EigenFaceRecognizer();
const fisher = new cv.FisherFaceRecognizer();
const lbph = new cv.LBPHFaceRecognizer();
eigen.train(trainImages, labels);
fisher.train(trainImages, labels);
lbph.train(trainImages, labels);
还可以将一些参数传递给识别器的构造函数来对它们进行微调,但为了简单起见,我们将使用默认设置。从逻辑上讲,
训练方法期望
训练图像和
标签数组的长度相同,
标签数组必须包含至少2个不同的标签。
3.识别面部
就是这样!我们现在可以运行我们测试图像的预测:
const runPrediction = (recognizer) => {
testImages.forEach((img) => {
const result = recognizer.predict(img);
console.log('predicted: %s, confidence: %s', nameMappings[result.label], result.confidence);
cv.imshowWait('face', img);
cv.destroyAllWindows();
});
};
console.log('eigen:');
runPrediction(eigen);
console.log('fisher:');
runPrediction(fisher);
console.log('lbph:');
runPrediction(lbph);
运行这个示例应该给出如下输出:
eigen:
predicted daryl to be: daryl, confidence: 1245.68
predicted negan to be: negan, confidence: 2247.25
predicted rick to be: negan, confidence: 2502.47
fisher:
predicted daryl to be: daryl, confidence: 452.15
predicted negan to be: negan, confidence: 464.76
predicted rick to be: rick, confidence: 831.38
lbph:
predicted daryl to be: daryl, confidence: 108.37
predicted negan to be: negan, confidence: 119.33
predicted rick to be: rick, confidence: 105.65
每个类(角色)仅使用3个图像,我们就可以得到很好的结果。Opencv4nodejs是一个npm包,将 Node.js绑定到OpenCV,并且OpenCV普通发布版通过异步API设计。该包将本机OpenCV库的所有性能优势都带到 Node.js应用程序,允许通过Promise轻松实现多线程的CV任务。