Opencv Python开发 第四章 级联和人脸检测

章节简介

本章以人脸检测为例, 定义具体可跟踪对象类型的数据文件, 即Haar级联分类器, 通过对比分析相邻图像区域来判断给定图像或子图像与已知对象是都匹配.

同时, 本章会将多个Haar级联分类器构成一个层次结构, 即一个分类器能识别整体区域, 而其它分类器可识别更小的区域.

至于为什么以老掉牙的人脸检测为例子, 是因为现阶段人脸检测有关的技术太成熟了…

往期博客

PythonOpencv开发 Python3.6.7+Opencv3.4.2.16环境配置

PythonOpenCV开发 前言

OpenCV Python开发 第一章 图像处理基础

OpenCV Python开发 第一章课后 自定义实现API

OpenCV Python开发 第二章 深度估计与分割

Opencv Python开发 第三章 图像检索

Haar级联的概念

还是以人脸为例子, 假设有一段监控视频, 拍到了一个人正脸步行经过的画面. 这样的情景下我们肯定是希望对人进行跟踪, 对人脸进行匹配和分类(比如通过人脸来识别这个人是谁). 那么我们必不可能局限于这个人的整体框架, 而必须将注意力放在人脸的细节部分, 比如脸上的疤痕, 瞳孔的颜色等等. 但是通常来说, 很难保证一段视频不受灯光, 视角, 视距, 摄像头抖动以及任何其它噪声的影响, 这些比可避免的影响容易使得图像出现轻微的不稳定.

因此需要解决的问题是, 如何提取图像的细节特征并生成稳定的分类结果?

Haar实际上是运用了boosting算法中的Adaboost算法, 而Haar分类器利用Adaboost算法构建一个强分类器进行级联,而在底层特征抽取上采用的是高校的矩形特征以及积分图方法

可以理解为: Haar=Haar++Adaboost+\large Haar分类器=类Haar特征+积分图法+Adaboost算法+级联

其中类Haar特征是一种用于实现实时人脸跟踪的特征, 最早于2001年由Paul Viola等人提出. 每个类Haar特征都描述了相邻图像区域的对比模式, 比如边和角点.

Haar分类器主要步骤如下:

  1. 提取类Haar特征;
  2. 利用积分图法对类Haar特征提取进行加速;
  3. 使用Adaboost算法训练强分类器,区分出人脸和非人脸;
  4. 使用筛选式级联把强的分类器级联在一起,从而提高检测准确度。

需要注意的是, OpenCV的Haar级联不具有旋转不变性的特点, Haar级联不认为倒置的人脸图像和直立的人脸图像一样.

获取Haar级联数据

在OpenCV3.4.2.16的源码中, 存在一个data文件夹, 里面包含了所有OpenCV的人脸检测的XML文件

在这里插入图片描述

通过文件名我们可以得知, 这些文件分别用于检测眼睛, 微笑, 鼻子等细节. 需要注意的是, 这些文件需要正面并且直立的人脸图像, 但是一张输入图像可以包含多张人脸

这里我们给python项目再创建一个新文件夹haarcascades, 然后在该文件夹下创建名为cascades的子文件夹, 并将上述的所有XML文件复制进来.

使用OpenCV进行人脸检测

静态图像中的人脸检测

没有需要特别事先说明的注意点, 这里直接给出代码, 往下结合代码进行详细的说明.

def detectFaces(img):
    """
    检测静态图像中的人脸并显示
    :param img: 待检测的图像
    """
    # 首先加载Haar分类器
    face_cascade = cv.CascadeClassifier('..\\haarcascades\\cascades\\haarcascade_frontalface_default.xml')
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 这一行直接进行检测, 返回的结果是一个个矩形, 每一个矩形代表了一个人脸
    faces = face_cascade.detectMultiScale(img_gray, 1.3, 5)
    for (x, y, w, h) in faces:
        img = cv.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
    plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
    plt.title('人脸检测')
    plt.xticks([]), plt.yticks([])
    plt.show()

