目录

前言

一、目标检测技术

二、样本采集工作原理

三、创建自己的级联分类器

Step1:准备好样本图像

Step2:环境配置(OpenCV win10)

Step3:设置路径

Step4:实现样本数据采集 

Step5:实现样本数据训练

Step6:生成级联分类器文件 

四、案例实现

Step1:灰度处理

Step2:二次压缩

Step3:直方图均衡化

Step4:标定、框选目标

💡案例完整代码

五、总结 


前言

本文继续以车辆识别为目标,继续改进方法,以此提高车辆识别进准度,核心的内容包括:OpenCV级联分类器概念创建自己的级联分类器以及使用级联分类器对车流进行识别 

一、目标检测技术

目前常用实用性目标检测与跟踪的方法有以下两种:

  • 帧差法
  • 识别原理:基于前后两帧图像之间的差异进行对比,获取图像画面中正在运动的物体从而达到目标检测
  • 缺点:画面中所有运动中物体都能识别
  • 举个例子:我们的目标是识别运动的车辆,但是,在画面中镜头晃动,大风吹过树叶飘动也会被计算在甄别范围内,这就会导致甄别物出现错误
  • 级联分类器
  • 那么,有没有更好的方式能降低我们甄别目标出现错误的概率呢?
  • 这边就可以使用我们的级联分类器了,如果我们的识别目标是车辆,那就需要把车辆相关的信息全部统计出来,比如:车辆颜色车辆形状车辆大小等信息,将他们存储在一个文件中,以提高甄别准确度,这个文件就是级联分类器
  • 级联分类器也是机器视觉和机器学习中非常重要的一个环节

二、样本采集工作原理

  • 分析一 

使用基于Haar特征的级联分类器的对象检测,这是一种基于机器学习的方法,其中从许多正负图像中训练级联函数,然后,用于检测其他图像中的对象,在这里,我将进行人脸检测举例说明,最初,该算法需要大量正图像(面部图像)和负图像(无面部图像)来训练分类器, 然后,我们需要从中提取特征,为此,使用下图所示的Haar功能, 它们就像我们的卷积核一样,每个特征都是通过从黑色矩形下的像素总和中减去白色矩形下的像素总和而获得的单个值

opencv级联分类器工具下载 opencv分类器物体识别_opencv级联分类器工具下载

  • 分析二

现在,每个内核的所有可能大小和位置都用于计算许多功能,试想一下它产生多少计算?即使是一个24x24的窗口也会产生超过160000个特征,这对于每个特征计算,我们需要找到白色和黑色矩形下的像素总和,为了解决这个问题,引入了整体图像,无论你的图像有多大,它都会将给定像素的计算减少到仅涉及四个像素的操作,它使事情变得更快, 但是,在我们计算的所有这些特征中,大多数都不相关,例如,下图,第一行显示了两个良好的特征,选择的第一个特征似乎着眼于眼睛区域通常比鼻子和脸颊区域更暗的性质,选择的第二个特征依赖于眼睛比鼻梁更黑的属性,但是,将相同的窗口应用于脸颊或其他任何地方都是无关紧要的,那么,我们将如何从16万多个功能中选择最佳特征?

opencv级联分类器工具下载 opencv分类器物体识别_opencv_02

我们将所有特征应用于所有训练图像,对于每个特征,它会找到最佳的阈值,该阈值会将人脸分为正面和负面,显然,会出现错误或分类错误,我们选择错误率最低的特征,这意味着它们是对人脸和非人脸图像进行最准确分类的特征,此过程并非如此简单,在开始时,每个图像的权重均相等,在每次分类后,错误分类的图像的权重都会增加,然后执行相同的过程,将计算新的错误率,还要计算新的权重,继续进行此过程,直到达到所需的精度或错误率或找到所需的功能数量为止, 最终分类器是这些弱分类器的加权和,之所以称为弱分类,是因为仅凭它不能对图像进行分类,而是与其他分类一起形成强分类器,甚至200个功能都可以提供95%的准确度检测

三、创建自己的级联分类器

Step1:准备好样本图像

  • 正样本数据采集(我们需要检识别的目标图片,例如:车辆)

opencv级联分类器工具下载 opencv分类器物体识别_opencv级联分类器工具下载_03

  • 负样本数据采集(非检测物的图片)

