【youcans 的 OpenCV 学习课】23. 人脸检测:Haar 级联检测器

专栏地址:『youcans 的图像处理学习课』
文章目录:『youcans 的图像处理学习课 - 总目录』



4. Haar 级联分类器

基于 Haar 特征的级联分类器是 Paul Viola 在论文”Rapid Object Detection using a Boosted Cascade of Simple Features”中提出的一种目标检测方法。

Haar 级联分类器在每一级的节点中,使用 AdaBoost 算法学习一个高检测率低拒绝率的多层分类器。其特点是:

  1. 使用 Haar-like 输入特征,对矩形图像区域的和或者差进行阈值化。

  2. 使用积分图像计算 45°旋转区域的像素和,加速 Haar-like 输入特征的计算。

  3. 使用统计 Boosting 来创建二分类(人脸/非人脸)的分类器节点(高通过率,低拒绝率)。

  4. 将弱分类器并联组合起来,构成筛选式级联分类器。

在这里插入图片描述

各级的 Boosting 分类器对于有人脸的检测窗口都能通过,同时拒绝一小部分非人脸的检测窗口,并将通过的检测窗口传给下一个分类器。依次类推,最后一个分类器将几乎所有非人脸的检测窗口都拒绝掉,只剩下有人脸的检测窗口。因此,只要检测窗口区域通过了所有各级 Boosting 分类器,则认为检测窗口中有人脸。

在实际应用中输入图片的尺寸较大,需要进行多区域、多尺度的检测。多区域是要遍历图片的不同位置,多尺度是为了检测图片中不同大小的人脸。

在 Haar 级联分类人脸检测器中,主要利用了人脸的结构化特征:

  • 与脸颊相比,眼部颜色较深
  • 与眼睛相比,鼻梁区域较为明亮
  • 眼睛、嘴巴、鼻子的位置较为固定

通过这 5 个矩形区域的明暗关系,就可以形成对人脸的各个部分的判别特征。例如在下图中,第一个特征检测眼部和上脸颊之间的强度差异,第二个特征检测双眼的间距。

在这里插入图片描述

Haar 人脸检测训练很高,但对侧脸的检测性能较差。


5. Haar 人脸/人眼检测器

5.1 OpenCV 中的级联分类器

OpenCV 中定义了级联分类器类 cv::CascadeClassifier。在 Python 语言中,使用接口函数 cv.CascadeClassifier() 从文件创建分类器。

cv.CascadeClassifier(filename)
cv.CascadeClassifier.load(filename[, ]) → retval
cv.CascadeClassifier.read(node[, ]) → retval
cv.CascadeClassifier.empty([, ]) → retval

cv.CascadeClassifier.convert(oldcascade, newcascade[, ]) → retval
cv.CascadeClassifier.detectMultiScale(image[, scaleFactor=1.1, minNeighbors=3, flags=0, minSize=Size(), maxSize=Size()]) → objects

成员函数 cv.CascadeClassifier.load() 从文件加载级联分类器模型,成员函数 cv.CascadeClassifier.read() 从FileStorage 节点读取分类器,成员函数 cv.CascadeClassifier.empty()检测分类器是否加载成功。

成员函数 cv.CascadeClassifier.detectMultiScale() 用于执行对图像进行目标检测,成员函数 cv.CascadeClassifier.convert()


参数说明:

  • filename:加载分类器模型的文件路径和名称,字符串。
  • image:待检测的输入图像,CV_8U 格式。
  • scaleFactor:搜索窗口的缩放比例,默认值为1.1。
  • minNeighbors:表示构成检测目标的相邻矩形的最小个数,默认值为 3。
  • flags:版本兼容标志,默认值为 0。
  • minSize:检测目标的最小尺寸,元组 (h,w)。
  • maxSize:检测目标的最大尺寸,元组 (h,w)。
  • objects:返回值,检测目标的矩形边界框 ,是形如 (N,4) 的Numpy数组。

参数说明:

(1)返回值 objects 是形如 (N,4) 的Numpy数组,每行有 4个元素 (x, y, width, height) 表示矩形框的左上顶点坐标 (x,y) 和宽度 width、高度 height。

(2)加载的级联分类器模型文件,扩展名为 .xml。


5.2 Haar 级联检测器的预训练模型

OpenCV 提供了级联分类器的训练方法,也可以直接下载预训练模型后使用 load() 方法加载模型。

OpenCV 提供的 Haar 级联检测器的预训练模型位于 OpenCV 安装包的 \data\haarcascades文件夹,或者从 【GitHub】opencv/data at 4.x 下载。

在这里插入图片描述


OpenCV 提供的 haar 级联检测器的预训练模型包括:

