小啾带你开天眼 之 人脸检测与识别(以及华强、皇叔、高祖配墨镜特效)【Python-Open_CV系列(十三)】


小啾带你开天眼 人脸识别
            【Python-Open_CV系列(十三)】

文章目录

  • ①素材准备
  • ②人脸检测
  • (一)级联分类器 与 相关方法介绍
  • (二)检测人脸
  • ③华强、皇叔、高祖配墨镜
  • ④人脸识别

  •       ʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞ
                     请添加图片描述请添加图片描述请添加图片描述
        ʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞʚʕ̯•͡˔•̯᷅ʔɞ


    🌹꧔ꦿ本系列blog传送门:

    OpenCV图像处理基本操作 【Python-Open_CV系列(一)】

    OpenCV像素处理基本操作 【Python-Open_CV系列(二)】

    OpenCV之 BGR、GRAY、HSV色彩空间&色彩通道专题 【Python-Open_CV系列(三)】

    OpenCV绘制图像与文字(可作为脚手架代码)(python) 【Python-Open_CV系列(四)】

    OpenCV图像几何变换专题(缩放、翻转、仿射变换及透视)【python-Open_CV系列(五)】

    基于梵·高《向日葵》的 图像阈值处理专题(二值处理、反二值处理、截断处理、自适应处理及Otsu方法)【Python-Open_CV系列(六)】

    OpenCV基本功 之 图像的掩模、运算 & 合并专题 -小啾带学【Python-Open_CV系列(七)】

    《三英战吕布》 – 图像模板匹配 【Python-Open_CV系列(八)】

    OpenCV滤波器 龙门石窟篇【Python-Open_CV系列(九)】(均值滤波器、中值滤波器、高斯滤波器、双边滤波器)

    Open_CV形态学运算专题 (腐蚀&膨胀、开&闭运算、梯度运算、顶帽运算黑帽运算 )【Python-Open_CV系列(十)】

    霍夫变换看不懂?小啾带你串一遍:OpenCV图形检测专题 这样学最简单【Python-Open_CV系列(十一)】

    小啾带你开天眼 之 开启py-OpenCV摄像头及视频处理【Python-Open_CV系列(十二)】

    小啾带你开天眼 之 人脸检测与识别(以及华强、皇叔、高祖配墨镜特效)【Python-Open_CV系列(十三)】


    大家好,我是侯小啾!

    今天分享的内容是,使用python-OoenCV做人脸识别。废话不多说,开始整活!


    ①素材准备

    正式开始之前,首先要解决的是素材问题。

    大家在学习过程中可以直接从百度上自选图片。比如,小啾准备了以下图片素材。并将其放在demo.py的当前目录下的picture文件夹中。小啾文件命名使用的是演员简称+序号的形式,cdm_1.jpg至cdm_5.jpg,shl_1.jpg至shl_5.jpg,yhw_1.jpg到yhw_5.jpg,共计三个人,十五张图。


    提示:准备图像素材时,尽量选择大小相近的,至少不能有的高>宽,有的宽>高这样的混合选择,否则这样会给后边的处理过程带来很大不便。后边需要把每个图像的shape调整一致。


    ②人脸检测

    所谓人脸检测,即从一幅图片中找出人脸的位置,OpenCV已经将具体的算法封装完毕,我们只需调用即可。

    (一)级联分类器 与 相关方法介绍

    OpenCV提供了一些已经训练好的级联分类器,这些级联分类器的位置在cv2文件夹内的data文件夹中,如下图所示,这些XML文件即为级联分类器:



    其中,不同分类器的功能是也有所不同的。
    比如,haarcascade_eye.xml是用来 检测眼睛的;
    haarcascade_eye_tree_eyeglasses.xml是用来检测眼镜的;
    haarcascade_frontalcatface.xml是用来检测正面猫脸的;
    haarcascade_frontalcatface_extended.xml,即正面猫脸的拓展
    haarcascade_frontalface_default.xml,haarcascade_frontalface_alt.xml,haarcascade_frontalface_alt2.xml,haarcascade_frontalface_alt_tree.xml是什么都是用来检测正面人脸的。算法不同,效果也可能会有所不同。具体选择要根据对需求的测试和进一步的学习。
    haarcascade_fullbody.xml是用来减少人的存在(身体)的;
    haarcascade_lefteye_2splits.xml用于检测左眼…

    这里就暂先列举这么多,更多级联分类器可以自行了解。



    相关的方法

    使用级联分类器,首先需要使用cv2.CascadeClassifier()方法创建出一个级联分类器对象Cascade,其语法为

    Cascade = cv2.CascadeClassifier(filename)

    其中filename即级联分类器的文件名及所在目录。

    ===============================================

    创建好级联分类器后,使用detectMultiScale()方法对图像进行识别,detectMultiScale()方法的语法为

    objects = Cascade.detectMultiScale(image, scaleFactor, minNeighbors, flags, minSize, maxSize)

    其中,image即原图像,scaleFactor是扫描时的缩放比例(可选);minNeighbors是指每个检测区域至少保留多少个结果才能检测为人脸,值越大分析误差越小,但是也越容易识别不出。(可选)minSize指最小的目标尺寸,maxSize指最大的目标尺寸,都可选。 flags为旧版本OPenCV的参数,可选,建议使用默认。


    (二)检测人脸

    下边使用级联分类器检测人脸,并在人脸位置画出红框以标记之。
    使用不同的分类器效果会有所不同。

    使用cv2.CascadeClassifier()方法加载级联分类器,创建出级联分类器对象。参数为级联分类器的文件名,可以将其复制到在方便我们使用的位置,比如当前目录。
    使用faceCascade.detectMultiScale()方法对图像进行识别,其中faceCascade是使用cv2.CascadeClassifier()方法创建的级联分类器对象。
    如使用haarcascade_frontalface_default.xml 会误把cdm_2.jpg和cdm_5.jpg中的背景也识别为了人脸(如下图所示),而使用haarcascade_frontalface_alt.xml 识别不出yhw_3中的曹操脸,经过测试使用cascades\haarcascade_frontalface_alt2.xml可以不多不少识别出十五张图中的所有人脸。
           

    以检测cdm_4.jpg中的人脸为例,使用cascades\haarcascade_frontalface_alt2.xml级联分类器,
    代码如下:

    import cv2
    # 以检测cdm_4.jpg中的人脸为例
    img = cv2.imread("pictures\\cdm_4.jpg")
    # 加载识别人脸的级联分类器
    # faceCascade = cv2.CascadeClassifier("cascades\\haarcascade_frontalface_default.xml")
    # faceCascade = cv2.CascadeClassifier("cascades\\haarcascade_frontalface_alt.xml")
    faceCascade = cv2.CascadeClassifier("cascades\\haarcascade_frontalface_alt2.xml")
    # 识别出所有人脸
    faces = faceCascade.detectMultiScale(img, 1.3)
    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 5)
    cv2.imshow("img", img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    图片中人物的人脸成功被识别出。

    其他图片及级联分类器读者可自行尝试,笔者不再展示。


    ③华强、皇叔、高祖配墨镜

    检测出人脸后,我们就可以根据人脸所在的位置,使用其他的含有透明像素的图像来覆盖,就可以实现一系列好用的特效,比如给买瓜的华强和蹦迪的皇叔配上墨镜。

    要实现这个特效首先需要准备的就是墨镜的素材图片了:

                  

    算法思路大致如下:

    首先定义一个覆盖图像的函数,需要传入的参数有四个,依次是①原背景图像,②调整好大小的覆盖图像,③覆盖图像的左上角在原背景图像中位置的x坐标值X,④要覆图像的左上角在原背景图像中位置的左上角的y轴坐标值Y。
    函数执行过程中覆盖图像的像素数组如果不是四通道,这应该改为BGRA四通道格式,然后依次对该BGRA图像上的每个像素点进行判断,如果A通道的值为0,即该位置像素为透明的,则该位置不需要执行覆盖。否则用该位置像素的B、G、R三个通道值替换图像时对应位置的三个通道值。该对应位置表述为在(X, Y)的基础上,加上该像素点在覆盖图像上的坐标点。

    此外如果该像素点在原图像上的位置超出了背景图像的边界,则也不予覆盖。

    然后就是在调用该函数之前,需要对覆盖图像的大小进行调整。首先选择合适的级联分类器,这里小啾选择的是haarcascade_frontalface_alt2.xml级联分类器,然后对图像中的人脸进行识别以获取到人脸在图像中高度左上角坐标,以及人脸区域的宽、高。

    得到这些数据后,就可以对覆盖图像的大小进行调整了调整过程根据背景图像中人脸的大小灵活调整。比如这里将覆盖图像的长调整为人脸在图像中宽度的0.8倍(要记得取整),然后再将覆盖图像的高度按照其宽度缩小的相同的比例进行缩小。

    随即,就是要确定覆盖图像覆盖在原背景图中左上角的位置,小啾这里选择的x坐标为,识别出的人脸区域的左上角x坐标加上0.1倍的区域宽度并取整;y轴坐标选择的是识别区域左上角的y坐标加上 三分之一倍的人脸区域的高度并取整。

    大家在具体操作过程中可以根据需求自行调试,做出更佳的选择。比如如果背景图像中的人物带了眼镜,我们再给其加墨镜特1效,可能就会因为眼镜框的不完全覆盖导致画面整体的视觉效果较差,这个时候可以考虑增大覆盖图像的高度,或者并做少许下移。

    确定好这些数据后,调用上边定义好的函数即可。


    import cv2
    
    
    # 定义覆盖图像函数
    def overlay_img(img, img_over, img_over_x, img_over_y):
        img_h, img_w, img_p = img.shape  # 背景图像宽、高、通道数
        img_over_h, img_over_w, img_over_c = img_over.shape  # 覆盖图像高、宽、通道数
        # 如果图像通道数为3,则转换成4通道图像,增加一个表示透明度的。
        if img_over_c == 3:
            img_over = cv2.cvtColor(img_over, cv2.COLOR_BGR2BGRA)
        for h in range(0, img_over_h):
            for w in range(0, img_over_w):
                # 判断该像素点如果不透明,则执行覆盖。否则不用覆盖。
                if img_over[h, w, 3] != 0:
                    # 如果该位置超出背景图边界则也不用执行覆盖
                    X = img_over_x + w
                    Y = img_over_y + h
                    if X > img_w or Y > img_h:
                        break
                    img[Y, X, 0] = img_over[h, w, 0]
                    img[Y, X, 1] = img_over[h, w, 1]
                    img[Y, X, 2] = img_over[h, w, 2]
        return img
    
    
    face_img = cv2.imread("pictures\\shl_4.jpg")  # 读取人脸图像
    glass_img = cv2.imread("glass.png", cv2.IMREAD_UNCHANGED)  # 读取眼镜图像,保留图像类型
    height, width, channel = glass_img.shape  # 获取眼镜图像高、宽、通道数
    # 加载级联分类器 haarcascade_frontalface_alt2.xml
    face_cascade = cv2.CascadeClassifier("cascades\\haarcascade_frontalface_alt2.xml")
    # garyframe = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY)  # 转为黑白图像
    faces = face_cascade.detectMultiScale(face_img, 1.3)  # 识别人脸
    
    # 根据人脸大小缩放眼镜 并覆盖
    for (x, y, w, h) in faces:  
        gw = int(w * 0.8)   
        gh = int(height * gw / width)  
        glass_img = cv2.resize(glass_img, (gw, gh))  
        overlay_img(face_img, glass_img, int(x + 0.1 * w), y + int(h * 1 / 3))
    cv2.imshow("screen", face_img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    对“shl_4.jpg”图像文件,代码执行效果如下:
          


    对“yhw_2.jpg”,代码的执行效果如下:
            


    对“cdm_4.jpg”,代码的执行效果如下:

    (因为这里选择的陈道明系列的剧照,头基本上都是歪的,针对这个问题,可以对覆盖图像使用一下仿射变换中的选择操作,旋转一定角度后再执行上述代码效果则会更佳,可自行尝试,这里不再展示)(旋转的同时要保证不需要的区域透明度为零)


    ④人脸识别

    OpenCV提供了三种人脸识别的方法,分别是Eigenfaces、Fisherfaces和LBPH三种人脸识别器。三种算法的算法有所不同,各具特色。


    Eigenfaces人脸识别器也叫“特征脸”,是根据 主成分分析(PCA)方法实现的。
    Fisherfaces人脸识别器是根据提出者的姓名命名的,其是根据 线性判别分析技术(LDA)方法实现的。
    LBPH人脸识别器即Local Binary Patter Histogram人脸识别器,是一种基于局部二进制模式算法,这种算法善于捕捉纹理特征。

    OpenCV提供的创建人脸识别器的方法为:


    recognizer = cv2.face.EigenFaceRecognizer_create(num_components, threshold)

    recognizer = cv2.face.FisherFacesRecognizer_create(num_components, threshold)

    recognizer = cv2.face.LBPHFaceRecognizer_create(radius, neighbors, grid_x, grid_y, threshold)

    这里分别使用每个人物的前四张图像作为样本,第五张图像作为测试图像进行人脸识别。
    经过测试,本样本使用Eigenfaces和Fisherfaces的识别结果都不佳(也可能是因为剧中的化妆效果所致)。使用LBPH人脸识别器则可以准确地识别出三个人。

    另一个需要注意并保证的细节是,进行人脸识别是,无论是训练集,还是测试集的图像,都应保证shape一致,必需相同大小

    import cv2
    import numpy as np
    
    # 先读取索引训练集的图像
    img1 = cv2.imread("pictures\\cdm_1.jpg", 0)
    img2 = cv2.imread("pictures\\cdm_2.jpg", 0)
    img3 = cv2.imread("pictures\\cdm_3.jpg", 0)
    img4 = cv2.imread("pictures\\cdm_4.jpg", 0)
    img6 = cv2.imread("pictures\\shl_1.jpg", 0)
    img7 = cv2.imread("pictures\\shl_2.jpg", 0)
    img8 = cv2.imread("pictures\\shl_3.jpg", 0)
    img9 = cv2.imread("pictures\\shl_4.jpg", 0)
    img11 = cv2.imread("pictures\\yhw_1.jpg", 0)
    img12 = cv2.imread("pictures\\yhw_2.jpg", 0)
    img13 = cv2.imread("pictures\\yhw_3.jpg", 0)
    img14 = cv2.imread("pictures\\yhw_4.jpg", 0)
    
    # 以cdm_4.jpg的大小为基准,将其他图像也都调整为该大小。
    y, x = img4.shape
    img1 = cv2.resize(img1, (x, y))
    img2 = cv2.resize(img2, (x, y))
    img3 = cv2.resize(img3, (x, y))
    img6 = cv2.resize(img6, (x, y))
    img7 = cv2.resize(img7, (x, y))
    img8 = cv2.resize(img8, (x, y))
    img9 = cv2.resize(img9, (x, y))
    img11 = cv2.resize(img11, (x, y))
    img12 = cv2.resize(img12, (x, y))
    img13 = cv2.resize(img13, (x, y))
    img14 = cv2.resize(img14, (x, y))
    
    # 将样本图像和标签都分别添加进列表中
    # cdm的图像标签为0,shl的图像标签为1,yhw的图像标签为2
    photos = list()
    lables = list()
    photos.append(img1)
    lables.append(0)
    photos.append(img2)
    lables.append(0)
    photos.append(img3)
    lables.append(0)
    photos.append(img4)
    lables.append(0)
    
    photos.append(img6)
    lables.append(1)
    photos.append(img7)
    lables.append(1)
    photos.append(img8)
    lables.append(1)
    photos.append(img9)
    lables.append(1)
    
    photos.append(img11)
    lables.append(2)
    photos.append(img12)
    lables.append(2)
    photos.append(img13)
    lables.append(2)
    photos.append(img14)
    lables.append(2)
    
    # 用字典匹配每个人的名字和上边设定的标签
    names = {"0": "cdm", "1": "shl", "2": "yhw"}
    # 创建特征脸识别器
    # recognizer = cv2.face.EigenFaceRecognizer_create()
    # 创建线性判别分析识别器
    # recognizer = cv2.face.FisherFaceRecognizer_create()  
    # 创建 LBPH人脸识别器
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    
    # 识别器开始训练
    recognizer.train(photos, np.array(lables))
    

    模型训练完成,下边开始使用测试集的图像进行识别。


    读取测试集图像,并也调整为上述大小:

    img5 = cv2.imread("pictures\\cdm_5.jpg", 0)
    img10 = cv2.imread("pictures\\shl_5.jpg", 0)
    img15 = cv2.imread("pictures\\yhw_5.jpg", 0)
    img5 = cv2.resize(img5, (x, y))
    img10 = cv2.resize(img10, (x, y))
    img15 = cv2.resize(img15, (x, y))
    

    识别img5:cdm_5.jpg

    img = img5
    label, confidence = recognizer.predict(img)
    print("confidence = " + str(confidence))
    print("识别结果:", names[str(label)])
    cv2.imshow("img", img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

                     


    识别img10:shl_5.jpg

    img = img10
    label, confidence = recognizer.predict(img)
    print("confidence = " + str(confidence))
    print("识别结果:", names[str(label)])
    cv2.imshow("img", img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    

                     


    识别img15:yhw_5.jpg

    img = img15
    label, confidence = recognizer.predict(img)
    cv2.imshow("img", img)
    print("confidence = " + str(confidence))
    print("识别结果:", names[str(label)])
    

                     


    本次分享就到这里,本专栏也到此就告一段落啦。小啾感谢您的关注与支持!更多精彩内容敬请期待!
    🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ

    来源:侯小啾

    物联沃分享整理
    物联沃-IOTWORD物联网 » 小啾带你开天眼 之 人脸检测与识别(以及华强、皇叔、高祖配墨镜特效)【Python-Open_CV系列(十三)】

    发表评论