学习opencv也有一段时间了,opencv里的知识要深究的话,可以说是无穷无尽,里面的要用到的数学知识很丰富,只可惜自己的数学达不到那种程度,所以只能通过相应的实践来弥补了。最近做了一个简单的人脸检测来结束目前opencv的基础学习,之后的路依然要脚踏实地地走下去。
文章目录
- 1.原理
- 2.实际操作
- 2.1.人脸检测
- 2.2.人脸和眼睛的检测
- 完整代码
1.原理
做人脸检测,首先要从图像中提取出特征,而本文则利用Haar特征来进行人脸检测。
Haar特征包括三类特征:边缘特征、线性特征、中心特征和对角线特征,组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和。Haar特征值反映了图像的灰度变化情况,例如脸部的一些特征能由矩形特征简单的描述,比如眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等,但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。
特征模板如下:
Haar特征具有结构简单、计算方便、提取速度快的优势,并且在尺度变化上具有鲁棒性。
2.实际操作
我使用的是opencv4.2,在解压出来的目录里有个叫data的文件夹,里面有haarcascades该文件夹,我们将利用该文件夹里的XML文件来进行人脸检测,同时在data文件夹下还有其他检测方法,可以去探索。
haarcascades文件夹下的XML文件可用于检测静止图像、视频和摄像头所得到的图像中的人脸。
2.1.人脸检测
做人脸检测首先需要将图像转换为灰度图像,接着将获取Haar特征级联检测器文件,然后使用人脸检测函数进行检测,最后在图上使用矩形框标注出人脸。
代码为:
def face_detect_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 级联检测器获取文件,基于haar特征
cascade_face_name = "D:/opencv3/Opencv4.2.0/opencv/data/haarcascades/haarcascade_frontalface_alt.xml"
face_detector = cv.CascadeClassifier(cascade_face_name)
# 人脸检测函数,在多个尺度空间上进行人脸检测
face = face_detector.detectMultiScale(gray, 1.1, 3)
# 参数一:灰度图像
# 参数二:尺度变换,就是向上或者向下每次是原来的多少倍,这里是1.1倍
# 参数三:人脸检测次数,设置越高,误检率越低,但是对于迷糊图片,我们设置越高,越不易检测出来,要适当降低(默认为3)
# 参数四:flags,要么使用默认值0,要么使用CV_HAAR_DO_CANNY_PRUNING如果设置为
# CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域,
# 因为这些区域通常不会是人脸所在区域
# 参数五:限制得到的目标区域的范围
for x, y, w, h in face:
cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2) # 检测人脸
cv.imshow("face_detect_demo", image)
原图为:
经过人脸检测后:
不仅可以检测正脸,还可以检测稍微侧一下的脸:
2.2.人脸和眼睛的检测
在做包括眼睛的检测时,我们会用到haarcascade_eye.xml。我们在原图上标注出人脸和眼睛,在此期间会用到ROI,代码如下:
ef face_detect_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 级联检测器获取文件,基于haar特征
cascade_face_name = "D:/opencv3/Opencv4.2.0/opencv/data/haarcascades/haarcascade_frontalface_alt.xml"
cascade_eye_name = "D:/opencv3/Opencv4.2.0/opencv/data/haarcascades/haarcascade_eye.xml"
face_detector = cv.CascadeClassifier(cascade_face_name)
eye_detector = cv.CascadeClassifier(cascade_eye_name)
# 人脸检测函数,在多个尺度空间上进行人脸检测
face = face_detector.detectMultiScale(gray, 1.1, 3)
# 参数一:灰度图像
# 参数二:尺度变换,就是向上或者向下每次是原来的多少倍,这里是1.1倍
# 参数三:人脸检测次数,设置越高,误检率越低,但是对于迷糊图片,我们设置越高,越不易检测出来,要适当降低(默认为3)
# 参数四:flags,要么使用默认值0,要么使用CV_HAAR_DO_CANNY_PRUNING如果设置为
# CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域,
# 因为这些区域通常不会是人脸所在区域
# 参数五:限制得到的目标区域的范围
for x, y, w, h in face:
cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2) # 检测人脸
roi_face = gray[y: y + h, x: x + w]
image_face = image[y: y + h, x: x + w]
eye = eye_detector.detectMultiScale(roi_face, 1.1, 3, 0, (55, 55))
for ex, ey, ew, eh in eye:
cv.rectangle(image_face, (ex, ey), (ex + ew, ey + eh), (255, 0, 0), 2) # 检测眼睛并画框
cv.imshow("face_detect_demo", image)
得到的结果为:
除此之外,该代码也可以用于动态场景下的人脸检测,可以打开电脑的摄像头。
完整代码
import cv2 as cv # 导入opencv模块
import numpy as np # 导入数学函数库
def face_detect_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 级联检测器获取文件,基于haar特征
cascade_face_name = "D:/opencv3/Opencv4.2.0/opencv/data/haarcascades/haarcascade_frontalface_alt.xml"
cascade_eye_name = "D:/opencv3/Opencv4.2.0/opencv/data/haarcascades/haarcascade_eye.xml"
face_detector = cv.CascadeClassifier(cascade_face_name)
eye_detector = cv.CascadeClassifier(cascade_eye_name)
# 人脸检测函数,在多个尺度空间上进行人脸检测
face = face_detector.detectMultiScale(gray, 1.1, 3)
# 参数一:灰度图像
# 参数二:尺度变换,就是向上或者向下每次是原来的多少倍,这里是1.1倍
# 参数三:人脸检测次数,设置越高,误检率越低,但是对于迷糊图片,我们设置越高,越不易检测出来,要适当降低(默认为3)
# 参数四:flags,要么使用默认值0,要么使用CV_HAAR_DO_CANNY_PRUNING如果设置为
# CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域,
# 因为这些区域通常不会是人脸所在区域
# 参数五:限制得到的目标区域的范围
for x, y, w, h in face:
cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2) # 检测人脸
roi_face = gray[y: y + h, x: x + w]
image_face = image[y: y + h, x: x + w]
eye = eye_detector.detectMultiScale(roi_face, 1.1, 3, 0, (55, 55))
for ex, ey, ew, eh in eye:
cv.rectangle(image_face, (ex, ey), (ex + ew, ey + eh), (255, 0, 0), 2) # 检测眼睛并画框
cv.imshow("face_detect_demo", image)
print("------------hello python!------------")
src = cv.imread("D:/opencv3/image/ym.jpg")
capture = cv.VideoCapture(0)
cv.namedWindow("input_image", cv.WINDOW_AUTOSIZE)
cv.namedWindow("face_detect_demo", cv.WINDOW_AUTOSIZE)
cv.imshow("input_image", src)
# while True:
# # 第一个参数ret 为True 或者False,代表有没有读取到图片
# # 第二个参数frame表示截取到一帧的图片
# ret, frame = capture.read()
# frame = cv.flip(frame, 1) # 镜像翻转图像
# face_detect_demo(frame)
# c = cv.waitKey(10)
# if c == 27: # ESC
# break
face_detect_demo(src)
cv.waitKey(0)
cv.destroyAllWindows() # 释放所有窗口