主要的流程其实只有三个部分:

  • 首先利用cv.CascadeClassifier()来加载一个Haar分类器, 返回一个类对象
  • 其次利用上一步得到的类对象, 调用其成员函数.detectMultiScale()来进行人脸检测, 每一张人脸返回一个矩形
  • 最后在原图上标出这些矩形即可.

查看opencv-python的源码可以看到, 函数detectMultiScale()有3个重载, 经过翻译后, 分别是:

    def detectMultiScale(self, image, scaleFactor=None, minNeighbors=None, flags=None, minSize=None, maxSize=None): 
        """
        detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) -> objects
        .   @brief 检测输入图像中的不同尺度的目标, 被检测到的目标将作为一个矩形列表被返回
        .
        .       @param image 输入图像,包含了待检测的目标, 像素值类型CV_8U, 即0~255的整数.
        .       @param objects 存放检测结果的列表, 每个元素都是一个矩形, 需要注意的是, 这个矩形有可能超出原图像的范围
        .       @param scaleFactor 每次图像缩小的比例
        .       @param minNeighbors 匹配成功所需要的周围矩形框的数目,每一个特征匹配到的区域都是一个矩形框,只有多个矩形框同时存在的时候,才认为是匹配成功,默认值是3, 也就是人脸检测时常用的值
        .       @param flags 可以取如下这些值:
        .           1, 利用canny边缘检测来排除一些边缘很少或者很多的图像区域
        .           2, 正常比例检测
        .           4, 只检测最大的物体
        .           8, 粗略的检测
        .       @param minSize 最小目标大小, 小于这个大小的目标被丢弃.
        .       @param maxSize 最大目标大小, 大于这个大小的目标被丢弃. 如果minSize == maxSize, 那么只检测大小正好为minSize的目标, 其它大小的目标一律被丢弃.
        """
        pass

    def detectMultiScale2(self, image, scaleFactor=None, minNeighbors=None, flags=None, minSize=None, maxSize=None):
        """
        detectMultiScale2(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) -> objects, numDetections
        .   @overload
        .
        .       @param numDetections 对应目标的检测数目
        .	    其余参数同detectMultiScale()
        .
        """
        pass

    def detectMultiScale3(self, image, scaleFactor=None, minNeighbors=None, flags=None, minSize=None, maxSize=None, outputRejectLevels=None): 
        """
        detectMultiScale3(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize[, outputRejectLevels]]]]]]) -> objects, rejectLevels, levelWeights
        .   @overload
        .       这个重载会同时返回rejectLevels, levelWeights, (需要设置outputRejectLevels=True).
        .	    以下为c++版本的一个示例
        .       @code
        .       Mat img;
        .       vector<double> weights;
        .       vector<int> levels;
        .       vector<Rect> detections;
        .       CascadeClassifier model("/path/to/your/model.xml");
        .       model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true);
        .       cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl;
        .       @endcode
        """
        pass

可以看到, 三个函数的整体是一样的, 唯一的区别就是在返回值.

视频中的人脸检测

熟悉的话, 这里其实没什么好说的, 将静态检测的代码封装成函数, 一帧一帧读取视频调用就可以了. 大致就是这样:

def staticDetect(img):
    pass

video = cv.VideoCapture(0)
ret, frame = video.read()	# 丢弃第一帧
while ret:
    ret, frame = video.read()
    staticDetect(frame)

章节总结

这章比较简短, 以人脸检测为例, 介绍了Haar级联和如何运用级联去检测目标. 检测眼睛, 鼻子等任何目标都是同样的逻辑:

  • 读取类Haar特征
  • 检测
  • 标注

新的API:

"""
opencv
"""
# 提取类Haar特征
cv.CascadeClassifier()
# 检测人脸, 注意该函数只接受灰度图
face_cascade.detectMultiScale()

章节代码

本章代码非常少, 就是静态人脸检测的所有代码, 已经在上面贴出了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章