haarcascade_eye.xml, 眼睛
haarcascade_eye_tree_eyeglasses.xml, 戴眼镜的眼睛
haarcascade_frontalcatface.xml, 正面猫脸
haarcascade_frontalcatface_extended.xml, 正面猫脸
haarcascade_frontalface_alt.xml, 正面人脸
haarcascade_frontalface_alt2.xml, 正面人脸
haarcascade_frontalface_alt_tree.xml, 正面人脸
haarcascade_frontalface_default.xml, 正面人脸
haarcascade_fullbody.xml, 人体
haarcascade_lefteye_2splits.xml, 左眼
haarcascade_license_plate_rus_16stages.xml, 
haarcascade_lowerbody.xml, 
haarcascade_profileface.xml, 
haarcascade_righteye_2splits.xml, 右眼
haarcascade_russian_plate_number.xml, 
haarcascade_smile.xml, 笑脸
haarcascade_upperbody.xml, 上身

5.3 人脸检测/人眼检测的实现步骤

使用 Haar 级联检测器检测图片中的人脸的步骤:

(1)创建一个 CascadeClassifier 级联分类器对象,使用 load() 方法从 .xml 文件加载级联分类器模型。

(2)读取待检测的图片。

(3)使用 detectMultiScale() 方法检测图片,返回检测到的面部或眼睛的边界矩形。

(4)将检测到的边界矩形绘制到检测图片上。


例程 17_6:使用 Haar 级联检测器检测人脸

使用 Haar 级联检测器检测图片中的人脸。

import numpy as np
import cv2 as cv

if __name__ == '__main__':
    # (6) 使用 Haar 级联分类器 预训练模型 检测人脸
    # 读取待检测的图片
    img = cv.imread("../images/img_group_02.jpg", flags=1)
    print(img.shape)

    # 加载 Haar 级联分类器 预训练模型
    model_path = "../data/haarcascade_frontalface_alt2.xml"
    face_detector = cv.CascadeClassifier(model_path)  # <class 'cv2.CascadeClassifier'>
    # 使用级联分类器检测人脸
    faces = face_detector.detectMultiScale(img, scaleFactor=1.1, minNeighbors=1,
                                           minSize=(30, 30), maxSize=(300, 300))
    print(faces.shape)  # (17, 4)
    print(faces[0])  # (x, y, width, height)

    # 绘制人脸检测框
    for x, y, width, height in faces:
        cv.rectangle(img, (x, y), (x + width, y + height), (0, 0, 255), 2, cv.LINE_8, 0)
    # 显示图片
    cv.imshow("faces", img)
    cv.waitKey(0)
    cv.destroyAllWindows()

在这里插入图片描述

Haar 级联检测器可以对人脸检出率较高,但有一定的错检率,即有些检测结果中并不是人脸。


例程 17_7:使用 Haar 级联检测器检测人眼

人眼检测的方法与人脸检测方法相同,只是使用了不同的预训练模型,例如 haarcascade_eye.xml。

由于眼睛比人脸的尺寸小,因此减小了检测函数 detectMultiScale() 的参数 minSize=(20, 20), maxSize。

import numpy as np
import cv2 as cv

if __name__ == '__main__':
    # (7) 使用 Haar 级联分类器 预训练模型 检测人眼
    # 读取待检测的图片
    img = cv.imread("../images/img_group_01.jpg", flags=1)
    print(img.shape)

    # 加载 Haar 级联分类器 预训练模型
    model_path = "../data/haarcascade_eye.xml"
    eye_detector = cv.CascadeClassifier(model_path)  # <class 'cv2.CascadeClassifier'>
    # 使用级联分类器检测人脸
    eyes = eye_detector.detectMultiScale(img, scaleFactor=1.1, minNeighbors=1,
                                           minSize=(20, 20), maxSize=(100, 100))
    print(eyes.shape)  # (51, 4)
    print(eyes[0])  # (x, y, width, height)

    # 绘制人脸检测框
    for x, y, width, height in eyes:
        cv.rectangle(img, (x, y), (x + width, y + height), (0, 0, 255), 2, cv.LINE_8, 0)
    # 显示图片
    cv.imshow("Haar_Cascade", img)
    # cv.imwrite("../images/imgSave3.png", img)
    cv.waitKey(0)
    cv.destroyAllWindows()

在这里插入图片描述


例程 17_8:使用 Haar 级联检测器检测人脸和人眼

为了提高检测效率,可以先检测人脸,再在人类窗口内检测人眼,不仅可以提高检测效率,而且可以提高检测精度。

import numpy as np
import cv2 as cv

