项目实战—人脸检测之级联分类器实战
在上个教程中,使用 Adaboost 学习分类函数过程非常有效,但仍存在一个重大问题。在图像中,大部分图像为非面部区域。对图像的每个区域给予等同的注意力是没有意义的,因为我们应该主要关注最有可能包含人脸的区域。Viola 和 Jone 使用级联分类器在减少了计算时间的同时,实现了更高的检测率。
关键思想是在识别人脸区域时排除不含人脸的子窗口。由于任务是正确识别人脸,我们希望假阴率最小,即包含人脸却未被识别的子窗口最少。
每个子窗口都使用一系列分类器。这些分类器是简单的决策树:
· 如果第一个分类器检测为正样本,继续用第二个
· 如果第二个分类器检测是正样本,继续用第三个
· 以此类推
虽然有时可能包含人脸的图被认成负样本被子窗口漏检。但初级分类器以较低的计算成本筛除了大多数负样本,下图的分类器可额外消除更多的负样本,但需要更多的计算量。
使用 Adaboost 训练分类器,并调整阈值使错误率降到最低。在训练该模型时,变量如下:
· 每个阶段分类器数量
· 每个阶段的特征数量
· 每个阶段的阈值
方便的是,在 OpenCV 中,整个模型已经经过预训练,可直接用于人脸检测。
如果想了解有关 Boosting 技术的更多信息,欢迎查看作者关于 Adaboost 的文章:
https://maelfabien.github.io/machinelearning/adaboost
实战
下一步是找到预训练的权重。我们将使用默认的预训练模型来检测人脸、眼睛和嘴巴。
关于OpenCV中的已经训练好的XML文件,这个需要自行去官网下载,这边就不再赘述。
确定路径后,以此方式声明级联分类器:
cascPath = "haarcascade_frontalface_default.xml" eyePath = "haarcascade_eye.xml" smilePath = "haarcascade_smile.xml" faceCascade = cv2.CascadeClassifier(cascPath) eyeCascade = cv2.CascadeClassifier(eyePath) smileCascade = cv2.CascadeClassifier(smilePath)
检测图像中的人脸
在实现实时人脸检测算法之前,让我们先尝试在图像上简单检测一下。从加载测试图像开始:
gray = cv2.imread('min.jpg',0) plt.figure(figsize=(12,8)) plt.imshow(gray, cmap='gray') plt.show()
然后开始检测人脸,并将检测到的人脸框起来。
# Detect faces faces = faceCascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, flags=cv2.CASCADE_SCALE_IMAGE ) # For each face for (x, y, w, h) in faces: # Draw rectangle around the face cv2.rectangle(gray, (x, y), (x+w, y+h), (255, 255, 255), 3)
以下是 detectMultiScale 函数常见的参数列表:
· scaleFactor:确定每个图像缩放比例大小。
· minNeighbors:确定每个候选矩形应保留多少个相邻框。
· minSize:最小目标的大小。小于该值的目标将被忽略。
· maxSize:最大目标的大小。大于该值的目标将被忽略。
最后,显示结果:
plt.figure(figsize=(12,8)) plt.imshow(gray, cmap='gray') plt.show()
在测试图像上成功检测到人脸。现在开始实时检测!
实时人脸检测
下面继续进行实时人脸检测的 Python 实现。第一步是启动摄像头,并拍摄视频。然后,将图像转换为灰度图。这用于减小输入图像的维数。实际上,我们应用了一个简单的线性变换,而不是每个像素用三个点来描述红、绿、蓝。
这在 OpenCV 中是默认实现的。
video_capture = cv2.VideoCapture(0) while True: # Capture frame-by-frame ret, frame = video_capture.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
现在我们使用上述定义的 faceCascade 变量,它包含一个预训练算法,现在将其用于灰度图。
faces = faceCascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE )
对于检测到的每个人脸,都加上一个矩形框:
for (x, y, w, h) in faces: if w > 250 : cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 3) roi_gray = gray[y:y+h, x:x+w] roi_color = frame[y:y+h, x:x+w]
对于检测到的每张嘴,都加上一个矩形框:
smile = smileCascade.detectMultiScale( roi_gray, scaleFactor= 1.16, minNeighbors=35, minSize=(25, 25), flags=cv2.CASCADE_SCALE_IMAGE ) for (sx, sy, sw, sh) in smile: cv2.rectangle(roi_color, (sh, sy), (sx+sw, sy+sh), (255, 0, 0), 2) cv2.putText(frame,'Smile',(x + sx,y + sy), 1, 1, (0, 255, 0), 1)
对于检测到的每双眼睛,都加上一个矩形框:
eyes = eyeCascade.detectMultiScale(roi_gray) for (ex,ey,ew,eh) in eyes: cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) cv2.putText(frame,'Eye',(x + ex,y + ey), 1, 1, (0, 255, 0), 1)
然后计算人脸总数,显示整体图像:
cv2.putText(frame,'Number of Faces : ' + str(len(faces)),(40, 40), font, 1,(255,0,0),2) # Display the resulting frame cv2.imshow('Video', frame)
当按下 q 键时,执行退出选项。
if cv2.waitKey(1) & 0xFF == ord('q'): break
最后当所有操作完成后,关闭所有窗口。
video_capture.release() cv2.destroyAllWindows()
封装代码:
import cv2 import matplotlib.pyplot as plt cascPath = "haarcascade_frontalface_default.xml" eyePath = "haarcascade_eye.xml" smilePath = "haarcascade_smile.xml" faceCascade = cv2.CascadeClassifier(cascPath) eyeCascade = cv2.CascadeClassifier(eyePath) smileCascade = cv2.CascadeClassifier(smilePath) font = cv2.FONT_HERSHEY_SIMPLEX video_capture = cv2.VideoCapture(0) while True: # Capture frame-by-frame ret, frame = video_capture.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = faceCascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(200, 200), flags=cv2.CASCADE_SCALE_IMAGE ) # Draw a rectangle around the faces for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 3) roi_gray = gray[y:y + h, x:x + w] roi_color = frame[y:y + h, x:x + w] cv2.putText(frame, 'Face', (x, y), font, 2, (255, 0, 0), 5) smile = smileCascade.detectMultiScale( roi_gray, scaleFactor=1.16, minNeighbors=35, minSize=(25, 25), flags=cv2.CASCADE_SCALE_IMAGE ) for (sx, sy, sw, sh) in smile: cv2.rectangle(roi_color, (sh, sy), (sx + sw, sy + sh), (255, 0, 0), 2) cv2.putText(frame, 'Smile', (x + sx, y + sy), 1, 1, (0, 255, 0), 1) eyes = eyeCascade.detectMultiScale(roi_gray) for (ex, ey, ew, eh) in eyes: cv2.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2) cv2.putText(frame, 'Eye', (x + ex, y + ey), 1, 1, (0, 255, 0), 1) cv2.putText(frame, 'Number of Faces : ' + str(len(faces)), (40, 40), font, 1, (255, 0, 0), 2) # Display the resulting frame cv2.imshow('Video', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # When everything is done, release the capture video_capture.release() cv2.destroyAllWindows()
至于结果大家自行实验。用自己的摄像头就可以。