使⽤OpenCV和Python进⾏⼈脸识别
介绍
⼈脸识别是什么?或识别是什么?当你看到⼀个苹果时,你的⼤脑会⽴刻告诉你这是⼀个苹果。在这个过程中,你的⼤脑告诉你这是⼀个苹果⽔果,⽤简单的语⾔来说就是识别。那么什么是⼈脸识别呢?我肯定你猜对了。当你看着你的朋友⾛在街上或他的照⽚时,你会认出他是你的朋友保罗。有趣的是,当你看你的朋友或他的照⽚时,你⾸先要看他的脸,然后再看其他东西。你想过为什么要这么做吗?这是为了让你看他的脸就能认出他来。好吧,这是你的⾯部识别。
但真正的问题是⼈脸识别是如何⼯作的?它⾮常简单和直观。举⼀个现实⽣活中的例⼦,当你在⽣活中第⼀次遇见⼀个⼈,你不认识他,对吧?当他和你说话或握⼿时,你看着他的脸、眼睛、⿐⼦、嘴巴、颜⾊和整体的表情。这是你通过收集⾯部数据来学习或训练那个⼈的⾯部识别。然后他告诉你他叫保罗。此时,你的⼤脑知道它刚刚学到的⾯部数据属于保罗。现在你的头脑已经训练好了,准备在保罗的脸上做⾯部识别。下次当你在照⽚中看到保罗或他的脸时,你会⽴刻认出他。这就是⼈脸识别的⼯作原理。你遇到保罗的次数越多,你的⼤脑就会收集到更多关于他的信息,尤其是他的脸,你就越能识别他。
下⼀个问题是如何⽤OpenCV编码⼈脸识别,毕竟这是你阅读这篇⽂章的唯⼀原因,对吧?那么,好吧。你可能会说我们的⼤脑可以很容易地做这些事情,但是把它们编码到电脑⾥是很困难的吗?别担⼼,不是
的。多亏了OpenCV,编码⼈脸识别变得越来越容易。⼈脸识别的编码步骤与我们在上⾯的实际⽰例中讨论的⼀样。
·训练数据收集:收集您想要识别的⼈的⾯部数据(本例中为⾯部图像)
·识别器的训练:将⼈脸数据(以及每个⼈脸的相应名称)输⼊⼈脸识别器,使其能够学习。
·识别:输⼊这些⼈的新⾯孔,看看你刚训练过的⼈脸识别器是否识别他们
OpenCV带有内置的⼈脸识别器,你所要做的就是给它输⼊⼈脸数据。这很简单,⼀旦我们完成了编码,它就会看起来很简单。
OpenCV⾯部识别器
OpenCV有三个内置的⼈脸识别器,多亏了OpenCV⼲净的编码,你可以通过改变⼀⾏代码来使⽤它们中的任何⼀个。下⾯是这些⼈脸识别器的名称和它们的OpenCV调⽤
1、EigenFaces⼈脸识别器识别器 - ateEigenFaceRecognizer()
2、FisherFaces⼈脸识别器识别器 - ateFisherFaceRecognizer()
3、局部⼆值模式直⽅图(LBPH)⼈脸识别器 - ateLBPHFaceRecognizer()
现在我们有三个⼈脸识别器,但是你知道该⽤哪⼀个吗?什么时候⽤吗?或者哪个更好?我猜你不知道。那么,接下来我们将深⼊研究每⼀个识别器。
EigenFaces⾯部识别器
这个算法考虑的事实是,并不是脸的所有部分都同样重要,或同样有⽤。当你看⼀个⼈的时候,你会通过他独特的特征认出他/她,⽐如眼睛、⿐⼦、脸颊、前额以及他们之间的差异。所以你实际上关注的是最⼤变化的区域(数学上说,这个变化是⽅差)。例如,从眼睛到⿐⼦有⼀个显著的变化,从⿐⼦到嘴也是如此。当你看多张脸的时候你可以通过看脸的这些部分来⽐较它们因为这些部分是脸最有⽤和最重要的组成部分。
重要的是,它们捕捉到⼈脸之间的最⼤变化,这种变化可以帮助你区分不同的⼈脸,这就是特征⼈脸识别系统的⼯作原理。
EignFaces⼈脸识别器将所有⼈的训练图像作为⼀个整体,并试图提取重要和有⽤的成分(捕捉最⼤⽅差/变化的成分),并丢弃其余的成分。这样,它不仅从训练数据中提取重要的组件,⽽且通过丢弃不重要的组件来节省内存。它提取的这些重要成分被称为主成分。
我所⽤主成分,⽅差,⾼变化区域,可互换的有⽤特征等术语,它们的性质基本上是⼀样的东西。
以下是显⽰从⾯部列表中提取的主要组件的图像。
主成分
你可以看到,主分量实际上表⽰⾯的,这些⾯被称为特征⾯,也就是算法的名字。
这就是特征⾯识别器⾃⾝的训练⽅式(通过提取主成分),它还记录了哪个主成分属于哪个⼈。在上⾯的图像中需要注意的⼀点是特征⾯算法也将光照作为⼀个重要的组成部分。
在随后的识别过程中,当你向算法输⼊新图像时,它也会在该图像上重复同样的过程。它从新映像中提取主组件,并将该组件与它在训练期间存储的组件列表进⾏⽐较,并到匹配最好的组件,并返回与该最佳匹配组件关联的person标签。
轻松+容易,对吧?下⼀个⽐这个更容易。
FisherFaces⼈脸识别器
该算法是改进后的FisherFaces⼈脸识别算法。FisherFaces⼈脸识别器同时查看所有⼈的训练⾯,并从所有⼈的训练⾯中到主要的组成部分。通过从所有的⼈脸中捕获主要的组成部分,你并没有把注意⼒集中在区分⼀个⼈和另⼀个⼈的特征上,⽽是集中在代表整个训练数据中所有⼈的所有⾯孔的特征上。
这种⽅法有⼀个缺点。例如,考虑下⾯的⾯光照变化。
你知道特征⾯⼈脸识别器也认为照明是⼀个重要的组成部分,对吧?想象⼀个场景,⼀个⼈所有的脸都有⾮常⾼的亮度变化(⾮常暗或者⾮常亮等等)。特征⼈脸识别者将会考虑这些光照变化⾮常有⽤的特征,并且可能会忽略其他⼈的⾯部特征,认为这些特征不太有⽤。现在所提取的特征特征⾯只代表⼀个⼈的⾯部特征,⽽不是所有⼈的⾯部特征。
如何解决这个问题?我们可以通过调整EigenFaces⼈脸识别器来解决这个问题,以便从每个⼈的脸部分别提取有⽤的特征,⽽不是提取所有脸部组合的有⽤特征。这样,即使⼀个⼈的光照变化很⼤,也不会影响其他⼈物特征提取过程。这正是FisherFaces⼈脸识别器算法的功能。
Fisherfaces算法不是提取表⽰所有⼈员所有⾯部的有⽤特征,⽽是提取可区分⼀个⼈和另⼀个⼈的有
⽤特征。通过这种⽅式,⼀个⼈的特征不会占据主导地位(被认为是更有⽤的特征)⽽其他⼈则具有区分⼀个⼈和另⼀个⼈的特征。
下⾯是使⽤Fisherfaces算法提取的特征的图像。
Fisher Faces
numpy官方教程 你可以看到提取的特征实际上代表了⾯孔,这些⾯被称为Fisher faces,因此算法的名称。
这⾥需要注意的⼀点是,Fisherfaces⼈脸识别器只会阻⽌⼀个⼈的特征凌驾于另⼀个⼈的特征之上,但它仍然认为光照变化是有⽤的特征。我们知道光照变化不是⼀个有⽤的特征来提取,因为它不是真正的脸的⼀部分。那么,该怎么摆脱这个照明问题?这就是我们的下⼀个⼈脸识别器锁解决的问题。
局部⼆值模式直⽅图(LBPH)⼈脸识别器
我们知道Eigenfaces和Fisherfaces都受光线影响,在现实⽣活中,我们⽆法保证完美的光照条件。 LBPH⼈脸识别器是克服这个缺点的⼀种改进。
这种想法是不看整个图像,⽽是查图像的局部特征。 LBPH算法试图出图像的局部结构,并通过⽐较每个像素与其相邻像素来实现。
取⼀个3x3的窗⼝,每移动⼀个图像(图像的每个局部),将中⼼的像素与相邻像素进⾏⽐较。强度值⼩于或等于中⼼像素的邻域⽤1表⽰,其它邻域⽤0表⽰。然后你以顺时针的顺序读取3x3窗⼝下的0/1值,你会得到⼀个像11100011这样的⼆进制模式,这个模式在图像的特定区域是局部的。在整个图像上这样做,就会得到⼀个局部⼆进制模式的列表。
LBP标签
现在你明⽩为什么这个算法的名字中有局部⼆进制模式?因为你得到⼀个局部⼆进制模式列表。现在你可能想知道,LBPH的直⽅图部分呢?在获得局部⼆进制模式列表后,您可以使⽤⼆进制到⼗进制转换将每个⼆进制模式转换为⼗进制数(如上图所⽰),然后对所有这些⼗进制值进⾏直⽅图制作。样本直⽅图是像下⾯这样的。
样本直⽅图
我猜这回答了直⽅图部分的问题。所以最终你会得到训练数据集中每个⼈脸图像的⼀个直⽅图,这意味着如果训练数据集中有100个图像,那么LBPH会在训练后提取100个直⽅图,并储存起来以便以后识别。记住,算法也会跟踪哪个直⽅图属于哪个⼈。
在识别后期,当您将新图像送⼊识别器进⾏识别时,它将⽣成新图像的直⽅图,将该直⽅图与其已有的直⽅图进⾏⽐较,到最佳匹配直⽅图并返回与该最佳匹配关联的⼈员标签匹配直⽅图。
下⾯是⼀张脸和它们各⾃的局部⼆进制模式图像的列表。您可以看到,LBP图像不受光照条件变化的影响。
局部⼈脸
理论部分已经结束,现在是编码部分!准备好开始编写代码了吗?那我们开始吧。
使⽤OpenCV编码⼈脸识别
本教程中的⼈脸识别过程分为三个步骤。
1、准备训练数据:在这⼀步中,我们将读取每个⼈/主体的训练图像及其标签,从每个图像中检测⼈脸并为每个检测到的⼈脸分配其所属⼈员的整数标签。
2、训练⼈脸识别器:在这⼀步中,我们将训练OpenCV的LBPH⼈脸识别器,为其提供我们在步骤1中准备的数据。
3、测试:在这⼀步中,我们会将⼀些测试图像传递给⼈脸识别器,并查看它是否能够正确预测它们
编程⼯具:
1. .
2. .
3.
注:Numpy使Python中的计算变得容易。除此之外,它还包含⼀个强⼤的N维数组实现,我们将使⽤它来将数据作为OpenCV函数的输⼊。
导⼊必需的模块
在开始实际编码之前,我们需要导⼊所需的编码模块。所以让我们先导⼊它们。
cv2:是Python的OpenCV模块,我们将⽤它来进⾏⼈脸检测和⼈脸识别。
os:我们将使⽤这个Python模块来读取我们的培训⽬录和⽂件名。
numpy:我们将使⽤此模块将Python列表转换为numpy数组,因为OpenCV⼈脸识别器接受numpy数组。
#导⼊OpenCV模块
import cv2
#导⼊os模块⽤于读取训练数据⽬录和路径
import os
# 导⼊numpy将python列表转换为numpy数组,OpenCV⾯部识别器需要它
import numpy as np
训练数据
训练中使⽤的图像越多越好。通常很多图像⽤于训练⾯部识别器,以便它可以学习同⼀个⼈的不同外观,例如戴眼镜,不戴眼镜,笑,伤⼼,快乐,哭泣,留着胡⼦,没有胡⼦等。简单的教程我们将只为每个⼈使⽤12张图⽚。
所以我们的训练数据由共2⼈组成,每个⼈有12张图像。所有培训数据都在培训数据⽂件夹内。训练数据⽂件夹包含每个⼈的⼀个⽂件
夹,并且每个⽂件夹以格式sLabel(例如s1,s2)命名,其中标签实际上是分配给该⼈的整数标签。例如,名为s1的⽂件夹意味着该⽂件夹包含⼈员1的图像。培训数据的⽬录结构树如下所⽰:
training-data
|-------------- s1
| |-- 1.jpg
| |-- ...
| |-- 12.jpg
|-------------- s2
| |-- 1.jpg
| |-- ...
| |-- 12.jpg
测试数据⽂件夹包含我们将⽤于在成功培训完成后测试⼈脸识别器的图像
由于OpenCV⼈脸识别器接受标签为整数,因此我们需要定义整数标签和⼈物实际名称之间的映射,所以下⾯我定义了⼈员整数标签及其各⾃名称的映射。
注意:由于我们尚未将标签0分配给任何⼈,因此标签0的映射为空。
#我们的训练数据中没有标签0,因此索引/标签0的主题名称为空
subjects = ["", "Ramiz Raja", "Elvis Presley"]
准备训练数据
您可能想知道为什么要进⾏数据准备,对吗?那么,OpenCV⼈脸识别器接受特定格式的数据。它接受两个⽮量,⼀个⽮量是所有⼈的脸部,第⼆个⽮量是每个脸部的整数标签,因此在处理脸部时,脸部识别器会知道该脸部属于哪个⼈。
例如,如果我们有两个⼈和两个图像为每个⼈。
PERSON-1 PERSON-2
img1 img1
img2 img2
然后,准备数据步骤将⽣成以下⾯和标签向量。
FACES LABELS
person1_img1_face 1
person1_img2_face 1
person2_img1_face 2
person2_img2_face 2
准备数据步骤可以进⼀步分为以下⼦步骤。
1、阅读培训数据⽂件夹中提供的所有主题/⼈员的⽂件夹名称。例如,在本教程中,我们有⽂件夹名称:s1,s2。
2、对于每个主题,提取标签号码。你还记得我们的⽂件夹有⼀个特殊的命名约定吗?⽂件夹名称遵循格式sLabel,其中Label是⼀个整数,代表我们已分配给该主题的标签。因此,例如,⽂件夹名称s1表⽰主题具有标签1,s2表⽰主题标签为2等。将在此步骤中提取的标签分配给在下⼀步中检测到的每个⾯部。
3、阅读主题的所有图像,从每张图像中检测脸部。
4、将添加到标签⽮量中的具有相应主题标签(在上述步骤中提取)的每个脸部添加到脸部⽮量。
#使⽤OpenCV⽤来检测脸部的函数
def detect_face(img):
#将测试图像转换为灰度图像,因为opencv⼈脸检测器需要灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#加载OpenCV⼈脸检测器,我正在使⽤的是快速的LBP
#还有⼀个更准确但缓慢的Haar分类器
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论