if __name__ == '__main__':
    # (8) 使用 Haar 级联分类器 预训练模型 检测人脸和人眼
    # 读取待检测的图片
    img = cv.imread("../images/img_group_01.jpg", flags=1)
    print(img.shape)

    # 加载 Haar 级联分类器 预训练模型
    face_path = "../data/haarcascade_frontalface_alt2.xml"  # 人脸检测器
    face_detector = cv.CascadeClassifier(face_path)  # <class 'cv2.CascadeClassifier'>
    eye_path = "../data/haarcascade_eye.xml"  # 人眼检测器
    eye_detector = cv.CascadeClassifier(eye_path)  # <class 'cv2.CascadeClassifier'>
    # 使用级联分类器检测人脸
    faces = face_detector.detectMultiScale(img, scaleFactor=1.1, minNeighbors=2,
                                           minSize=(30, 30), maxSize=(300, 300))
    print(faces.shape)  # (15, 4)

    # 绘制人脸检测框
    for x, y, width, height in faces:
        cv.rectangle(img, (x, y), (x + width, y + height), (0, 0, 255), 2, cv.LINE_8, 0)
        # 在人脸区域内检测人眼
        roi = img[y:y + height, x:x + width]  # 提取人脸
        # 检测人眼
        eyes = eye_detector.detectMultiScale(roi, scaleFactor=1.1, minNeighbors=1,
                                             minSize=(20, 20), maxSize=(80, 80))
        # 绘制人眼
        for ex, ey, ew, eh in eyes:
            cv.rectangle(img, (x+ex, y+ey), (x+ex+ew, y+ey+eh), (255, 0, 0), 2)

    # 显示图片
    cv.imshow("Haar_Cascade", img)
    # cv.imwrite("../images/imgSave4.png", img)
    cv.waitKey(0)
    cv.destroyAllWindows()

在这里插入图片描述


例程 17_9:使用 Haar 级联分类器进行视频人脸检测

首先启动摄像头或读取视频文件,读取视频帧。然后将视频帧转换为灰度图像,使用 cascade级联检测器检测人脸。对于检测到的人脸,都加上一个矩形框。

import numpy as np
import cv2 as cv

if __name__ == '__main__':
    # (9) 使用 Haar 级联分类器检测视频流
    # 加载 Haar 级联分类器 预训练模型
    face_path = "../data/haarcascade_frontalface_default.xml"  # 人脸检测器
    face_detector = cv.CascadeClassifier(face_path)  # <class 'cv2.CascadeClassifier'>

    # 创建视频读取/捕获对象
    vedioRead = "../images/Megamind.avi"  # 读取视频文件的路径
    videoCap = cv.VideoCapture(vedioRead)  # 实例化 VideoCapture 类

    frameNum = 0  # 视频帧数初值
    # ret, frame = videoCap.read()  # 读取一帧图像
    # print("frame: ", frame.shape)  # (528, 720, 3)
    timef = 40  # 设置抽帧间隔
    plt.figure(figsize=(9, 5.6))
    while videoCap.isOpened():  # 检查视频捕获是否成功
        ret, frame = videoCap.read()  # 读取一帧图像, (528, 720, 3)
        if ret is True:
            frameNum += 1  # 读取视频的帧数
            gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)  # 灰度处理
            # 使用级联分类器检测人脸
            faces = face_detector.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=2,
                                                   minSize=(50, 50), maxSize=(300, 300))
            # 绘制人脸检测框
            for x, y, width, height in faces:
                cv.rectangle(frame, (x, y), (x + width, y + height), (0, 0, 255), 2, cv.LINE_8, 0)
            cv.imshow('frame', frame)  # 目标识别视频
            if (frameNum%timef==0 and 1<=frameNum//timef<= 6):  # 判断抽帧条件
                plt.subplot(2, 3, frameNum//timef), plt.axis('off')
                plt.title("{}. Face detector (f={})".format(frameNum//timef, frameNum))
                plt.imshow(cv.cvtColor(frame, cv.COLOR_BGR2RGB))
            if cv.waitKey(10)&0xFF == 27:  # 按 'Esc' 退出
                break
        else:
            print("Can't receive frame at frameNum {}.".format(frameNum))
            break

    print("frameNum: ", frameNum)
    # 释放资源
    videoCap.release()  # 关闭读取视频文件
    cv.destroyAllWindows()  # 关闭显示窗口
    plt.tight_layout()
    plt.show()

在这里插入图片描述



版权声明:

youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/130423104)
Copyright 2022 youcans, XUPT
欢迎关注 『youcans 的 OpenCV 学习课』 系列,持续更新

猜你喜欢

转载自blog.csdn.net/youcans/article/details/130423104