使用OpenCV和Python进行人脸识别
What is face recognition? Or what is recognition? When you look at an apple fruit, your mind immediately tells you that this is an apple fruit. This process, your mind telling you that this is an apple fruit is recognition in simple words. So what is face recognition then? I am sure you have guessed it right. When you look at your friend walking down the street or a picture of him, you recognize that he is your friend Paulo. Interestingly when you look at your friend or a picture of him you look at his face first before looking at anything else. Ever wondered why you do that? This is so that you can recognize him by looking at his face. Well, this is you doing face recognition.
什么是人臉識別? 還是什么是認可? 當您查看一個蘋果果實時,您的大腦會立即告訴您這是一個蘋果果實。 這個過程,您的大腦告訴您這是一個蘋果果實,簡而言之就是識別。 那么,什么是人臉識別呢? 我相信你猜對了。 當您看著在街上行走的朋友或他的照片時,您會發現他是您的朋友Paulo。 有趣的是,當您看著您的朋友或他的照片時,您首先要看他的臉,然后再看其他任何東西。 有沒有想過為什么要這么做? 這樣一來,您可以通過看著他的臉認出他。 好吧,這就是您要進行人臉識別。
But the real question is how does face recognition works? It is quite simple and intuitive. Take a real-life example, when you meet someone first time in your life you don’t recognize him, right? While he talks or shakes hands with you, you look at his face, eyes, nose, mouth, color, and overall look. This is your mind learning or training for the face recognition of that person by gathering face data. Then he tells you that his name is Paulo. At this point, your mind knows that the face data it just learned belongs to Paulo. Now your mind is trained and ready to do face recognition on Paulo’s face. Next time when you will see Paulo or his face in a picture you will immediately recognize him. This is how to face recognition work. The more you will meet Paulo, the more data your mind will collect about Paulo and especially his face, and the better you will become at recognizing him.
但是真正的問題是面部識別如何工作? 這非常簡單直觀。 以一個現實生活中的例子為例,當您第一次遇到某人時,您不會認出他,對嗎? 當他與您交談或握手時,您會看著他的臉,眼睛,鼻子,嘴巴,顏色和整體外觀。 這是您通過收集面部數據來學習或培訓該人的面部識別的思想。 然后他告訴你,他的名字叫Paulo。 至此,您的大腦已經知道,剛剛學習的面部數據屬于Paulo。 現在,您的思想已經訓練完畢,可以在Paulo的臉上進行臉部識別了。 下次當您在圖片中看到Paulo或他的臉時,您將立即識別出他。 這是面對識別工作的方式。 您遇到Paulo的次數越多,您的思想就可以收集到有關Paulo特別是他的面Kong的更多數據,并且您越能識別他。
Now the next question is how to code face recognition with OpenCV, after all this is the only reason why you are reading this article, right? OK then. You might say that our mind can do these things easily but to actually code them into a computer is difficult? Don’t worry, it is not. Thanks to OpenCV, coding face recognition is as easier as it feels. The coding steps for face recognition are the same as we discussed in the real-life example above.
現在,下一個問題是如何使用OpenCV編寫人臉識別代碼,畢竟這是您閱讀本文的唯一原因,對嗎? 那好吧。 您可能會說我們的思想可以輕松地完成這些事情,但是很難將它們實際編碼到計算機中? 不用擔心,事實并非如此。 多虧了OpenCV,對面部識別進行編碼變得像感覺一樣容易。 人臉識別的編碼步驟與我們在上面的實際示例中討論的相同。
Training Data Gathering: Gather face data (face images in this case) of the persons you want to recognize
訓練數據收集:收集您要識別的人員的面部數據(在這種情況下為面部圖像)
Training of Recognizer: Feed that face data (and respective names of each face) to the face recognizer so that it can learn.
識別器的訓練:將面部數據(以及每個面部的相應名稱)輸入到面部識別器,以便可以學習。
Recognition: Feed new faces of the persons and see if the face recognizer you just trained recognizes them.
識別:喂養人員的新面Kong,并查看您剛剛訓練的面部識別器是否能夠識別出他們。
OpenCV comes equipped with a built-in face recognizer, all you have to do is feed it the face data. It’s that simple and this how it will look once we are done coding it.
OpenCV配備了內置的面部識別器,您所要做的就是向其提供面部數據。 就這么簡單,這就是我們完成編碼后的樣子。
OpenCV人臉識別器 (OpenCV Face Recognizers)
OpenCV has three built-in face recognizers and thanks to OpenCV’s clean coding, you can use any of them by just changing a single line of code. Below are the names of those face recognizers and their OpenCV calls.
OpenCV具有三個內置的人臉識別器,并且由于OpenCV簡潔編碼,您只需更改一行代碼即可使用其中任何一個。 以下是這些面部識別器的名稱及其OpenCV調用。
EigenFaces Face Recognizer Recognizer — cv2.face.createEigenFaceRecognizer()
cv2.face.createEigenFaceRecognizer()人臉識別器識別器— cv2.face.createEigenFaceRecognizer()
FisherFaces Face Recognizer Recognizer — cv2.face.createFisherFaceRecognizer()
FisherFaces人臉識別器識別器— cv2.face.createFisherFaceRecognizer()
Local Binary Patterns Histograms (LBPH) Face Recognizer — cv2.face.createLBPHFaceRecognizer()
本地二進制模式直方圖(LBPH)人臉識別器— cv2.face.createLBPHFaceRecognizer()
We have got three face recognizers but do you know which one to use and when? Or which one is better? I guess not. So why not go through a brief summary of each, what you say? I am assuming you said yes :) So let’s dive into the theory of each.
我們有3個面部識別器,但是您知道使用哪個以及何時使用嗎? 還是哪個更好? 我猜不會。 那么,為什么不對每個內容都做一個簡短的總結呢? 我假設您說的是:)因此,讓我們深入研究每個概念。
EigenFaces人臉識別器 (EigenFaces Face Recognizer)
This algorithm considers the fact that not all parts of a face are equally important and equally useful. When you look at someone you recognize him/her by his distinct features like eyes, nose, cheeks, forehead, and how they vary with respect to each other. So you are actually focusing on the areas of maximum change (mathematically speaking, this change is variance) of the face. For example, from eyes to nose there is a significant change and the same is the case from nose to mouth. When you look at multiple faces you compare them by looking at these parts of the faces because these parts are the most useful and important components of a face. Important because they catch the maximum change among faces, change the helps you differentiate one face from the other. This is exactly how EigenFaces face recognizer works.
該算法考慮到以下事實:并非面部的所有部分都同樣重要和有用。 當您看著某人時,您會通過他的獨特特征(例如眼睛,鼻子,臉頰,前額以及它們之間的相互差異)來識別他/她。 因此,您實際上是在關注面部的最大變化區域(從數學上來說,這種變化是方差)。 例如,從眼睛到鼻子有很大的變化,從鼻子到嘴也是如此。 當您查看多個面Kong時,可以通過查看面Kong的這些部分進行比較,因為這些部分是面Kong中最有用和最重要的組成部分。 這很重要,因為它們可以捕捉到面Kong之間的最大變化,因此更改有助于您將一張面Kong與另一張面Kong區分開。 這正是EigenFaces人臉識別器的工作原理。
EigenFaces face recognizer looks at all the training images of all the persons as a whole and tries to extract the components which are important and useful (the components that catch the maximum variance/change) and discards the rest of the components. This way it not only extracts the important components from the training data but also saves memory by discarding the less important components. These important components it extracts are called principal components. Below is an image showing the principal components extracted from a list of faces.
EigenFaces人臉識別器會整體上查看所有人員的所有訓練圖像,并嘗試提取重要且有用的組件(捕獲最大方差/變化的組件),并丟棄其余組件。 這樣,它不僅從訓練數據中提取重要組成部分,而且還通過丟棄次要重要組成部分來節省內存。 它提取的這些重要成分稱為主成分 。 下圖顯示了從面部列表中提取的主要成分。
Principal Components, 主要成分, source 來源You can see that the principal components actually represent faces and these faces are called eigenfaces and hence the name of the algorithm.
您可以看到,主要成分實際上代表人臉,這些人臉被稱為本征人臉 ,因此是算法的名稱。
So this is how EigenFaces face recognizer trains itself (by extracting principal components). Remember, it also keeps a record of which principal component belongs to which person. One thing to note in the above image is that the Eigenfaces algorithm also considers illumination as an important component.
因此,這就是EigenFaces人臉識別器如何進行自身訓練(通過提取主要成分)。 請記住,它還會記錄哪個主要組成部分屬于哪個人。 上圖中需要注意的一件事是,特征臉算法也將照明視為重要組成部分 。
Later during recognition, when you feed a new image to the algorithm, it repeats the same process on that image as well. It extracts the principal component from that new image and compares that component with the list of components it stored during training and finds the component with the best match and returns the person label associated with that best match component.
在識別的后期,當您向算法提供新圖像時,它也會對該圖像重復相同的過程。 它從該新圖像中提取主要成分,并將該成分與其在訓練過程中存儲的成分列表進行比較,找到最匹配的成分,并返回與該最匹配成分關聯的人員標簽。
Easy peasy, right? The next one is easier than this one.
輕輕松松吧? 下一個比這個容易。
FisherFaces人臉識別器 (FisherFaces Face Recognizer)
This algorithm is an improved version of EigenFaces face recognizer. Eigenfaces face recognizer looks at all the training faces of all the persons at once and finds principal components from all of them combined. By capturing principal components from all the of them combined you are not focusing on the features that discriminate one person from the other but the features that represent all the persons in the training data as a whole.
該算法是EigenFaces人臉識別器的改進版本。 特征臉識別器一次查看所有人員的所有訓練臉,然后從所有結合的人中找到主要成分。 通過從所有組合中獲取主要組成部分,您將不會專注于將一個人與另一個人區分開的功能,而是將整個訓練數據中代表所有人的功能都放在了一邊。
This approach has drawbacks, for example, images with sharp changes (like light changes which is not a useful feature at all) may dominate the rest of the images and you may end up with features that are from an external source like light and are not useful for discrimination at all. In the end, your principal components will represent light changes and not the actual facial features.
這種方法有一些缺點,例如, 具有劇烈變化的圖像(例如光線變化根本不是有用的功能)可能會主導其余圖像,并且您最終可能會獲得來自外部光源(例如光線)的特征,而這些特征卻并非如此。對歧視很有用。 最后,您的主要成分將代表光線變化,而不是實際的面部特征。
Fisher faces algorithm, instead of extracting useful features that represent all the faces of all the persons, it extracts useful features that discriminate one person from the others. This way features of one person do not dominate over the others and you have the features that discriminate one person from the others.
Fisher人臉算法沒有提取代表所有人的所有面Kong的有用特征,而是提取了將一個人與其他人區分開的有用特征。 這樣,一個人的功能不會主導其他人,而您卻具有將一個人與其他人區分開的功能。
Below is an image of features extracted using the Fisherfaces algorithm.
下圖是使用Fisherfaces算法提取的特征圖像。
Fisher Faces, Fisher Faces, source 來源You can see that features extracted actually represent faces and these faces are called fisher faces and hence the name of the algorithm.
您可以看到提取的特征實際上代表了面Kong,這些面Kong稱為費舍爾面Kong ,因此是算法的名稱。
One thing to note here is that even in the Fisherfaces algorithm if multiple persons have images with sharp changes due to external sources like the light they will dominate over other features and affect recognition accuracy.
這里要注意的一件事是, 即使在Fisherfaces算法中,如果多個人的圖像由于外部光源(例如光源)而具有急劇變化的圖像,它們也會在其他特征上占主導地位并影響識別精度 。
Getting bored with this theory? Don’t worry, only one face recognizer is left and then we will dive deep into the coding part.
厭倦了這種理論? 不用擔心,只剩下一個人臉識別器,然后我們將深入研究編碼部分。
本地二進制模式直方圖(LBPH)人臉識別器 (Local Binary Patterns Histograms (LBPH) Face Recognizer)
I wrote a detailed explanation of Local Binary Patterns Histograms in my previous article on face detection using local binary pattern histograms. So here I will just give a brief overview of how it works.
我在上一篇有關使用局部二進制模式直方圖的人臉檢測的文章中寫了局部二進制模式直方圖的詳細說明。 因此,在這里我將簡要概述其工作原理。
We know that Eigenfaces and Fisherfaces are both affected by light and in real life, we can’t guarantee perfect light conditions. LBPH face recognizer is an improvement to overcome this drawback.
我們知道本征面和Fisherfaces都受光照影響,在現實生活中,我們不能保證完美的光照條件。 LBPH人臉識別器是克服此缺點的一項改進。
The idea is to not look at the image as a whole instead of finding the local features of an image. LBPH algorithm try to find the local structure of an image and it does that by comparing each pixel with its neighboring pixels.
這個想法是不要整體看圖像,而不是尋找圖像的局部特征。 LBPH算法嘗試查找圖像的局部結構,并通過將每個像素與其相鄰像素進行比較來做到這一點。
Take a 3x3 window and move it one image, at each move (each local part of an image), compare the pixel at the center with its neighbor pixels. The neighbors with intensity value less than or equal to a center pixels are denoted by 1 and others by 0. Then you read these 0/1 values under a 3x3 window in clockwise order and you will have a binary pattern like 11100011 and this pattern is local to some area of the image. You do this on the whole image and you will have a list of local binary patterns.
取一個3x3的窗口并將其移動一張圖像,每次移動(圖像的每個局部),將中心的像素與其相鄰像素進行比較。 強度值小于或等于中心像素的鄰居用1表示,其他用0表示。然后在3x3窗口下按順時針順序讀取這些0/1值,將得到一個像11100011這樣的二進制模式,該模式為局部于圖像的某些區域。 您在整個圖像上執行此操作,并且將具有本地二進制模式的列表。
LBP Labeling LBP標簽Now you get why this algorithm has Local Binary Patterns in its name? Because you get a list of local binary patterns. Now you may be wondering, what about the histogram part of the LBPH? Well after you get a list of local binary patterns, you convert each binary pattern into a decimal number (as shown in the above image) and then you make a histogram of all of those values. A sample histogram looks like this.
現在您了解了為什么該算法的名稱中包含“本地二進制模式”? 因為您可以獲得本地二進制模式的列表。 現在您可能想知道,LBPH的直方圖部分如何? 在獲得本地二進制模式列表之后,可以將每個二進制模式轉換為十進制數(如上圖所示),然后對所有這些值進行直方圖繪制。 樣本直方圖如下所示。
Sample Histogram 直方圖樣本I guess this answers the question about the histogram part. So in the end you will have one histogram for each face image in the training data set. That means if there were 100 images in training data set then LBPH will extract 100 histograms after training and store them for later recognition. Remember, the algorithm also keeps track of which histogram belongs to which person.
我猜這回答了有關直方圖部分的問題。 因此,最后,您將在訓練數據集中為每個面部圖像創建一個直方圖 。 這意味著如果訓練數據集中有100張圖像,則LBPH將在訓練后提取100個直方圖并將其存儲以供以后識別。 請記住,該算法還跟蹤哪個直方圖屬于哪個人 。
Later during recognition, when you will feed a new image to the recognizer for the recognition it will generate a histogram for that new image, compare that histogram with the histograms it already has, finds the best match histogram and return the person label associated with that best match histogram.
稍后在識別期間,當您將新圖像提供給識別器進行識別時,它將為該新圖像生成一個直方圖,將該直方圖與已有的直方圖進行比較,找到最匹配的直方圖,并返回與該圖像相關聯的人員標簽最佳匹配直方圖。
Below is a list of faces and their respective local binary patterns images. You can see that the LBP images are not affected by changes in light conditions.
以下是面Kong及其各自的本地二進制圖案圖像的列表。 您可以看到LBP圖像不受光照條件變化的影響。
LBP Faces, LBP Faces, source 來源The theory part is over and now comes the coding part! Ready to dive into coding? Let’s get into it then.
理論部分已經結束,現在是編碼部分! 準備開始編寫代碼了嗎? 讓我們開始吧。
使用OpenCV編碼人臉識別 (Coding Face Recognition with OpenCV)
The Face Recognition process in this tutorial is divided into three steps.
本教程中的人臉識別過程分為三個步驟。
Prepare training data: In this step, we will read training images for each person/subject along with their labels, detect faces from each image, and assign each detected face an integer label of the person it belongs to.
準備訓練數據:在此步驟中,我們將讀取每個人/對象的訓練圖像及其標簽,從每個圖像中檢測面部,并為每個檢測到的面部分配一個所屬人員的整數標簽。
Train Face Recognizer: In this step, we will train OpenCV’s LBPH face recognizer by feeding it the data we prepared in step 1.
訓練人臉識別器:在這一步中,我們將向OpenCVLBPH人臉識別器提供在步驟1中準備的數據,以對其進行訓練。
Testing: In this step, we will pass some test images to face recognizer and see if it predicts them correctly.
測試:在此步驟中,我們會將一些測試圖像傳遞給人臉識別器,看看它是否正確預測了它們。
To detect faces, I will use the code from my previous article on face detection. So if you have not read it, I encourage you to do so to understand how to face detection works and its Python coding.
為了檢測面部,我將使用上一篇有關面部檢測的文章中的代碼。 因此,如果您還沒有閱讀它,我建議您這樣做,以了解臉部檢測的工作原理及其Python編碼。
導入所需的模塊 (Import Required Modules)
Before starting the actual coding we need to import the required modules for coding. So let’s import them first.
在開始實際編碼之前,我們需要導入所需的編碼模塊。 因此,讓我們先導入它們。
cv2: is the OpenCV module for Python which we will use for face detection and face recognition.
cv2:是Python的OpenCV模塊,我們將使用它進行面部檢測和面部識別。
os: We will use this Python module to read our training directories and file names.
os:我們將使用此Python模塊讀取我們的訓練目錄和文件名。
NumPy: We will use this module to convert Python lists to NumPy arrays as OpenCV face recognizers accept NumPy arrays.
NumPy:當OpenCV人臉識別器接受NumPy數組時,我們將使用此模塊將Python列表轉換為NumPy數組。
import cv2
#import os module for reading training data directories and paths
import os
#import numpy to convert python lists to numpy arrays as
#it is needed by OpenCV face recognizers
import numpy as np
#matplotlib for display our images
import matplotlib.pyplot as plt
%matplotlib inline
訓練數據 (Training Data)
The more images used in training the better. Normally a lot of images are used for training a face recognizer so that it can learn different looks of the same person, for example with glasses, without glasses, laughing, sad, happy, crying, with a beard, without a beard, etc. To keep our tutorial simple we are going to use only 12 images for each person.
訓練中使用的圖像越多越好。 通常,很多圖像用于訓練面部識別器,以便可以識別同一個人的不同外觀,例如戴著眼鏡,不戴眼鏡,笑,悲傷,快樂,哭泣,有胡須,沒有胡須等。為了使我們的教程簡單,我們將只為每個人使用12張圖像。
So our training data consists of a total of 2 persons with 12 images of each person. All training data is inside the folder. training-data folder contains one folder for each person and each folder is named with a format sLabel (e.g. s1, s2) where the label is actually the integer label assigned to that person. For example, a folder named s1 means that this folder contains images for person 1. The directory structure tree for training data is as follows:
因此,我們的訓練數據總共由2個人組成,每人12張圖片。 所有訓練數據都在文件夾內。 training-data文件夾為每個人包含一個文件夾,并且每個文件夾都以 sLabel (eg s1, s2) 格式 sLabel (eg s1, s2) ,其中標簽實際上是分配給該人的整數標簽 。 例如,名為s1的文件夾意味著該文件夾包含人員1的圖像。訓練數據的目錄結構樹如下:
training-data|-------------- s1
| |-- 1.jpg
| |-- ...
| |-- 12.jpg
|-------------- s2
| |-- 1.jpg
| |-- ...
| |-- 12.jpg
The folder contains images that we will use to test our face recognizer after it has been successfully trained.
該文件夾包含成功訓練后將用于測試面部識別器的圖像。
As OpenCV face recognizer accepts labels as integers so we need to define a mapping between integer labels and persons actual names so below I am defining a mapping of persons integer labels and their respective names.
由于OpenCV人臉識別器接受標簽為整數,因此我們需要定義整數標簽與人員實際姓名之間的映射,因此下面我定義人員整數標簽及其相應名稱的映射。
Note: As we have not assigned label 0 to any person so the mapping for label 0 is empty.
注意:由于我們尚未將label 0分配給任何人,因此標簽0的映射為空 。
#there is no label 0 in our training data so subject name for #index/label 0 is emptysubjects = ["", "Tom Cruise", "Shahrukh Khan"]
準備訓練數據 (Prepare training data)
You may be wondering why data preparation, right? Well, OpenCV face recognizer accepts data in a specific format. It accepts two vectors, one vector is of faces of all the persons and the second vector is of integer labels for each face so that when processing a face the face recognizer knows which person that particular face belongs too.
您可能想知道為什么要準備數據,對嗎? 好吧,OpenCV人臉識別器可以接受特定格式的數據。 它接受兩個向量,一個向量是所有人的臉,第二個向量是每個人臉的整數標簽,因此在處理人臉時,人臉識別器也知道該特定人臉屬于哪個人。
For example, if we had 2 persons and 2 images for each person.
例如,如果我們有2個人,每個人有2張圖片。
PERSON-1 PERSON-2img1 img1
img2 img2
Then the prepare data step will produce the following face and label vectors.
然后,準備數據步驟將產生以下面部和標簽向量。
FACES LABELSperson1_img1_face 1
person1_img2_face 1
person2_img1_face 2
person2_img2_face 2
Preparing the data step can be further divided into the following sub-steps.
準備數據的步驟可以進一步分為以下子步驟。
Read all the folder names of subjects/persons provided in the training data folder. So for example, in this tutorial we have folder names: s1, s2.
閱讀培訓數據文件夾中提供的所有科目/人員的文件夾名稱。 因此,例如,在本教程中,我們具有文件夾名稱: s1, s2 。
For each subject, extract the label number. Do you remember that our folders have a special naming convention? Folder names follow the format sLabel where Label is an integer representing the label we have assigned to that subject. So for example, folder name s1 means that the subject has label 1, s2 means the subject label is 2, and so on. The label extracted in this step is assigned to each face detected in the next step.
對于每個主題,提取標簽編號。 您還記得我們的文件夾有特殊的命名約定嗎? 文件夾名稱遵循sLabel格式,其中Label是一個整數,表示我們已分配給該主題的標簽。 因此,例如,文件夾名稱s1表示主題具有標簽1,s2表示主題標簽為2,依此類推。 在此步驟中提取的標簽被分配給在下一步中檢測到的每個面部。
Did you read my last article on face detection? No? Then you better do so right now because to detect faces, I am going to use the code from my previous article on face detection. So if you have not read it, I encourage you to do so to understand how to face detection works and its coding. Below is the same code.
您讀過我上一篇有關面部檢測的文章嗎? 沒有? 那么您最好現在就這樣做,因為要檢測面部,我將使用上一篇有關面部檢測的文章中的代碼。 因此,如果您還沒有閱讀它,我鼓勵您這樣做,以了解臉部檢測的工作原理及其編碼。 下面是相同的代碼。
#function to detect face using OpenCVdef detect_face(img):
#convert the test image to gray image as opencv face detector expects gray images
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#load OpenCV face detector, I am using LBP which is fast
#there is also a more accurate but slow Haar classifier
face_cascade = cv2.CascadeClassifier('opencv-files/lbpcascade_frontalface.xml')
#let's detect multiscale (some images may be closer to camera than others) images
#result is a list of faces
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5);
#if no faces are detected then return original img
if (len(faces) == 0):
return None, None
#under the assumption that there will be only one face,
#extract the face area
(x, y, w, h) = faces[0]
#return only the face part of the image
return gray[y:y+w, x:x+h], faces[0]
I am using OpenCV’s LBP face detector. On line 4, I convert the image to grayscale because most operations in OpenCV are performed in grayscale, then on line 8, I load LBP face detector using cv2.CascadeClassifier class. After that online 12, I use cv2.CascadeClassifier class' detectMultiScale method to detect all the faces in the image. on line 20, from detected faces, I only pick the first face because in one image there will be only one face (under the assumption that there will be only one prominent face). As faces returned by detectMultiScale the method are actually rectangles (x, y, width, height) and not actual faces images so we have to extract face image area from the main image. So on line 23 I extract face area from the gray image and return both the face image area and face rectangle.
我正在使用OpenCVLBP人臉檢測器 。 在第4行,我將圖像轉換為灰度,因為OpenCV中的大多數操作都是在灰度下執行的,然后在第8行,我使用cv2.CascadeClassifier類加載了LBP人臉檢測器。 在在線12之后,我使用cv2.CascadeClassifier類的detectMultiScale方法檢測圖像中的所有面Kong。 在第20行中 ,我僅從檢測到的面部中選擇第一張面部,因為在一個圖像中將只有一個面部(在假設只有一個突出面部的情況下)。 由于detectMultiScale返回的detectMultiScale實際上是矩形(x,y,寬度,高度),而不是實際的面部圖像,因此我們必須從主圖像中提取面部圖像區域。 因此,在第23行,我從灰度圖像中提取了臉部區域,并返回了臉部圖像區域和臉部矩形。
Now you have got a face detector and you know the 4 steps to prepare the data, so are you ready to code the prepare data step? Yes? So let’s do it.
現在您已經有了一個面部檢測器,并且您知道準備數據的四個步驟,那么您準備好編寫準備數據的步驟了嗎? 是? 因此,讓我們開始吧。
#this function will read all persons' training images, detect face from each image#and will return two lists of exactly same size, one list
# of faces and another list of labels for each face
def prepare_training_data(data_folder_path):
#------STEP-1--------
#get the directories (one directory for each subject) in data folder
dirs = os.listdir(data_folder_path)
#list to hold all subject faces
faces = []
#list to hold labels for all subjects
labels = []
#let's go through each directory and read images within it
for dir_name in dirs:
#our subject directories start with letter 's' so
#ignore any non-relevant directories if any
if not dir_name.startswith("s"):
continue;
#------STEP-2--------
#extract label number of subject from dir_name
#format of dir name = slabel
#, so removing letter 's' from dir_name will give us label
label = int(dir_name.replace("s", ""))
#build path of directory containin images for current subject subject
#sample subject_dir_path = "training-data/s1"
subject_dir_path = data_folder_path + "/" + dir_name
#get the images names that are inside the given subject directory
subject_images_names = os.listdir(subject_dir_path)
#------STEP-3--------
#go through each image name, read image,
#detect face and add face to list of faces
for image_name in subject_images_names:
#ignore system files like .DS_Store
if image_name.startswith("."):
continue;
#build image path
#sample image path = training-data/s1/1.pgm
image_path = subject_dir_path + "/" + image_name
#read image
image = cv2.imread(image_path)
#display an image window to show the image
cv2.imshow("Training on image...", image)
cv2.waitKey(100)
#detect face
face, rect = detect_face(image)
#------STEP-4--------
#for the purpose of this tutorial
#we will ignore faces that are not detected
if face is not None:
#add face to list of faces
faces.append(face)
#add label for this face
labels.append(label)
cv2.destroyAllWindows()
cv2.waitKey(1)
cv2.destroyAllWindows()
return faces, labels
I have defined a function that takes the path, where training subjects’ folders are stored, as parameters. This function follows the same 4 prepare data substeps mentioned above.
我定義了一個函數,該函數采用存儲培訓對象文件夾的路徑作為參數。 此功能遵循上述相同的4個準備數據子步驟。
(step-1) On line 8 I am using os.listdir a method to read names of all folders stored on path passed to function as a parameter. On line 10-13 I am defining labels and faces vectors.
(步驟1)在第8行上,我正在使用os.listdir方法來讀取存儲在傳遞為參數的路徑上的所有文件夾的名稱。 在10-13行中,我正在定義標簽和面向量。
(step-2) After that I traverse through all subjects’ folder names and from each subject’s folder name on line 27 I am extracting the label information. As folder names follow the sLabel naming convention so removing the letter s from folder name will give us the label assigned to that subject.
(步驟2)之后,遍歷所有主題的文件夾名稱,并從第27行的每個主題的文件夾名稱中遍歷,然后提取標簽信息。 由于文件夾名稱遵循sLabel命名約定,因此從文件夾名稱中刪除字母s將為我們分配給該主題的標簽。
(step-3) On line 34, I read all the images names of the current subject being traversed, and on lines 39–66 I traverse those images one by one. On line 53–54 I am using OpenCV’s imshow(window_title, image) along with OpenCV's waitKey(interval) method to display the current image being traversed. The waitKey(interval) method pauses the code flow for the given interval (milliseconds), I am using it with 100ms interval so that we can view the image window for 100ms. On line 57, I detect face from the current image being traversed.
(第3步)在第34行上 ,我讀取了正在遍歷的當前對象的所有圖像名稱,在第39-66行上,我一張遍了遍歷了這些圖像。 在第53–54行中,我使用OpenCVimshow(window_title, image)以及OpenCVwaitKey(interval)方法來顯示正在遍歷的當前圖像。 waitKey(interval)方法將代碼流暫停給定的間隔(毫秒),我以100ms的間隔使用它,以便我們可以查看圖像窗口100ms。 在第57行 ,我從正在遍歷的當前圖像中檢測到人臉。
(step-4) On line 62–66, I add the detected face and label to their respective vectors.
(第4步)在第62–66行上 ,我將檢測到的面部和標簽添加到它們各自的向量中。
But a function can’t do anything unless we call it on some data that it has to prepare, right? Don’t worry, I have got data of two beautiful and famous celebrities. I am sure you will recognize them!
但是,除非我們在必須準備的數據上調用它,否則函數將無法執行任何操作,對嗎? 別擔心,我有兩個美麗而著名的名人的數據。 我相信您會認出他們!
Let’s call this function on images of these beautiful celebrities to prepare data for the training of our Face Recognizer. Below is a simple code to do that.
讓我們在這些美麗名人的圖像上調用此功能,以準備用于訓練人臉識別器的數據。 以下是執行此操作的簡單代碼。
#let's first prepare our training data#data will be in two lists of same size
#one list will contain all the faces
#and other list will contain respective labels for each face
print("Preparing data...")
faces, labels = prepare_training_data("training-data")
print("Data prepared")
#print total faces and labels
print("Total faces: ", len(faces))
print("Total labels: ", len(labels))
Output
輸出量
Preparing data...Data prepared
Total faces: 23
Total labels: 23
This was probably the boring part, right? Don’t worry, the fun stuff is coming up next. It’s time to train our own face recognizer so that once trained it can recognize new faces of the persons it was trained on. Read? Ok then let’s train our face recognizer.
這可能是無聊的部分,對吧? 不用擔心,接下來會出現有趣的東西。 現在該訓練我們自己的面部識別器了,以便一旦受過訓練就可以識別受過訓練的人員的新面Kong。 讀? 好吧,讓我們訓練我們的面部識別器。
火車面部識別器 (Train Face Recognizer)
As we know, OpenCV comes equipped with three face recognizers.
眾所周知,OpenCV配備了三個面部識別器。
EigenFace Recognizer: This can be created with cv2.face.createEigenFaceRecognizer()
EigenFace Recognizer:可以使用cv2.face.createEigenFaceRecognizer()創建
FisherFace Recognizer: This can be created with cv2.face.createFisherFaceRecognizer()
FisherFace Recognizer:可以使用cv2.face.createFisherFaceRecognizer()創建
Local Binary Patterns Histogram (LBPH): This can be created with cv2.face.LBPHFisherFaceRecognizer()
本地二進制模式直方圖(LBPH):可以使用cv2.face.LBPHFisherFaceRecognizer()創建
I am going to use LBPH face recognizer but you can use any face recognizer of your choice. No matter which of the OpenCV’s face recognizer you use the code will remain the same. You just have to change one line, the face recognizer initialization line given below.
我將使用LBPH人臉識別器,但您可以使用您選擇的任何人臉識別器。 無論您使用哪種OpenCV面部識別器,代碼都將保持不變。 您只需要更改一行,即下面給出的人臉識別器初始化行。
#create our LBPH face recognizerface_recognizer = cv2.face.createLBPHFaceRecognizer()
#or use EigenFaceRecognizer by replacing above line with
#face_recognizer = cv2.face.createEigenFaceRecognizer()
#or use FisherFaceRecognizer by replacing above line with
#face_recognizer = cv2.face.createFisherFaceRecognizer()
Now that we have initialized our face recognizer and we also have prepared our training data, it’s time to train the face recognizer. We will do that by calling the train(faces-vector, labels-vector) method of face recognizer.
現在我們已經初始化了臉部識別器,并且我們還準備了訓練數據,現在該訓練臉部識別器了。 我們將通過調用人臉識別器的train(faces-vector, labels-vector)方法來做到這一點。
#train our face recognizer of our training faces face_recognizer.train(faces, np.array(labels))Did you notice that instead of passing labels vector directly to face recognizer I am first converting it to numpy array? This is because OpenCV expects labels vector to be a numpy array.
您是否注意到 ,不是將labels矢量直接傳遞給面部識別器,而是先將其轉換為numpy數組? 這是因為OpenCV期望標簽vector是一個numpy數組。
Still not satisfied? Want to see some action? The next step is the real action, I promise!
還是不滿意? 想看到一些動作嗎? 我保證,下一步就是真正的行動!
預測 (Prediction)
Now comes my favorite part, the prediction part. This is where we actually get to see if our algorithm is actually recognizing our trained subjects’ faces or not. We will take two test images of our celebrities, detect faces from each of them, and then pass those faces to our trained face recognizer to see if it recognizes them.
現在是我最喜歡的部分,預測部分。 在這里,我們實際上可以看到算法是否真正識別出受過訓練的受試者的臉。 我們將為名人拍攝兩張測試圖像,從每位名人中檢測出面Kong,然后將這些面Kong傳遞給我們訓練有素的面部識別器,以查看其是否能夠識別出他們。
Below are some utility functions that we will use for drawing bounding box (rectangle) around face and putting celeberity name near the face bounding box.
以下是一些實用程序函數,我們將使用它們來在臉部周圍繪制邊界框(矩形)并將名人姓名放在臉部邊界框附近。
First function draw_rectangle draws a rectangle on image based on passed rectangle coordinates. It uses OpenCV's built in function cv2.rectangle(img, topLeftPoint, bottomRightPoint, rgbColor, lineWidth) to draw rectangle. We will use it to draw a rectangle around the face detected in test image.
第一個函數draw_rectangle基于傳遞的矩形坐標在圖像上繪制一個矩形。 它使用OpenCV內置函數cv2.rectangle(img, topLeftPoint, bottomRightPoint, rgbColor, lineWidth)繪制矩形。 我們將使用它在測試圖像中檢測到的面部周圍繪制一個矩形。
Second function draw_text uses OpenCV's built in function cv2.putText(img, text, startPoint, font, fontSize, rgbColor, lineWidth) to draw text on image.
第二個函數draw_text使用OpenCV內置函數cv2.putText(img, text, startPoint, font, fontSize, rgbColor, lineWidth)在圖像上繪制文本。
Now that we have the drawing functions, we just need to call the face recognizer’s predict(face) method to test our face recognizer on test images. Following function does the prediction for us.
現在我們有了繪圖功能,我們只需要調用面部識別器的predict(face)方法即可在測試圖像上測試面部識別器。 跟隨函數為我們做預測。
#this function recognizes the person in image passed#and draws a rectangle around detected face with name of the
#subject
def predict(test_img):
#make a copy of the image as we don't want to change original
img = test_img.copy()
#detect face from the image
face, rect = detect_face(img)
#predict the image using our face recognizer
label= face_recognizer.predict(face)
#get name of respective label returned by face recognizer
label_text = subjects[label]
#draw a rectangle around face detected
draw_rectangle(img, rect)
#draw name of predicted person
draw_text(img, label_text, rect[0], rect[1]-5)
return img
line-6 read the test image
第6行讀取測試圖像
line-7 detect face from test image
第7行從測試圖像中檢測人臉
line-11 recognize the face by calling face recognizer’s predict(face) method. This method will return a lable
第11行通過調用人臉識別器的predict(face)方法來識別人predict(face) 。 此方法將返回一個標簽
line-12 get the name associated with the label
第12行獲得與標簽關聯的名稱
line-16 draw rectangle around the detected face
第16行在檢測到的人臉周圍繪制矩形
line-18 draw name of predicted subject above face rectangle
第18行在臉部矩形上方繪制預測主題的名稱
Now that we have the prediction function well defined, next step is to actually call this function on our test images and display those test images to see if our face recognizer correctly recognized them. So let’s do it. This is what we have been waiting for.
現在我們已經很好地定義了預測功能,下一步是在測試圖像上實際調用此功能,并顯示這些測試圖像,以查看我們的面部識別器是否正確識別了它們。 因此,讓我們開始吧。 這就是我們一直在等待的。
print("Predicting images...")#load test images
test_img1 = cv2.imread("test-data/test1.jpg")
test_img2 = cv2.imread("test-data/test2.jpg")
#perform a prediction
predicted_img1 = predict(test_img1)
predicted_img2 = predict(test_img2)
print("Prediction complete")
#create a figure of 2 plots (one for each test image)
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
#display test image1 result
ax1.imshow(cv2.cvtColor(predicted_img1, cv2.COLOR_BGR2RGB))
#display test image2 result
ax2.imshow(cv2.cvtColor(predicted_img2, cv2.COLOR_BGR2RGB))
#display both images
cv2.imshow("Tom cruise test", predicted_img1)
cv2.imshow("Shahrukh Khan test", predicted_img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(1)
cv2.destroyAllWindows()
Output
輸出量
Predicting images...Prediction complete Face Recognition Results 人臉識別結果
woohoo! Isn't it beautiful? Indeed, it is!
嗚呼! 不漂亮嗎 的確是!
The full code can be found on this GitHub link.
完整的代碼可以在GitHub鏈接上找到 。
尾注 (End Notes)
Face Recognition is a fascinating idea to work on and OpenCV has made it extremely simple and easy for us to code it. It just takes a few lines of code to have a fully working face recognition application and we can switch between all three face recognizers with a single line of code change. It’s that simple.
人臉識別是一個有趣的想法,OpenCV使我們對其進行編碼變得極其簡單和容易。 一個完整的人臉識別應用程序只需要幾行代碼,我們只需更改一行代碼就可以在所有三個人臉識別器之間進行切換。 就這么簡單。
Although EigenFaces, FisherFaces, and LBPH face recognizers are good but there are even better ways to perform face recognition like using Histogram of Oriented Gradients (HOGs) and Neural Networks. So the more advanced face recognition algorithms are nowadays implemented using a combination of OpenCV and Machine learning. I have plans to write some articles on those more advanced methods as well, so stay tuned!
盡管EigenFaces,FisherFaces和LBPH面部識別器都不錯,但是還有更好的方法來進行面部識別,例如使用定向梯度直方圖(HOG)和神經網絡。 因此,如今結合使用OpenCV和機器學習來實現更高級的面部識別算法。 我也計劃寫一些關于這些更高級方法的文章,敬請期待!
Originally published at https://github.com/informramiz.
最初發布在 https://github.com/informramiz 。
翻譯自: https://medium.com/swlh/face-recognition-with-opencv-and-python-f51fb0389254
總結
以上是生活随笔為你收集整理的使用OpenCV和Python进行人脸识别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android摘抄
- 下一篇: python,人工智能,水果识别