【OpenCV-Python:实现人脸、人眼、嘴巴识别】实战(一)

  AI时代的到来,手机上的APP开始应用人脸识别去完成事情,如iphoneX的人脸解锁,百度自动贩卖机的人脸识别系统进行支付,支付宝的人脸识别登陆等,提升了使用软件的易用性,但也由于其便利性,在某些市面上的应用已经发生了非本人(活人)经过图片可经过人脸识别,想一想这样本身在应用上存储的信息和钱财不能获得保障,是一件很可怕的事情。由于这样,更应该去了解人脸识别的理论及实现,从而更好的去防范这种行为的发生。话很少说,把实现方法和在实现过程当中遇到的问题和你们讨论讨论~html

  1、环境准备python

    MacOs+PyCharm+Python+OpenCV数组

    1.Mac下PyCharm下载、安装、激活网络

      参考:http://blog.csdn.net/qq_35246620/article/details/78254527?utm_source=gold_browser_extensionapp

    2.Python安装python2.7

      Mac系统下自己已自带python2.7,因此这一步能够省去。要查看版本的话,终端使用以下命令查看:    iphone

which python python

      

    3.OpenCV安装ide

      在实现人脸识别的代码中,咱们须要用到的依赖库有:ui

        Pillow 5.0.0
        Pillow-PIL 0.1dev
        numpy 1.14.1
        opencv-python 3.3.0.10人工智能

      在本机环境安装并配置以上库步骤偏复杂,因此直接在PyCharm中该工程中设置虚拟环境,而后再在虚拟环境中下载安装。虚拟环境与本机进行隔离,在不一样的项目中使用到的第三方库版本可切换。下面是虚拟环境的建立和安装依赖库步骤:

      由于OpenCV是安装到虚拟环境中,而咱们后面须要的xml文件是在OpenCV/data/haarcascades/下,因此须要到OpenCV官网下载对应安装包