我们可以在生活中观察到,大马路上除了车辆,还存在诸多的干扰,比如说:行人、斑马线、交通设别(路障、护栏、红绿灯、路灯等等),以及上文所说的风吹动,树叶飘动等等

opencv级联分类器工具下载 opencv分类器物体识别_opencv_04

opencv级联分类器工具下载 opencv分类器物体识别_目标检测_05

Step2:环境配置(OpenCV win10)

  • 下载OpenCV win10系统安装包
  • 从安装包中的opencv\build\x64\vc15\bin 找到

        -  opencv_createsamples.exe

        -  opencv_traincascade.exe

        -  opencv_world342.dll

  • 将以上文件拷贝到正负样本文件路径下

opencv级联分类器工具下载 opencv分类器物体识别_opencv_06

Step3:设置路径

  • 创建正负样本的图像路径的 .txt文件

opencv级联分类器工具下载 opencv分类器物体识别_计算机视觉_07

opencv级联分类器工具下载 opencv分类器物体识别_opencv级联分类器工具下载_08

Step4:实现样本数据采集 

  • 调用opencv中opencv_createsamples.exe实现样本数据采集

通过命令行执行命令进行样本采集生成car_samples.vec正样本矢量集文件,命令行如下:

opencv_createsamples.exe -info car_list.txt -vec car_samples.vec  -num 80 -w 33 -h 33


  • - info字段填写正样本描述文件
  • - vec用于保存制作的正样本
  • - num制定正样本的数目
  •  - w和-h分别指定正样本的宽和高
  • 在windows下终端运行,如下图所示: 

opencv级联分类器工具下载 opencv分类器物体识别_opencv_09

Step5:实现样本数据训练

  • 调用opencv中opencv_traincascade.exe对样本进行训练 

通过命令行执行命令进行训练生成,命令行如下:

opencv_traincascade.exe -data data -vec car_samples.vec  -bg ng_data.txt  -numPos 80 -numNeg 240 -numStages 7 -w 33 -h 33 -minHitRate 0.995 -maxFalseAlarmRate 0.45 -mode ALL


  • -data:指定保存训练结果的文件夹
  • -vec:指定正样本集; -bg:指定负样本的描述文件夹 
  • -numPos:指定每一级参与训练的正样本的数目(要小于正样本总数)
  • -numNeg:指定每一级参与训练的负样本的数目(可以大于负样本图片的总数)
  • -numStage:训练的级数
  • -w:正样本的宽
  • -h:正样本的高 
  • -minHitRate:每一级需要达到的命中率(一般取值0.95-0.995)
  • -maxFalseAlarmRate:每一级所允许的最大误检率 
  • -mode:使用Haar-like特征时使用,可选BASIC、CORE或者ALL 


  • 另外,还可指定以下字段:
  • -featureType:可选HAAR或LBP,默认为HAAR;
  • 在windows下终端运行,进行训练,每一层训练都会有显示,如下图所示:  
  • PS:训练时需要考虑一下自身电脑配置,小心电脑跑出问题!

opencv级联分类器工具下载 opencv分类器物体识别_目标检测_10

Step6:生成级联分类器文件 

  • 生成文件,如下所示: 

opencv级联分类器工具下载 opencv分类器物体识别_opencv_11

四、案例实现

我们将继续编写代码通过C++ 编写 OpenCV 在Qt上来展现出我们的案例效果

opencv级联分类器工具下载 opencv分类器物体识别_c++_12

Step1:灰度处理

  • 将我们的色彩通道缩小,简化矩阵,提高运算速度 
//【灰度处理】
    Mat gray;
    cvtColor(frame,gray,CV_RGB2GRAY);

Step2:二次压缩

  • 灰度处理还不够,级联分类器比帧差法还更加慢,因此,再将灰度图大小压缩一半左右 
//一次还不够,级联分类器比帧差法还更加慢
    //【二次压缩】因此,再将灰度图大小压缩一半左右
    Mat smalling(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1);
    resize(gray,smalling,smalling.size(),0,0,INTER_LINEAR);

Step3:直方图均衡化

  • 将缩小一半的灰度图进行均值化使其更加黑白分明
