引言
提到opencv,就不得不提其图像识别能力,最近旷世开源的YoloX项目兴起,作为目前Yolo系列中的最强者,本人对其也很感兴趣,但是完全没用机器学习和计算机视觉的基础,知其然,不知其所以然,于是想稍稍入坑一下opencv图像识别,了解一下相关算法,(说不定以后毕设会用到呢)。了解之后选取了人脸识别中Haar特征和Adaboost分类器作为学习对象。以此记录自己的学习成果。
一、opencv识别基本流程
1.1 XML模型调用
从opencv路径中找到自带的训练好的分类器haarcascade_eye.xml
、haarcascade_frontalface_alt_tree.xml
import cv2 as cv
def face_detection(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) # 图像灰度化
face_detector = cv.CascadeClassifier("haarcascade_frontalface_alt_tree.xml") # 调用分类器加载脸部模型
eyes_detector = cv.CascadeClassifier("haarcascade_eye.xml") # 调用分类器加载眼部模型
faces = face_detector.detectMultiScale(gray, 1.01, 5) # 扫描图片,返回结果坐标
"""
detectMultiScale(self, image, scaleFactor=None, minNeighbors=None, minSize=None, maxSize=None)
image:输入的图像
scaleFactor:比例因子,图像尺寸每次减少的比例,要大于1,需自己调整
minNeighbors:最小附近像素值
"""
for x, y, w, h in faces:
img = cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2) # 在面部画矩形
roi_gray = gray[y:y+h, x:x+w] # 获取面部区域灰度矩阵
roi_color = img[y:y+h, x:x+w]
eyes = eyes_detector.detectMultiScale(roi_gray, 1.3, 5) # 继续检测眼部
for ex, ey, ew, eh in eyes:
cv.rectangle(roi_color, (ex, ey), (ex + ew, ey + ey), (0, 255, 0), 2) # 在眼部画矩形
cv.imshow("result", image) # 显示
def main():
src = cv.imread("test.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
face_detection(src)
cv.waitKey(0)
cv.destroyAllWindows() # 关闭所有窗口
if __name__ == '__main__':
main()
1.2 运行结果:
面部和眼部都识别出来,效果不错。
二、XML模型训练
2.1 需要解决的问题
1、如何准确判断识别目标 ——> Haar-like特征
2、如何训练出可以识别的网络 ——> Adaboost级联分类器
2.2 Haar-like特征
Haar特征是用黑色和白色矩形组成的矩形,用于代表面部某个区域的明暗情况,其分为三类:边缘特征、线性特征、中心特征和对角线特征,组合成特征。
特征值计算方式为:白色矩形像素和减去黑色矩形像素和。
基础特征有:
Haar特征使用举例:
其中中间一幅表示眼睛区域的颜色比脸颊区域的颜色深,右边一幅表示鼻梁两侧比鼻梁的颜色要深。
矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数。因此很小的图像区域可以算出极大量的特征值,这对于计算机是个很大的负荷,于是需要使用积分图来降低运算难度。
2.3 积分图计算
积分图的目标是将一幅图所有从原点到目标点的矩形的像素和的值保存在内存中,需要计算矩形像素和时只需要简单加减法就能得到结果,速度稳定,可以大大提高运算效率。其计算方法是遍历出每个点到原点的矩形的像素和,或通过递推快速计算。
积分图是一个二维矩阵,令其为S。
初始化:
令p(-1,j)=0,其中p为该整列的像素和;
令S(-1,j)=0;
递推关系:
S(i,j)=S(i-1,j)+p(i,j);
p(i,j)=p(i,j-1)+f(i,j);
其中f(i,j)为该位置的像素值;
最终得到一个矩阵S用于计算。
例:对于该特征,计算其特征值,坐标分别为a、b、c、d、e、f。
公式:
Sum=S(e)+S(a)-S(b)-S(d)-S(f)-S(b)+S(e)+S(c)
因此,特征值的计算速度几乎是恒定的。
2.4 Adaboost级联分类器
Boost分类器原理:
Boosting算法的工作机制是首先从训练集用初始权重D(1)训练出一个弱学习器1,根据弱学习的学习误差率表现来更新训练样本的权重,使得之前弱学习器1学习误差率高的训练样本点的权重变高,使得这些误差率高的点在后面的弱学习器2中得到更多的重视。然后基于调整权重后的训练集来训练弱学习器2.,如此重复进行,直到弱学习器数达到事先指定的数目T,最终将这T个弱学习器通过集合策略进行整合,得到最终的强学习器。
Adaboost基本模型:
其中,只有图片通过了所有的检测才能输出,否则舍弃,这样做的好处是可以在某一阶段放弃计算错误数据,转而进行下一次计算,节省了相当多的时间。
2.5 级联分类器的训练
关于级联分类器的概率论与数理统计原理,由于本人还没有完全搞懂,暂且不表,详细算法见参考文献。
这里介绍一下opencv训练xml文件的流程。
所需文件:
正样本:识别目标的样本图片n份
负样本:完全不包含目标的样本3n份
opencv提供了训练用的源代码:traincascade
traincascade详细参数、使用说明见官网文档
生成训练样本代码:createsamples将样本封装为训练用数据集
如果是下载源代码后编译的opencv,文件夹中会自动生成相应的可执行文件。
训练流程:
三、参考文献
1、【YoloX源代码】
2、【opencv源代码】
3、【traincascade说明文档】
4、【opencv-CascadeClassifier分类器】
5、【Adaboost算法】
6、【人脸检测之Haar分类器】
7、宋俊芳,马浩轩,赵海莉,张李帅.基于Haar级联分类器和LBPH算法的人脸识别[J].软件,2021,42(04):45-47