https://opencv.org/opencv-3-3.html 解压缩后,咱们就看到了xml文件。

 

  2、代码实现   

 1 # !/usr/bin/env python
 2 # coding=utf-8
 3 import os  4 import numpy  5 from PIL import Image, ImageDraw  6 import cv2  7 #created by chenfenyu 2018.3.20
 8 
 9 
 10 cap = cv2.VideoCapture(0)  11 #获取外接摄像头
 12 eye = cv2.imread("/Users/funny/Downloads/img/eye.png")  13 #读取眼睛区域替换的图片
 14 mouth = cv2.imread("/Users/funny/Downloads/img/mouth.png")  15 #读取嘴巴区域替换的图片
 16 size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) + 0.5), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) + 0.5))  17 #获取摄像头返回的宽和高
 18 fourcc = cv2.VideoWriter_fourcc(*'mp4v')  19 #肯定保存视频的格式
 20 video = cv2.VideoWriter("/Users/funny/Downloads/a.avi", fourcc, 5, size)  21 ''' cv.VideoWriter参数(视频存放路径,视频存放格式,fps帧率,视频宽高)  22  注意点1:OpenCV只支持avi的格式,并且生成的视频文件不能大于2GB,并且不能添加音频  23  注意点2:若填写的文件名已存在,则该视频不会录制成功,但可正常使用  24 '''
 25 print cap.isOpened()  26 #检测是否摄像头正常打开:成功打开时,isOpened返回ture
 27 classifier_face = cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_frontalface_alt.xml")  28 #定义分类器(人脸识别)
 29 classifier_eye = cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_eye.xml")  30 #定义分类器(人眼识别)
 31 classifier_mouth=cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_mcs_mouth.xml")  32 #定义分类器(嘴巴识别)
 33 while (True):  34     #取得cap.isOpened()返回状态为True,即检测到人脸
 35     #img = cv2.imread("/Users/funny/Downloads/img/pp.png")
 36     ret, img = cap.read()  37     '''第一个参数ret的值为True或False,表明有没有读到图片  38  第二个参数是frame,是当前截取一帧的图片  39     '''
 40     faceRects_face = classifier_face.detectMultiScale(img, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, (20, 20))  41     #检测器:detectMultiScale参数(图像,每次缩小图像的比例,匹配成功所须要的周围矩形框的数目,检测的类型,匹配物体的大小范围)
 42     key = cv2.waitKey(1)  43     #键盘等待
 44     if len(faceRects_face) > 0:  45         #检测到人脸
 46         for faceRect_face in faceRects_face:  47             x, y, w, h = faceRect_face  48             #获取图像x起点,y起点,宽,高
 49             h1=int(float(h/1.5))  50             #截取人脸区域高度的一半位置,以精确识别眼睛的位置
 51             intx=int(x)  52             inty=int(y)  53             intw=int(w)  54             inth=int(h)  55             #转换类型为int,方便以后图像截取
 56             my = int(float(y + 0.7 * h))  57             #截取人脸区域下半部分左上角的y起点,以精确识别嘴巴的位置
 58             mh = int(0.4 * h)  59             #截取人脸区域下半部分高度,以精确识别嘴巴的位置
 60             img_facehalf = img[inty:(inty+h1), intx:intx+intw]  61             img_facehalf_bottom = img[my:(my + mh), intx:intx + intw]  62             '''img获取坐标为,【y,y+h之间(竖):x,x+w之间(横)范围内的数组】  63  img_facehalf是截取人脸识别到区域上半部分  64  img_facehalf_bottom是截取人脸识别到区域下半部分  65             '''
 66             cv2.rectangle(img, (int(x), my), (int(x) + int(w), my + mh), (0, 255, 0), 2, 0)  67             '''矩形画出区域 rectangle参数(图像,左顶点坐标(x,y),右下顶点坐标(x+w,y+h),线条颜色,线条粗细)  68  画出人脸识别下部分区域,方便定位  69             '''
 70             faceRects_mouth = classifier_mouth.detectMultiScale(img_facehalf_bottom, 1.1, 1, cv2.CASCADE_SCALE_IMAGE, (5, 20))  71             #嘴巴检测器
 72             if len(faceRects_mouth) > 0:  73                 for faceRect_mouth in faceRects_mouth:  74                     xm1, ym1, wm1, hm2 = faceRect_mouth  75                     cv2.rectangle(img_facehalf_bottom, (int(xm1), int(ym1)), (int(xm1) + int(wm1), int(ym1) + int(hm2)), (0,0, 255), 2, 0)  76                     img_mx = cv2.resize(mouth, (wm1, hm2), interpolation=cv2.INTER_CUBIC)  77                     #调整覆盖图片大小 resize参数(图像,检测到的(宽,高),缩放类型)
 78                     if key == ord('z'):  79                     #检测当键盘输入z时,开始替换图片
 80                         img[my+ym1:(my+ym1+hm2), intx+xm1:(intx + xm1+wm1)] = img_mx  81                         #将调整大小后的图片赋值给img
 82             cv2.rectangle(img, (int(x), int(y)), (int(x) + int(w), int(y) + int(h1)), (0, 255, 0), 2, 0)  83             # 画出人脸识别上部分区域,方便定位
 84             faceRects_eye = classifier_eye.detectMultiScale(img_facehalf, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, (20, 20))  85             #检测器识别眼睛
 86             if len(faceRects_eye) > 0:  87                 #检测到眼睛后循环
 88                 eye_tag = []  89                 #定义一个列表存放两只眼睛坐标
 90                 for faceRect_eye in faceRects_eye:  91                     x1, y1, w1, h2 = faceRect_eye  92                     cv2.rectangle(img_facehalf, (int(x1), int(y1)), (int(x1) + int(w1), int(y1) + int(h2)), (0, 255, 0), 2, 0)  93                     #画出眼睛区域
 94                     a = ((inty+y1),(inty+y1 + h2), (intx+x1),(intx+x1 + w1))  95                     #定义a变量获取眼睛坐标,如今img顶点位置已经改变,须要加上intx和inty的值才能够
 96  eye_tag.append(a)  97                     #经过append存入数组a中
 98                 n_eyetag = numpy.array(eye_tag)  99                 #存放为ndarray数组类型,输入内容为[[x1 y1 x1+w y1+h][x1 y1 x1+w y1+h]...],后面会获取多维数组的下标来替换数值
