第九节:人脸检测
今天要写的是大家都喜欢的人脸检测,我记得我当时自学opencv就是被这人脸检测所吸引的,然后又学习了不同的人脸检测算法,最后走向了深度学习,然后学习了更多的目标检测算法。等把opencv写完,我会写一些深度学习相关的算法。。话不多说,今天我们将用两种方法实现人脸检测。
(一)模板匹配(cv2.matchTemplate)
模板匹配是用来在一副大图中搜寻查找模版图像位置的方法,OpenCV 为我们提供了函数 cv2.matchTemplate和 2D 卷积一样,它也是用模板图像在输入图像(大图)上滑动,并在每一个位置对模板图像和与其对应的输入图像的子区域进行比较。 OpenCV 提供了几种不同的比较方法。返回的结果是一个灰度图像,每一个像素值表示了此区域与模板的匹配程度(这是我们的套话,因为我们不讲原理)
cv2.matchTemplate(img, template, method)
参数一是需要匹配的图像,参数二是匹配图像的模板,参数三是进行匹配的方法,有下面的6种
TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
TM_CCORR:计算相关性,计算出来的值越大,越相关
TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
cv2.matchTemplate这个函数的返回值有很多个,将这个函数的返回值传入cv2.minMaxLoc中,cv2.minMaxLoc会返回4个值min_val(匹配结果的最小值), max_val(匹配结果的最大值), min_loc(匹配结果的最小值对应的左上角坐标), max_loc(匹配结果的最大值对应的左上角坐标)。为什么会有最大值和最小值呢,因为不同的算法计算的结果不一样,有的算法是计算出来的值越小,越相关,有的算法是计算出来的值越小,越相关。所以有的方法是取 min_loc(匹配结果的最小值对应的左上角坐标),有的方法是取 max_loc(匹配结果的最大值对应的左上角坐标)。
cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED这两个算法取min_loc(匹配结果的最小值对应的左上角坐标)
cv2.TM_CCORR,cv2.TM_CCOEFF,cv2.TM_CCORR_NORMED,cv2.TM_CCOEFF_NORMED这四个取max_loc(匹配结果的最大值对应的左上角坐标)
还有我建议使用带归一化的算法,因为感觉更公平
上代码:
import cv2
img = cv2.imread('lena.jpg')
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
template = cv2.imread('template.jpg',0)
h, w = template.shape[:2]
res = cv2.matchTemplate(img1, template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
cv2.rectangle(img, max_loc, (max_loc[0]+w,max_loc[1]+h),(0,255,0), 3)
cv2.imshow("Result", img)
cv2.imshow("template",template)
cv2.waitKey(0)
小伙伴们就多试试吧,我就不把所有的算法跑一遍了
(二)多模板匹配(cv2.matchTemplate)
import cv2
import numpy as np
threshold = 0.8
img = cv2.imread('mario.jpg')
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg',0)
h, w = template.shape[:2]
res = cv2.matchTemplate(img1, template, cv2.TM_CCOEFF_NORMED)
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h),(0,255,0), 1)
cv2.imshow("Result", img)
cv2.imshow("mario_coin",template)
cv2.waitKey(0)
这里代码是直接copy官方的代码,不懂得小伙伴就百度一下吧
(三)cv2.CascadeClassifier检测人脸(cv2.CascadeClassifier)
这方法比上面的那个方法要高级点,CascadeClassifier,是Opencv中做人脸检测的时候的一个级联分类器。在使用这个检测时,你需要传入对应的模型文件。比如说,如果你需要检测人脸,你就传入人脸模型检测文件,检测人眼,就传入对应的人眼模型文件。
detectMultiScale有六个参数,返回值是目标对象的矩形框向量组
参数1:image待检测图像,通常为灰度图像。
参数2:scaleFactor表示在前后两次相继的扫描中,搜索窗口的缩放比例。
参数3:minNeighbors表示构成检测目标的相邻矩形的最小个数。默认情况下,该值为3 ,意味着有3个以上的检测标记存在时,才认为人脸存在。如果希望提高检测的准确率,可以将该值设置得更大,但同时可能会让- -些人脸无法被检测到。
参数4:flages该参数通常被省略。在使用低版本OpenCV ( OpenCV 1.X版本)时,它可能会被
设置为CV_ HAAR_ DO_ CANNY_ PRUNING ,表示使用Canny边缘检测器来拒绝一些区域。
参数5:minsize目标的最小尺寸,小于这个尺寸的目标将被忽略。
参数6:maxsize目标的最大尺寸,大于这个尺寸的目标将被忽略。
是不是很简单,话不多说,上代码:
import cv2
faceCascade= cv2.CascadeClassifier("haarcascade_frontalface_default.xml")#检测人脸的模型文件
img = cv2.imread('lena.png')
imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(imgGray,1.1,3)
#其中它的几个参数需要注意一下,第一个是传入的图像,第二个是scaleFactor(指定在每个图片缩放比例),第三个minNeighbors(指定需要检测的有效点数),第四个minSize(最小可能的对象大小),第五个maxSize(最大可能的对象大小)
#我在这里有些参数没有设置,小伙伴们可以选一张有多人脸的图片,试着改变其中的一些参数看看会有什么效果
for (x,y,w,h) in faces:
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),3)#这里还是原谅色
cv2.imshow("Result", img)
cv2.waitKey(0)
上面的detectMultiScale需注意一下它的参数,小伙伴们可以修改一些参数,多试试(当然,注意控制变量)
(四)cv2.CascadeClassifier检测人脸 视频流(cv2.CascadeClassifier)
图片都检测了,那肯定得来一个视频的呀,方法与上面相似,只是要增加一些程序。视频的检测与摄像头捕获的视频是一样的,所以这里我们选择用摄像头捕获视频
上代码:
import cv2
cap = cv2.VideoCapture(0) # 调用摄像头(一般电脑自带摄像头index为0)
faceCascade= cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
while cap.isOpened():
cat, frame = cap.read() # 读取每一帧图片
if not cat:
# 判断是否读取到图片
break
# 转换为灰度图片
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 5) # 中值滤波,这个中值滤波,小伙伴可以尝试将它去掉看看
img = cv2.GaussianBlur(gray, (3, 3), 0) # 高斯滤波降噪处理,是不是感觉有图像处理的地方,就有高斯滤波
th1 = cv2.adaptiveThreshold(gray, 255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY, 17, 6) # 自适应二值化 # 自适应二值化处理
# 人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
faceRects = faceCascade.detectMultiScale(th1, scaleFactor=1.1, minNeighbors=4)#这几个参数我调了好几次,个人感觉只能这样了
if len(faceRects) > 0:
# 历遍每次检测的所有脸
for face in faceRects:
x, y, w, h = face # face是一个元祖,返回了分类器的检测结果,包括起始点的坐标和高度宽度
cv2.rectangle(frame, (x - 5, y - 5), (x + w + 5, y + h + 5), (0,255,0), 3) # 绘制人脸检测的线框(还是原谅色)
font = cv2.FONT_HERSHEY_SIMPLEX # 创建摄像头前置的文字框
cv2.putText(frame, 'face', (x + 30, y + 30), font, 1, (255, 0, 255), 3)
cv2.imshow("face_detect", frame) # 显示人脸检测结果
c = cv2.waitKey(10)
if c & 0xFF == ord('q'):
# 按q退出
break
这里的照片是我手机里的(我以前拍的)。不过,通过使用这张照片,倒是让我发现,opencv的这个模型算法似乎对小人脸的捕捉比较准确
这里面的代码都有讲到过,对于小伙伴们来说,应该不是问题
(五)备注
补充一下,可能有小伙伴找不到识别人脸模型文件"haarcascade_frontalface_default.xml",以为每个人的环境都不一样,所以我也给不出具体的路径,不过,在你安装opencv库的时候,这些模型文件都会下载下来,所以你的电脑上不出意外的话已经安装了,你可以直接在电脑了检索这个文件。找不到也没有关系,我会贴上模型文件的链接
百度网盘链接
提取码:58ox
不过今天写完,可能要过一段时间才写了,因为有点事。(哎~,反正也没啥人看)
不知道若干年后,自己再看自己写的这些会有什么感想。(其实我并非计算机专业,学这些纯属爱好)
(六)结语
学习opencv有很多的方法,我的建议是你可以加一些群,可以充分利用B站,CSDN,和百度。
在我的博客中,我不会讲解opencv的算法实现(当然我也不太会),我只会讲解一些函数的调用,不理解就多改一些参数,多尝试尝试,慢慢你就理解来。相信你总有一天可以说opencv不过“Ctrl+C,Crtl+V”
如果有什么错误的地方,还请大家批评指正,最后,希望小伙伴们都能有所收获。