equalizeHist(smalling,smalling);
    //imshow("smalling",smalling);

Step4:标定、框选目标

  • 调用级联分类器进行模型匹配并框选目标
//调用级联分类器进行模型匹配并框出内容
    vector<Rect>cars;
    //【参数说明】 待检测的图片帧  被检测物体的矩形向量容器  每次搜索减小的图像比例  检测目标周围相邻矩形的最小个数(此处设为2个) 类型 目标区域的大小尺寸
    cascade.detectMultiScale(smalling,cars,1.05,5,0|CV_HAAR_SCALE_IMAGE,Size(25,25));

    vector<Rect>::const_iterator iter;
    //【绘制标记框】注意,标记要画在原帧上,要讲方框的大小和帧坐标扩大,因为是根据灰度图识别的,灰度图被缩小了
    for(iter=cars.begin();iter!=cars.end();iter++)
    {
        rectangle(frame,
                  cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),
                  cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),
                  Scalar(0,255,0),2,8
                    );
    }
    imshow("frame",frame);
}

💡案例完整代码

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void datectCarDaw(Mat &frame,CascadeClassifier cascade,double scale)
{
    //【灰度处理】
    Mat gray;
    cvtColor(frame,gray,CV_RGB2GRAY);

    //一次还不够,级联分类器比帧差法还更加慢
    //【二次压缩】因此,再将灰度图大小压缩一半左右
    Mat smalling(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1);
    resize(gray,smalling,smalling.size(),0,0,INTER_LINEAR);

    //【直方图均衡化】将缩小一半的灰度图进行均值化使其更加黑白分明
    equalizeHist(smalling,smalling);
    //imshow("smalling",smalling);

    //调用级联分类器进行模型匹配并框出内容
    vector<Rect>cars;
    //【参数说明】 待检测的图片帧  被检测物体的矩形向量容器  每次搜索减小的图像比例  检测目标周围相邻矩形的最小个数(此处设为2个) 类型 目标区域的大小尺寸
    cascade.detectMultiScale(smalling,cars,1.05,5,0|CV_HAAR_SCALE_IMAGE,Size(25,25));

    vector<Rect>::const_iterator iter;
    //【绘制标记框】注意,标记要画在原帧上,要讲方框的大小和帧坐标扩大,因为是根据灰度图识别的,灰度图被缩小了
    for(iter=cars.begin();iter!=cars.end();iter++)
    {
        rectangle(frame,
                  cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),
                  cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),
                  Scalar(0,255,0),2,8
                    );
    }
    imshow("frame",frame);
}

int main(int argc, char *argv[])
{
    //级联分类器(模型)
    CascadeClassifier cascade;
    cascade.load("C:/Users/86177/Desktop/cars-face/cars.xml");//读取级联分类器

    Mat frame;
    VideoCapture cap("C:/Users/86177/Desktop/image/test.mp4");//视频路径
    while (cap.read(frame))
    {
        imshow("video",frame);//将读到的帧显示出来
        datectCarDaw(frame,cascade,2);//将读到的帧传入函数用作识别
        waitKey(5);//延时5ms
    }
    return 0;
}
  • 实现效果,总体来说还是相当不错的,相较于帧差法而言,精度提高很多
  • 级联分类器实现,如下图所示:
  • 为了方便对比,再次使用帧差法实现,可以看到移动的电动车都被识别进去了,如下图所示:
  • 帧差法实现,如下图所示:

五、总结 

  • 本文核心的内容包括:OpenCV级联分类器概念创建自己的级联分类器以及使用级联分类器对车流进行识别
  • 以车辆识别为例子,讲解了级联分类器相较于帧差法的实战案例,实际效果还是不错的
  • 车辆识别在我们日常生活中非常常见,是一个很经典的案例,本案例采用了较高级别的级联分类器,可以实现对车辆较高精度的识别,车辆识别还是需要更大更复杂的样本量才能在复杂环境下对车辆进行精确的识别,总体而言效果相较于帧差法更加精准了!新能源汽车的发展,与车辆识别同理的无人驾驶技术,在针对实际路况的识别难度还是较大的,各大巨头还在努力研究相关技术,相信在不久的将来,我们能看到无人驾驶技术达到较为成熟的一天!