【教程】OpenCV—Node.js教程系列:Node.js+OpenCV面部臉識別
最近我將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
影象上標上daryl <n >,rick <n >,negan <n >,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任務。