机器视觉——python实现人脸检测

from __future__ import print_function
from preprocess import FaceDetector
import argparse
import cv2

ap = argparse.ArgumentParser()
ap.add_argument("-f", "--face", required = True,
help = "path to where the face cascade resides")
ap.add_argument("-i", "--image", required = True,
help = "path to where the image file resides")
args = vars(ap.parse_args())
# load the image and convert it to grayscale
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

首先导入必要的包,为了保持代码的整洁,在同目录下新建了一个preprocess文件夹,然后新建一个init.py文件,使得python解释器将该文件夹解释为包。在该文件夹下还有个facedetector.py文件。不懂init.py的参考前面的文章。然后我们解析参数,读取图片并将图片转化为灰度图像。

机器视觉——python实现人脸检测

但是,让我们不要走得太远。在我们考虑在图像中寻找面部之前,我们首先需要定义一个类来处理我们如何在图像中找到面部。

新建一个facedetector.py文件(该文件就是上面导入的包)

import cv2 
class FaceDetector:
def __init__(self,faceCascadepath):
# load the face detector
self.faceCascade = cv2.CascadeClassifier(faceCascadepath)
def detect(self,image,scaleFactor = 1.1,minNeighbors=5,minSize=(30,30)):
# detect faces in the image
rects = self.faceCascade.detectMultiScale(image,
scaleFactor=scaleFactor,minNeighbors=minNeighbors,
minSize=minSize,flags=cv2.CASCADE_SCALE_IMAGE)
# return the rectangles representing bounding
# boxes around the faces
return rects

为了构建人脸识别软件,我们采用在OpenCV中内置的Haar级联分类器。这些分类器已经过预先训练以识别面孔!

构建我们自己的分类器当然不在本案例研究的范围之内。但如果我们想,我们需要很多“积极”和“消极”的图像。正图像将包含具有面部的图像,而负面图像将包含没有面部的图像。基于此数据集,我们可以提取特征来表征图像中的面部(或没有面部)并构建我们自己的分类器。这将是很多工作,而且非常耗时。

无论如何,这些分类器通过以不同的比例尺寸从左到右,从上到下扫描图像来工作。从左到右,从上到下扫描图像称为“滑动窗口(sliding window)”方法。

当窗口从左向右和从上到下移动时,一次一个像素,根据我们提供给分类器的参数,询问分类器是否“认为”在当前窗口中存在面部。

在类里面,我们定义了构造函数,该函数需要一个参数——我们级联分类器所在的路径。此分类器被序列化为XML文件。对cv2.CascadeClassifier的调用将对分类器进行反序列化,将其加载到内存中,并允许我们检测图像中的面部。

为了在图像中实际找到面部,我们接着定义了检测方法。该函数需要一个必需参数,即想要找到面部的图像,后跟三个可选参数。让我们来看看这些参数意味着什么:

scaleFactor:图像大小在每个图像尺度上减少了多少。这个值用于创建缩放金字塔,以便检测图像中多个缩放的面部(一些面可能更接近前景,因此更大;其他面可能更小并且在背景中,因此使用不同的缩放)。比如设置值为1.05,则表明我们在金字塔中的每个级别将图像的大小减小了5%。

minNeighbors:每个窗口应该有多少个neighbors才能将窗口中的区域视为一个脸。级联分类器将检测面部周围的多个窗口。此参数控制需要检测多少矩形(Neighbors)才能将窗口标记为面部。

minSize:宽度和高度(以像素为单位)的元组,表示窗口的最小尺寸。小于此大小的边界框将被忽略。从(30,30)开始并从那里进行微调是一个好主意。

通过调用在FaceDetector类的构造函数中创建的分类器的detectMultiScale方法,处理检测图像中的实际面部。我们使用我们默认的scaleFactor,minNeighbors和minSize,然后该方法为我们完成了整个人脸检测过程!

然后,detectMultiScale方法返回rects,这是一个包含图像中面部边界框的元组列表。这些边界框只是面部的(x,y)位置,以及框的宽度和高度。

接着,让我们继续detect_faces.py文件的编写,来实现我们自己的图像面部识别。

文件detect_faces.py

# find faces in the image
fd = FaceDetector(args["face"])
faceRects = fd.detect(gray,scaleFactor=1.1,minNeighbors=5,
minSize=(30,30))
print("I found {} face(s)".format(len(faceRects)))
# loop over the faces and draw a rectangle around each

for (x,y,w,h) in faceRects:
cv2.rectangle(image,(x,y),(x + w,y + h),(0,255,0),2)
# show the detected faces
cv2.imshow("Faces",image)
cv2.waitKey(0)

我们首先实例化我们的FaceDetector类,提供XML分类器的路径作为唯一参数。通过调用detect方法检测传入图像中的实际面部。然后打印我们在图像中一共找到了几个faces。

但是为了在图像周围实际绘制一个边界框,我们需要单独循环它们。同样,每个边界框只是一个有四个值的元组:在图像中,x和y为起始位置,然后是脸部的宽度和高度。

对cv2.rectangle的调用会在face上绘制一个绿色框。最后执行我们的脚本。

文件目录

机器视觉——python实现人脸检测

执行脚本文件

F:\20181116\Case Studies, 3nd Edition\face detect>python detect_faces.py --image "My Snapshot.jpg" --face cascades\haarcascade_frontalface_default.xml
I found 1 face(s)

显示结果:

机器视觉——python实现人脸检测

需要注意的是,图片最好不要有中文路径,不然报错

F:\20181116\Case Studies, 3nd Edition\face detect>python detect_faces.py -image GDP组合.jpg --face cascades\haarcascade_frontalface_default.xml l
usage: detect_faces.py [-h] -f FACE -i IMAGE
detect_faces.py: error: unrecognized arguments: GDP组合.jpg

也许这看上去好像很完美了,但是当我们多测试几张图片的时候,发现了如下的结果

机器视觉——python实现人脸检测

F:\20181116\Case Studies, 3nd Edition\face detect>python detect_faces.py --i spurs.jpg --face cascades\haarcascade_frontalface_default.xml
I found 3 face(s)

明明没有Tim的脸,为啥检测到了三个脸呢?

答案在于我们上面讨论过的cv2.detectMutliScale函数的参数。这些参数往往是敏感的,一组图像的某些参数选择不适用于另一组图像。

在大多数情况下,罪魁祸首将是scaleFactor参数。在其他情况下,它可能是minNeighbors。但作为调试规则,从scaleFactor开始,根据需要进行调整,然后转到minNeighbors。

考虑到这个调试规则,我们首先改变了对FaceDetector检测方法的调用:

faceRects = fd.detect(gray,scaleFactor=1.3,minNeighbors=5,
minSize=(30,30))

唯一的变化是scaleFactor参数,将其从1.1更改为1.3。

我们在看一次结果:

机器视觉——python实现人脸检测

很显然,这一次结果是正确的,只有两个面部(虽然石佛和妖刀退役了,跑车也远离了圣城),但无论如何我们的结果是正确的。

博客地址(包含源代码下载):https://0leo0.github.io/2018/case_study_01.html

关注不迷路哦!!


分享到:


相關文章: