OpenCV3入门教程(四)人脸检测与识别

------韦访 20181023

1、概述

学Tensorflow的时候学过怎么用神经网络来做人脸检测和识别,现在看看Opencv怎么做。

2、Haar特征

Haar-like特征是计算机视觉领域一种常用的特征描述算子,也称为Haar特征,因为Haar-like特征是受一维Haar小波的启示而发明的,所以也称为类Haar特征。 

Haar-like特征模板内只有白色和黑色两种矩形,并定义该模板的特征值为白色矩阵像素之和“减去”黑色矩阵像素之和,它反应了图像的灰度变化情况。

Haar-like特征一般分为三类:边缘特征、线性特征(包含对角线特征)、点特征(也称为中心特征),如下图所示,

OpenCV3入门教程(四)人脸检测与识别

通过改变特征模板的大小和位置,就可以在图像子窗口中穷举出大量的特征。而这些特征的集合称为级联。OpenCV提供了尺度不变Haar级联的分类器和跟踪器,并可将其保存成指定的文件格式。但OpenCV的Haar级联不具备旋转不变性,即,OpenCV认为同一张人脸,倒置的图片和直立的图片不一样。

 

3、获取Haar级联数据

OpenCV根目录下的data/haarcascades文件夹下包含了所有OpenCV的人脸检测的XML文件,这些文件可以用于检测静止图像、视频以及摄像头所得到的图像中的人脸。

OpenCV3入门教程(四)人脸检测与识别

 

将所有的XML文件拷贝到我们工程文件夹下。

4、静态图像人脸检测

代码如下,

#encoding:utf-8
import cv2

face_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')

gray_img = cv2.imread('xi.jpg', flags=cv2.IMREAD_GRAYSCALE)

faces = face_cascade.detectMultiScale(gray_img, 1.01, 5)
for (x, y, w, h) in faces:
    gray_img = cv2.rectangle(gray_img, (x, y), (x + w, y + h),(255, 0, 0), 2)

cv2.imshow('gray_img', gray_img)
cv2.waitKey(0)

运行结果:

OpenCV3入门教程(四)人脸检测与识别

 detectMultiScale函数的第二个参数scaleFactor,可以根据情况调节,它表示人脸检测过程中每次迭代时图像的压缩率。我们试着将它变大和变小看看有什么现象。首先将其改为1.1,

OpenCV3入门教程(四)人脸检测与识别

 发现检测到的人脸的数量少了,如果改为1.5呢?

OpenCV3入门教程(四)人脸检测与识别

一张人脸都检测不到了。

反之,将其改小看看,先改成1.001,

OpenCV3入门教程(四)人脸检测与识别

把不是人脸的地方也识别成人脸了,而且运行的时间明显变长了。

5、视频图像人脸检测

首先,试着播放视频,代码如下,

import cv2

videoCapture = cv2.VideoCapture('./003.flv')

fps = videoCapture.get(cv2.CAP_PROP_FPS)
size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
        int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))

success, frame = videoCapture.read()
while success:
    cv2.imshow('Video', frame)
    cv2.waitKey(1000 / int(fps))
    success, frame = videoCapture.read()

cv2.destroyWindow('Video')
videoCapture.release()

运行结果,

Unable to stop the stream: Inappropriate ioctl for device

百度了一下,说是没安装ffmpeg,但是我已经安装了,不过版本有点低,0.8版的,将ffmpeg版本升级到4.0试试。再运行,结果如下,

OpenCV3入门教程(四)人脸检测与识别

 好了,现在,我们就对这个视频进行人脸检测,为了方便查看识别的准确率,我们将识别出来的人脸保存到本地,代码如下,

import cv2

videoCapture = cv2.VideoCapture('./003.flv')

fps = videoCapture.get(cv2.CAP_PROP_FPS)
size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
        int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))

faceCascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
success, frame = videoCapture.read()
faceIndex = 0
while success:
    gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = faceCascade.detectMultiScale(gray_img, 1.5, 5)
    for (x, y, w, h) in faces:
        gray_img = cv2.rectangle(gray_img, (x, y), (x + w, y + h), (255, 0, 0), 2)
        cv2.imwrite('image/face' + str(faceIndex) + '.jpg', gray_img[y:y+h, x:x+w])
        faceIndex += 1
    cv2.imshow('Video', gray_img)
    cv2.waitKey(1000 / int(fps))
    success, frame = videoCapture.read()

cv2.destroyWindow('Video')
videoCapture.release()

运行结果:

OpenCV3入门教程(四)人脸检测与识别

 OpenCV3入门教程(四)人脸检测与识别

 OpenCV3入门教程(四)人脸检测与识别

 

可以看到,虽然也能将人脸识别出来了,但是,很多不是人脸的,他也当成人脸了,而有些很明显的人脸,却没识别出来。通过观测误识的图片大小,发现很多误识的图片大小都是小于100x100像素的,那么,修改代码,认为小于100x100的图片不是人脸。只要修改这行即可,代码如下,

faces = faceCascade.detectMultiScale(gray_img, 1.550minSize=(100100))

运行结果:

OpenCV3入门教程(四)人脸检测与识别

还是有一些误识的人脸,但是,609张图片里只有13张是误识的,已经比上一个结果好很多了。

 

6、OpenCV人脸识别算法简介

Opencv有三种人脸识别算法,分别是Eigenfaces、Fisherfaces和Local Binary Pattern Histogram(LBPH)。

Eigenfaces是通过PCA来处理的,PCA的本质就是识别某个人脸的主成分和人脸数据库中的对比,输出一个值,这个值越小,说明两个人脸的差别越小,如果为0,表示完全匹配。

Fisherfaces是从PCA衍生而来的,比Eigenfaces更容易得到准确的效果。

LBPH粗略的将检测到的人脸分成小单元,并将其与模型中对应的单元进行比较,对每个区域的匹配值产生一个直方图。LBPH是唯一允许模型样本人脸和检测到的人脸在形状和大小都可以不同的人脸识别算法。

7、生成人脸识别样本数据

首先要确定样本数据满足以下条件:

  1. 图像是灰度图像,后缀为.pgm
  2. 图像形状为正方形
  3. 图像大小都一样,这里使用200x200

直接使用上面检测视频中的人脸的代码来改,代码如下,

import cv2

videoCapture = cv2.VideoCapture('./003.flv')

fps = videoCapture.get(cv2.CAP_PROP_FPS)
size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
        int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))

faceCascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
success, frame = videoCapture.read()
faceIndex = 0
while success:
    gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = faceCascade.detectMultiScale(gray_img, 1.5, 5, 0, minSize=(100, 100))
    for (x, y, w, h) in faces:
        gray_img = cv2.rectangle(gray_img, (x, y), (x + w, y + h), (255, 0, 0), 2)
        f = cv2.resize(gray_img[y:y + h, x:x + w], (200, 200))
        cv2.imwrite('image3/face' + str(faceIndex) + '.pgm', f)
        faceIndex += 1
    cv2.imshow('Video', gray_img)
    cv2.waitKey(1000 / int(fps))
    success, frame = videoCapture.read()

cv2.destroyWindow('Video')
videoCapture.release()

挑出20张同一个人的人脸,并为其标上标签,写入CSV文件,如下所示,

OpenCV3入门教程(四)人脸检测与识别

其中,label为9表示女主,

OpenCV3入门教程(四)人脸检测与识别

label为91表示男主,

OpenCV3入门教程(四)人脸检测与识别

分别挑20张图片,并依规则写入CSV文件中。

OpenCV3入门教程(四)人脸检测与识别

 

8、安装opencv_contrib库

在进行人脸识别前,需要安装opencv_contrib库,执行以下命令即可,

sudo pip install opencv_contrib_python

9、代码实现

解析CSV文件,并读取图片,

#encoding:utf-8
import cv2
import pandas as pd
import numpy as np

def readImages(path):
    cvsData = pd.read_csv(path, header=None)
    # print(cvsData.head())
    cvsData = cvsData.as_matrix()
    images,labels = [], []
    for filename, id in cvsData[:]:
        # print(filename)
        # print(id)
        img = cv2.imread(filename, cv2.COLOR_BGR2GRAY)
        img = cv2.resize(img, (200, 200))
        images.append(np.asarray(img, dtype=np.uint8))
        labels.append(id)

    return images, labels

打开视频,进行人脸检测,并识别检测到的人脸,然后对识别出的人脸归类,label为91表示男主,label为9表示女主,注意,predict函数返回的是人脸label和可信度,对于Eigenfaces来说,小于5000的评分都是相当可靠的识别,评分为0表示完全匹配。为了对比结果,我们将识别到的结果以图片的形式保存起来。

def faceRec():
    [images, labels] = readImages('image4/data.csv')
    labels = np.asarray(labels, dtype=np.int32)
    model = cv2.face.EigenFaceRecognizer_create()
    # print(images)
    # print(labels)
    model.train(np.asarray(images), np.asarray(labels))

    videoCapture = cv2.VideoCapture('./003.flv')

    fps = videoCapture.get(cv2.CAP_PROP_FPS)

    faceCascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
    success, frame = videoCapture.read()
    faceIndex = 0
    while success:
        gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = faceCascade.detectMultiScale(gray_img, 1.5, 5, 0, minSize=(100, 100))
        for (x, y, w, h) in faces:
            gray_img = cv2.rectangle(gray_img, (x, y), (x + w, y + h), (255, 0, 0), 2)
            f = cv2.resize(gray_img[y:y + h, x:x + w], (200, 200))
            params = model.predict(f)
            print(params)
            if params[0] == 91 and params[1] <= 5000:
                #男主
                cv2.imwrite('image5/male_' + str(faceIndex) + '.pgm', f)
            elif params[0] == 9 and params[1] <= 5000:
                # 女主
                cv2.imwrite('image5/female_' + str(faceIndex) + '.pgm', f)
            faceIndex += 1

        cv2.imshow('Video', gray_img)
        cv2.waitKey(1000 / int(fps))
        success, frame = videoCapture.read()

    cv2.destroyWindow('Video')
    videoCapture.release()

运行结果,

OpenCV3入门教程(四)人脸检测与识别

模型还是能很好的将男主和女主区别出来的,并且没有误识,也没有将其他人脸识别成这个两个人。人脸识别就讲到这了。

完整代码如下,

#encoding:utf-8
import cv2
import pandas as pd
import numpy as np

def readImages(path):
    cvsData = pd.read_csv(path, header=None)
    # print(cvsData.head())
    cvsData = cvsData.as_matrix()
    images,labels = [], []
    for filename, id in cvsData[:]:
        # print(filename)
        # print(id)
        img = cv2.imread(filename, cv2.COLOR_BGR2GRAY)
        img = cv2.resize(img, (200, 200))
        images.append(np.asarray(img, dtype=np.uint8))
        labels.append(id)

    return images, labels

def faceRec():
    [images, labels] = readImages('image4/data.csv')
    labels = np.asarray(labels, dtype=np.int32)
    model = cv2.face.EigenFaceRecognizer_create()
    # print(images)
    # print(labels)
    model.train(np.asarray(images), np.asarray(labels))

    videoCapture = cv2.VideoCapture('./003.flv')

    fps = videoCapture.get(cv2.CAP_PROP_FPS)

    faceCascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
    success, frame = videoCapture.read()
    faceIndex = 0
    while success:
        gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = faceCascade.detectMultiScale(gray_img, 1.5, 5, 0, minSize=(100, 100))
        for (x, y, w, h) in faces:
            gray_img = cv2.rectangle(gray_img, (x, y), (x + w, y + h), (255, 0, 0), 2)
            f = cv2.resize(gray_img[y:y + h, x:x + w], (200, 200))
            params = model.predict(f)
            print(params)
            if params[0] == 91 and params[1] <= 5000:
                #男主
                cv2.imwrite('image5/male_' + str(faceIndex) + '.pgm', f)
            elif params[0] == 9 and params[1] <= 5000:
                # 女主
                cv2.imwrite('image5/female_' + str(faceIndex) + '.pgm', f)
            faceIndex += 1

        cv2.imshow('Video', gray_img)
        cv2.waitKey(1000 / int(fps))
        success, frame = videoCapture.read()

    cv2.destroyWindow('Video')
    videoCapture.release()

faceRec()