本章以人脸检测为例, 定义具体可跟踪对象类型的数据文件, 即Haar级联分类器, 通过对比分析相邻图像区域来判断给定图像或子图像与已知对象是都匹配.
同时, 本章会将多个Haar级联分类器构成一个层次结构, 即一个分类器能识别整体区域, 而其它分类器可识别更小的区域.
至于为什么以老掉牙的人脸检测为例子, 是因为现阶段人脸检测有关的技术太成熟了…
还是以人脸为例子, 假设有一段监控视频, 拍到了一个人正脸步行经过的画面. 这样的情景下我们肯定是希望对人进行跟踪, 对人脸进行匹配和分类(比如通过人脸来识别这个人是谁). 那么我们必不可能局限于这个人的整体框架, 而必须将注意力放在人脸的细节部分, 比如脸上的疤痕, 瞳孔的颜色等等. 但是通常来说, 很难保证一段视频不受灯光, 视角, 视距, 摄像头抖动以及任何其它噪声的影响, 这些比可避免的影响容易使得图像出现轻微的不稳定.
因此需要解决的问题是, 如何提取图像的细节特征并生成稳定的分类结果?
Haar实际上是运用了boosting算法中的Adaboost算法, 而Haar分类器利用Adaboost算法构建一个强分类器进行级联,而在底层特征抽取上采用的是高校的矩形特征以及积分图方法
可以理解为: H a a r 分 类 器 = 类 H a a r 特 征 + 积 分 图 法 + A d a b o o s t 算 法 + 级 联 \large Haar分类器=类Haar特征+积分图法+Adaboost算法+级联 Haar分类器=类Haar特征+积分图法+Adaboost算法+级联
其中类Haar特征是一种用于实现实时人脸跟踪的特征, 最早于2001年由Paul Viola等人提出. 每个类Haar特征都描述了相邻图像区域的对比模式, 比如边和角点.
Haar分类器主要步骤如下:
需要注意的是, OpenCV的Haar级联不具有旋转不变性的特点, Haar级联不认为倒置的人脸图像和直立的人脸图像一样.
在OpenCV3.4.2.16的源码中, 存在一个data文件夹, 里面包含了所有OpenCV的人脸检测的XML文件
通过文件名我们可以得知, 这些文件分别用于检测眼睛, 微笑, 鼻子等细节. 需要注意的是, 这些文件需要正面并且直立的人脸图像, 但是一张输入图像可以包含多张人脸
这里我们给python项目再创建一个新文件夹haarcascades, 然后在该文件夹下创建名为cascades的子文件夹, 并将上述的所有XML文件复制进来.
没有需要特别事先说明的注意点, 这里直接给出代码, 往下结合代码进行详细的说明.
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 weights;
. vector levels;
. vector 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级联和如何运用级联去检测目标. 检测眼睛, 鼻子等任何目标都是同样的逻辑:
"""
opencv
"""
# 提取类Haar特征
cv.CascadeClassifier()
# 检测人脸, 注意该函数只接受灰度图
face_cascade.detectMultiScale()
本章代码非常少, 就是静态人脸检测的所有代码, 已经在上面贴出了.