“ 本案例使用python+ OpenCV 完成,OpenCV是一个神奇的「开源计算机视觉库」,具备很强的计算效率,且专门用于实时应用,因此,它非常适合使用摄像头的实时人脸识别。
作者丨徐恩伟![134327d82cdf7fc70e7301e7a5172e9f.png](https://i-blog.csdnimg.cn/blog_migrate/f480abc82883e69ec005708e3f411a30.jpeg)
01
—
【案例介绍】
本案例使用python+ OpenCV 完成,OpenCV是一个神奇的「开源计算机视觉库」,具备很强的计算效率,且专门用于实时应用,因此,它非常适合使用摄像头的实时人脸识别。在本案例中,你将学会:
使用基于Haar特征的Cascade级联分类器进行人脸部识别
用人脸识别同样的道理,扩展到人眼识别、微笑识别上
用opencv自带的Harr级联分类器进行人脸、人眼与微笑识别(附源代码,直接复制粘贴即可运行)
02
—
【基本原理】
(一)案例所需环境
(1)python 3 (python主程序)
(2)OpenCV库(开源的计算机视觉库)
(二)基本原理:OpenCV中的Haar-cascade检测
(1)何为Haar
Haar是哈尔特征(Haar-like features)的简称,是一种用于物体识别的一种数字图像特征。它们因为与哈尔小波转换极为相似而得名,是第一种即时的人脸检测运算。历史上,直接使用图像的强度(就是图像每一个像素点的RGB值)使得特征的计算强度很大。帕帕乔治奥等人提出可以使用基于哈尔小波的特征而不是图像强度,维奥拉和琼斯进而提出了哈尔特征。哈尔特征使用检测窗口中指定位置的相邻矩形,计算每一个矩形的像素和并取其差值,然后用这些差值来对图像的子区域进行分类。
Haar特征包含三种:边缘特征、线性特征、中心特征和对角线特征。每种分类器都从图片中提取出对应的特征。
比如上图中,横的黑道将人脸中较暗的双眼提取了出来,而竖的白道将人脸中较亮的鼻梁提取了出来。
(2)何为Cascade
如果能找到一个简单的方法能够检测某个窗口是不是人脸区域,如果该窗口不是人脸区域,那么就只看一眼便直接跳过,也就不用进行后续处理了,这样就能集中精力判别那些可能是人脸的区域。为此,有人引入了Cascade 分类器。它不是将6000个特征都用在一个窗口,而是将特征分为不同的阶段,然后一个阶段一个阶段的应用这些特征(通常情况下,前几个阶段只有很少量的特征)。如果窗口在第一个阶段就检测失败了,那么就直接舍弃它,无需考虑剩下的特征。如果检测通过,则考虑第二阶段的特征并继续处理。如果所有阶段的都通过了,那么这个窗口就是人脸区域。作者的检测器将6000+的特征分为了38个阶段,前五个阶段分别有1,10,25,25,50个特征(前文图中提到的识别眼睛和鼻梁的两个特征实际上是Adaboost中得到的最好的两个特征)。根据作者所述,平均每个子窗口只需要使用6000+个特征中的10个左右。
基于Haar特征的cascade级联分类器是Paul Viola和 Michael Jone在2001年的论文”Rapid Object Detection using a Boosted Cascade of Simple Features”中提出的一种有效的物体检测方法。这种机器学习方法基于大量正面、负面图像训练级联函数,然后用于检测其他图像中的对象。这里,我们将用它进行人脸识别。
详情参见:Cascade Classifier Training
在OpenCV 中包含很多预训练分类器,即提供了可以直接用于检测人脸、眼睛、笑容等的相关算法,以供开发者调用,相关的 XML 文件可从该目录下载:haarcascades
(3)Cascade级联分类器的训练方法:Adaboost
级联分类器的函数是通过大量带人脸
和不带人脸
的图片通过机器学习得到的。对于人脸识别来说,需要几万个特征,通过机器学习找出人脸分类效果最好、错误率最小的特征。训练开始时,所有训练集中的图片具有相同的权重,对于被分类错误的图片,提升权重,重新计算出新的错误率和新的权重。直到错误率或迭代次数达到要求。这种方法叫做Adaboost
。
在Opencv中可以直接调用级联分类器函数。
(4)Haar-cascade级联检测的基本原理:CascadeClassifier
在opencv中通过CascadeClassifier检测模块(算法模块)能够实现基于Haar特征的人脸或人眼等其他部位Cascade级联分类器,这些算法被封装成xml文件进行存放,其中包含相应的检测特征值,特征size大小根据训练时的参数而定,检测的时候可以简单理解为就是将每个固定size特征(检测窗口)与输入图像的同样大小区域比较,如果匹配那么就记录这个矩形区域的位置,然后滑动窗口,检测图像的另一个区域,重复操作。由于输入的图像中特征大小不定,比如在输入图像中眼睛是50x50的区域,而训练时的是25x25,那么只有当输入图像缩小到一半的时候,才能匹配上,所以这里还有一个逐步缩小图像,也就是制作图像金字塔的流程.
由于人脸可能出现在图像的任何位置,在检测时用固定大小的窗口对图像从上到下、从左到右扫描,判断窗口里的子图像是否为人脸,这称为滑动窗口技术(sliding window)。为了检测不同大小的人脸,还需要对图像进行放大或者缩小构造图像金字塔,对每张缩放后的图像都用上面的方法进行扫描。
以512x512大小的图像为例,假设分类器窗口为24x24,滑动窗口的步长为1,则总共需要扫描的窗口数为:
即要检测一张图片需要扫描大于120万个窗口,这里我们不得不借助计算机编程的程序来实现。
CascadeClassifier.detectMultiScale的调用方法为如下,只需要我们掌握一些调用的参数意义即可。
class CascadeClassifier: detectMultiScale( const Mat& image, vector<Rect>& objects,double scaleFactor, int minNeighbors,int flags, Size minObjectSize, Size maxObjectSize) { vector<int> fakeLevels; vector<double> fakeWeights; detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor, minNeighbors, flags, minObjectSize, maxObjectSize, false ); }
参数意思:\1. const Mat& image:输入图像\2. vector& objects:输出的矩形向量组\3. double scaleFactor=1.1:这个是每次缩小图像的比例,默认是1.1\4. minNeighbors=3:匹配成功所需要的周围矩形框的数目,每一个特征匹配到的区域都是一个矩形框,只有多个矩形框同时存在的时候,才认为是匹配成功,比如人脸,这个默认值是3。\5. flags=0:可以取如下这些值: CASCADE_DO_CANNY_PRUNING=1, 利用canny边缘检测来排除一些边缘很少或者很多的图像区域 CASCADE_SCALE_IMAGE=2, 正常比例检测 CASCADE_FIND_BIGGEST_OBJECT=4, 只检测最大的物体 CASCADE_DO_ROUGH_SEARCH=8 初略的检测\6. minObjectSize maxObjectSize:匹配物体的大小范围
如果对上述的原理看不懂也没有关系,因为基于上述原理实现的操作代码别人已经写好了,我们在python中直接调用即可而不必过于关注其实现过程,所以可以直接进行下述的编程操作,在编写代码的过程中进行理解人脸识别的逻辑关系。
03
—
【案例实践】
以下所有步骤我均在Windows 电脑上进行了测试、运行很好。
一、安装扩展库
本文采用pip进行安装,因此需要成功配置好python的安装环境。
(1)opencv-python 的安装,输入:pip install opencv-python。
注:安装openCV,不是pip install OpenCv,而是 pip install OpenCv-python
numpy与OpenCV绑定安装,无需自己输入命令。
(2) pillow的安装,输入:pip install pillow
注:pillow为图像处理包。
(3) contrib的安装,输入:pip instal opencv-contrib-python
注:contrib是用于训练自己的人脸模型的一个OpenCV扩展包
下图为三个包的安装过程截图,由于我之前已经安装过,会显示包已存在。首次安装会有进度条,成功后会有Successfully字样。
二、人脸部检测
人脸识别的最基础任务是人脸检测,你必须首先判断是否有人脸才能在未来与捕捉到的新人脸对比时进而识别它。
下面,我们就开始用 OpenCV 创建人脸检测器吧!
"""功能:检测人脸部位的极简代码版本:V1.0"""import cv2def main(): # 读取照片 img = cv2.imread("img/4.jpg", 1) # 人脸识别分类器 faceCascade = cv2.CascadeClassifier("enginer\haarcascade_frontalface_default.xml") # 返回的faces是一个包含脸的坐标的列表,即脸在整个检测图像的x,y坐标位置,w、h脸部区域的长、宽 faces = faceCascade.detectMultiScale(img,1.1,4,minSize=(32,32)) #对图片中所有的人脸进行标记 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # 将照片显示出来 cv2.imshow("Video", img) cv2.waitKey() cv2.destroyAllWindows()if __name__ == '__main__': main()
三、人眼睛识别
对于人的眼睛的识别,可以先对其进行人脸部识别,然后在识别脸的部分区域进行眼睛的识别。
"""功能:检测人眼睛的极简代码版本:V1.0"""import cv2def main(): # 读取照片 img = cv2.imread("img/4.jpg", 1) # 人脸识别分类器 faceCascade = cv2.CascadeClassifier("enginer\haarcascade_frontalface_default.xml") # 眼睛识别分类 eyeCascade = cv2.CascadeClassifier("enginer\haarcascade_eye.xml") # 返回的faces是一个包含脸的坐标的列表,即脸在整个检测图像的x,y坐标位置,w、h脸部区域的长、宽 faces = faceCascade.detectMultiScale(img,1.1,4,minSize=(32,32)) #对图片中所有的人脸进行标记 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # 选出人脸区域,在人脸区域而不是全图中进行人眼检测,节省计算资源 face_area = img[y:y + h, x:x + w] eyes=eyeCascade.detectMultiScale(face_area,1.1,4) for (ex,ey,ew,eh) in eyes: cv2.rectangle(face_area,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) # 将照片显示出来 cv2.imshow("Video", img) cv2.waitKey() cv2.destroyAllWindows()if __name__ == '__main__': main()
四、人的微笑识别
对于人微笑的识别,亦可以先对其进行人脸部识别,然后在识别脸的部分区域进行微笑的识别。
"""功能:检测人脸微笑的极简代码版本:V1.0"""import cv2def main(): # 读取照片 img = cv2.imread("img/4.jpg", 1) # 人脸识别分类器 faceCascade = cv2.CascadeClassifier("enginer\haarcascade_frontalface_default.xml") # 眼睛识别分类 eyeCascade = cv2.CascadeClassifier("enginer\haarcascade_eye.xml") # 微笑识别分类 smileCascade = cv2.CascadeClassifier("enginer\haarcascade_smile.xml") # 返回的faces是一个包含脸的坐标的列表,即脸在整个检测图像的x,y坐标位置,w、h脸部区域的长、宽 faces = faceCascade.detectMultiScale(img,1.1,4,minSize=(32,32)) #对图片中所有的人脸进行标记 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # 选出人脸区域,在人脸区域而不是全图中进行人眼检测,节省计算资源 face_area = img[y:y + h, x:x + w] eyes=eyeCascade.detectMultiScale(face_area,1.1,4) for (ex,ey,ew,eh) in eyes: cv2.rectangle(face_area,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) # 选出人脸区域,在人脸区域而不是全图中进行微笑检测,节省计算资源 smiles=smileCascade.detectMultiScale(face_area,1.1,4) for (sx,sy,sw,sh) in smiles: cv2.rectangle(face_area,(sx,sy),(sx+sw,sy+sh),(0,0,255),2) cv2.putText(img, 'Smile', (x, y - 7), 3, 1.2, (0, 0, 255), 2, cv2.LINE_AA) # 将照片显示出来 cv2.imshow("Video", img) cv2.waitKey() cv2.destroyAllWindows()if __name__ == '__main__': main()
五、接受电脑摄像头的实时输入,进行人的脸部、眼睛、微笑的识别
我们将通过摄像头捕捉一个新人脸,然后对捕捉的人脸部、眼睛、微笑进行实时的识别。
"""功能:借助电脑摄像头有检测人脸、眼睛、微笑的极简代码版本:V1.0"""import cv2def main(): # 打开摄像头 cap = cv2.VideoCapture(0) # 人脸识别分类器 faceCascade = cv2.CascadeClassifier("enginer\haarcascade_frontalface_default.xml") # 眼睛识别分类 eyeCascade = cv2.CascadeClassifier("enginer\haarcascade_eye.xml") # 微笑识别分类 smileCascade = cv2.CascadeClassifier("enginer\haarcascade_smile.xml") while True: #读取照片 cuess,img=cap.read() # 返回的faces是一个包含脸的坐标的列表,即脸在整个检测图像的x,y坐标位置,w、h脸部区域的长、宽 faces = faceCascade.detectMultiScale(img, 1.1, 4, minSize=(32, 32)) # 对图片中所有的人脸进行标记 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # 选出人脸区域,在人脸区域而不是全图中进行人眼检测,节省计算资源 face_area = img[y:y + h, x:x + w] eyes=eyeCascade.detectMultiScale(face_area,1.1,4) for (ex,ey,ew,eh) in eyes: cv2.rectangle(face_area,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) # 选出人脸区域,在人脸区域而不是全图中进行微笑检测,节省计算资源 smiles=smileCascade.detectMultiScale(face_area,1.1,4) for (sx,sy,sw,sh) in smiles: cv2.rectangle(face_area,(sx,sy),(sx+sw,sy+sh),(0,0,255),2) cv2.putText(img, 'Smile', (x, y - 7), 3, 1.2, (0, 0, 255), 2, cv2.LINE_AA) # 将照片显示出来 cv2.imshow("Video", img) k = cv2.waitKey(30) # cv2.waitKey(delay) 是屏幕刷新的时间 if k == 27: # press 'ESC' to quit 在ASCII码中esc按键为27,因此27对应的就是ESC退出键 break cap.release() cv2.destroyAllWindows()if __name__ == '__main__': main()
六、结语
本案例中
仅为人脸检测,非人脸“辩识”,即只能框出人脸的位置,看不出人脸是谁。
仅能标出静态图片和视频帧上的人脸、人眼和微笑,不能进行“活体识别”,即不能看出这张脸是真人还是手机上的照片,如果用于人脸打卡签到、人脸支付的话会带来潜在的安全风险。
仅为普通的机器学习方法,没有用到深度学习和深层神经网络。
七、改进提升
使用函数式编程在同一个程序里实现人脸、眼睛、微笑的识别,提高代码的复用率。
"""功能:使用函数式编程在同一个程序里实现人脸、眼睛、微笑的识别,提高代码的复用率版本:V1.0"""import cv2# 从文件读取照片def img_from_file(faceCascade, eyeCascade, smileCascade,n): img = cv2.imread("img/"+str(n)+".jpg", 1) recognize_enginer(faceCascade, eyeCascade, smileCascade, img) cv2.waitKey()# 从视频读取照片def img_from_capture(faceCascade,eyeCascade,smileCascade): cap=cv2.VideoCapture(0) sucess,img=cap.read() recognize_enginer(faceCascade, eyeCascade, smileCascade, img) cv2.waitKey() # 从笔记本摄像头实时读取照片def img_all_capture(faceCascade,eyeCascade,smileCascade): cap = cv2.VideoCapture(0) while True: sucess,img=cap.read() recognize_enginer(faceCascade, eyeCascade, smileCascade, img) k = cv2.waitKey(30) # cv2.waitKey(delay) if k == 27: # press 'ESC' to quit 在ASCII码中esc按键为27,因此27对应的就是ESC退出键 break cap.release() # 核心代码,识别人的脸、眼睛、微笑的关键代码,可以供其他程序单独调用def recognize_enginer(faceCascade,eyeCascade,smileCascade,img): # 返回的faces是一个包含脸的坐标的列表,即脸在整个检测图像的x,y坐标位置,w、h脸部区域的长、宽 faces = faceCascade.detectMultiScale(img, 1.3, 5, minSize=(32, 32)) # 对图片中所有的人脸进行标记 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) # 选出人脸区域,在人脸区域而不是全图中进行人眼检测,节省计算资源 face_area = img[y:y + h, x:x + w] eyes = eyeCascade.detectMultiScale(face_area) for (ex, ey, ew, eh) in eyes: cv2.rectangle(face_area, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2) smiles = smileCascade.detectMultiScale(face_area,scaleFactor= 1.16,minNeighbors=65,minSize=(25, 25),flags=cv2.CASCADE_SCALE_IMAGE) for (sx, sy, sw, sh) in smiles: cv2.rectangle(face_area, (sx, sy), (sx + sw, sy + sh), (0, 0, 255), 2) cv2.putText(img, 'Smile', (x, y - 7), 3, 1.2, (0, 0, 255), 2, cv2.LINE_AA) # 做完标记,显示出来 cv2.imshow("Video", img)# 程序的主函数def main(): # 人脸识别分类器 faceCascade = cv2.CascadeClassifier("enginer\haarcascade_frontalface_default.xml") # 眼睛识别分类 eyeCascade = cv2.CascadeClassifier("enginer\haarcascade_eye.xml") # 微笑识别分类 smileCascade = cv2.CascadeClassifier("enginer\haarcascade_smile.xml") # 以下三种方式可以选择其一 # ① 从文件读取照片 #img_from_file(faceCascade, eyeCascade, smileCascade,3) # ② 从摄像机读取照片 #img_from_capture(faceCascade,eyeCascade,smileCascade) # ③ 从摄像机读取实时照片 img_all_capture(faceCascade,eyeCascade,smileCascade) # 关闭 cv2.destroyAllWindows()#程序的入口if __name__ == '__main__': main()
微信编辑:徐恩伟
【微信小程序】几款使用的小程序推荐
【浏览器插件篇】浏览器插件推荐
【人工智能教学】人工智能将使中国教育仅存的优势荡然无存
【13】项目案例—Python爬取链家网租房信息
需要您的转发和阅读哦