100 
101                 if len(faceRects_eye)==2: 102                     #眼睛识别到两个时,同时替换图片
103                     img_ex=cv2.resize(eye,(n_eyetag[0,1]-n_eyetag[0,0], n_eyetag[0,3]-n_eyetag[0,2]),interpolation=cv2.INTER_CUBIC) 104                     img_ex1 = cv2.resize(eye, (n_eyetag[1, 1] - n_eyetag[1, 0], n_eyetag[1, 3] - n_eyetag[1, 2]), interpolation=cv2.INTER_CUBIC) 105                     if key == ord('p'): 106                     #检测到键盘输入p时,进行替换
107                         img[n_eyetag[0,0]:n_eyetag[0,1],n_eyetag[0,2]:n_eyetag[0,3]]=img_ex 108                         img[n_eyetag[1, 0]:n_eyetag[1, 1], n_eyetag[1, 2]:n_eyetag[1, 3]] = img_ex1 109                 if len(faceRects_eye)==1: 110                     # 眼睛识别到一个时,替换图片
111                     img_ex = cv2.resize(eye, (n_eyetag[0, 1] - n_eyetag[0, 0], n_eyetag[0, 3] - n_eyetag[0, 2]), interpolation=cv2.INTER_CUBIC) 112                     if key == ord('p'): 113                         img[n_eyetag[0, 0]:n_eyetag[0, 1], n_eyetag[0, 2]:n_eyetag[0, 3]] = img_ex 114 
115  video.write(img) 116     cv2.imshow('video', img) 117     #显示图片,标题名字为video
118     cv2.resizeWindow('video',1280,720) 119     #调整窗口大小video为1280*720
120     if key == ord('q'): 121     #检测到键盘输入q,退出循环
122         break
123 
124 video.release() 125 #再也不录制视频
126 cap.release() 127 #释放摄像头
128 cv2.destroyAllWindows() 129 #关闭全部窗口显示

    代码中都有注释,基本看懂第一个循环输出人脸区域检测的内容就足够了,后面只是在原来的基础上进行多个区域的循环和判断。接下来我来说一下这个代码的实现思路,画成图解释一下:

      

   

 

    1.打开摄像头的时候,开始检测人脸的区域。

    2.将获取到的区域上、下半部分图像存储下来。

    3.检测上半部分图像区域中双眼的位置。

    4.检测下半部分图像区域中嘴巴的位置。

    5.模拟人脸识别所进行的操做,如眨眼,摇头,慢慢张开嘴巴,这里咱们直接使用键盘‘p’来替换眼睛的位置,‘z’替换嘴巴的位置。

    6.关闭摄像头。

    说到这里,你们可能有点疑问为何要划出上下部分区域来识别眼睛和鼻子,缘由是识别器的准确度不是很高,若检测眼睛,鼻子区域也会识别成眼睛,因此才须要划分范围,请看下图:

     下面我来跟你们讲一下IMG取某区域内图像的坐标以及cv2.rectangle的定位坐标。

      1)cv2.rectangle,咱们从上面的注释中知道,获取的是左上角和右下角的顶点坐标,用画图的方式表达以下:

      2)IMG区域取值范围则和1)不一样,IMG[左上顶点y:左下顶点y,左上顶点x:右上顶点x],用图来表示S◇acef区域以下:

         因此,以后截取后记得以最原始的IMG坐标为顶点,再来表示要截取的区域,否则就会报错。

  3、Q&A

    一、Q:2018-03-20 11:17:10.309 python[79368:108479406] mMovieWriter.status: 3. Error: Cannot Save

       A:存放信息目录下已有同名文件,只须要删除文件便可,不影响程序正常使用

    二、Q:网络上下载的opencv 识别器xml文件,运行工程时提示

OpenCV Error: Unknown error code -49 (Input file is empty) in cvOpenFileStorage, file /Users/travis/build/skvark/opencv-python/opencv/
modules/core/src/persistence.cpp, line 4484 Traceback (most recent call last): File "/Users/funny/PycharmProjects/untitled1/open-cv.py", line 19, in <module> classifier_mouth=cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_mcs_mouth1.xml") cv2.error: /Users/travis/build/skvark/opencv-python/opencv/modules/core/src/persistence.cpp:4484: error: (-49) Input file is empty in
function cvOpenFileStorage

      A:通常xml文件不会超过1m我下载的这个文件超过了6m,如何检测是不是可用文件?在存放xml文件的目录下,单击后在预览区能显示内容则是正确的,请看下图:

  

        不能显示内容则是错误不可以使用的文件:

      

     三、Q:报错信息:没法覆盖图像

File "/Users/funny/PycharmProjects/untitled1/open-cv.py", line 52, in <module>
    img[ym1:(my+ym1+hm2), intx+xm1:(intx + xm1+wm1)] = img_mx
ValueError: could not broadcast input array from shape (86,143,3) into shape (662,143,3)

      A:计算图像的位置不正确,已经超出了范围,建议参考二代码中讲解图像IMG显示的区域坐标。 

  4、程序已知的问题

    一、暂时定位嘴巴和眼睛位置是以人脸分开两个区域来缩小搜索检测范围,因此抬起头或多人时,仍会检测到别的地方,这是因为OpenCV内的Haar特征分类器训练模型仍是不够,因此检测不够精准。程序实现的是实时检测摄像头内图像,换成识别静态图片则准确多了。

    二、只要检测到人脸,就会一直循环检测,因此当人物晃动的时候,能检测到多个眼睛和嘴巴,并且检测框不断变换大小。

    三、程序暂时只实现了单眼、双眼同时替换,嘴巴替换。当摄像头检测到多个眼睛,只能同时替换至多两个眼睛。

    四、替换的图片在肉眼看来很假,瞳孔颜色,眼睛大小都还不够精准,只是用替换图替换。

  5、参考资料

    一、OpenCV教程

    二、Python入门教程

    三、人工智能书籍

相关文章
相关标签/搜索