OpenCV中级联分类器Cascade训练方法(Linux)

OpenCV中级联分类器Cascade训练方法,实现目标检测

  • 1.OpenCV中级联分类器简介
  • 2.准备分类器训练所需的官方工具
    • 1)opencv_createsamples
    • 2)opencv_traincascade
    • 3)opencv_performance
  • 3.准备训练数据(正样本、负样本)
    • 1)文件夹结构
    • 2)正负样本
  • 4.训练
    • 1)生成正样本描述文件
    • 2)生成负样本相对路径
    • 3)利用opencv_traincascade训练生成.xml
  • 5.检测代码(仅供参考)


1.OpenCV中级联分类器简介

  • OpenCV中可供训练的分类器有两个:
    • opencv_haartraining
      在其官方文档中提到opencv_haartraining已经是一个过时的应用
    • opencv_traincascade
    1. 该分类器是2.x版本中基于C++写的新版本的分类器。opencv_traincascade支持Haar和LBP。与Haar特征相比,LBP特征是整数特征,因此训练和检测过程都会比Haar特征快几倍。
    2. LBP和Haar特征用于检测的准确率,是依赖训练过程中的训练数据的质量和训练参数。

2.准备分类器训练所需的官方工具

  • 1)opencv_createsamples

    • 用于准备训练数据的正样本和测试样本。
    • opencv_createsamples可以生成支持opencv_haartraining和opencv_traincascade分类器的正样本数据。
    • 输出文件是以.vec为后缀的包含图像信息的二进制数据类型。
  • 2)opencv_traincascade

    • 用于训练cascade分类器,注意stage。
  • 3)opencv_performance

    • 可以用来评估分类器的质量。
    • 但仅对opencv_haartraining生成的分类器有效。

注意:所用文件位置:

  • 在已配好OpenCV环境的状态下(一般在编译目录下):
    - Linux:/usr/local/bin/ 或者 …/opencv-3.4.1/build/bin
    - Windows:…\opencv\build\x64\vc12\bin
    所需的两个训练工具,windows环境下为.exe

3.准备训练数据(正样本、负样本)

  • 1)文件夹结构

OpenCV中级联分类器Cascade训练方法(Linux)_第1张图片
classifier: 用于存放训练好的.xml文件。需要手动建立,否则最后stage保存会出现问题。
pos: 用于存放正样本图像
neg: 用于存放负样本图像
opencv_creatsamples
opencv_traincascade
pos.txt、neg.txt: 用于存放正负样本图像路径(可不必手动创建,之后自动生成)

  • 2)正负样本

    • 建议正样本和负样本的数量比例最好在1:2和1:3之间(为了尽可能地保证训练效果,当然不在这个比例之间也可训练),负样本图像中不要包含检测的目标并且要尽可能的多样化

    • 将正样本规格化为同一尺寸,并转灰度(最好是8的倍数,我这里用的是32×32,也可以56×56等等,但是不推荐正样本尺寸太大)

    • 负样本不需要规格化,但是要保证负样本的尺寸普遍大于正样本的尺寸,实在不放心可以把负样本给规格化了保证都大于正样本的尺寸

    • 归一化后正负样本示例
      OpenCV中级联分类器Cascade训练方法(Linux)_第2张图片
      OpenCV中级联分类器Cascade训练方法(Linux)_第3张图片

    附1:归一化文件名指令:

    i=1; for x in *; do mv $x $i.jpg; let i=i+1; done
    

    附2:归一化图像尺寸、灰度cv代码:

    #include
    #include
    #include
    
    using namespace std;
    using namespace cv;
    
    int main() {
       int imageNum = 29;
       string Path = "../../pos";
       Mat image_src, image_gray;
       for(int i = 1;i<=imageNum;i++){
       	string imagePath = cv::format("%s/%d.jpg",Path.c_str(),i);
       	cout<<imagePath<<endl;
       	image_src = cv::imread(imagePath);
    
       	cv::resize(image_src,image_src,cv::Size(32,32));
       	cvtColor(image_src, image_gray, CV_BGR2GRAY);
    
       	imshow("gray",image_gray);
       	imwrite(imagePath,image_gray);
       	cv::waitKey(30);
       }
       cout<<"Finish!"<<endl;
       return 0;
    }
    

4.训练

  • 1)生成正样本描述文件

    • 生成正样本路径:
      ls pos/*.* > pos.txt
      
      OpenCV中级联分类器Cascade训练方法(Linux)_第4张图片
      OpenCV中级联分类器Cascade训练方法(Linux)_第5张图片
    • 替换描述信息:
      在pos.txt文本中ctrl+H将所有的jpg替换成jpg 1 0 0 32 32。 jpg后面的五个数字信息分别表示样本中目标个数、目标在图像中的起始位置x、y、样本的尺寸大小width、height。因为我的正样本只包含目标,所以样本的起始位置为0 0。
      OpenCV中级联分类器Cascade训练方法(Linux)_第6张图片

    OpenCV中级联分类器Cascade训练方法(Linux)_第7张图片

  • 利用opencv_creatsamples生成描述文件.vec:

    ./opencv_createsamples -vec pos.vec -info pos.txt -num 29 -w 32 -h 32
    

    其中-info表示指定的描述文件,-vec指定输出的vec文件名,-w-h表示规定的样本尺寸,-num表示创建的样本数目。
    OpenCV中级联分类器Cascade训练方法(Linux)_第8张图片
    生成了pos.vec即说明成功:
    生成pos.vec

  • 2)生成负样本相对路径

    注意:与正样本不同,负样本需要的为相对路径,但不需额外信息

    ls ./neg/*.* > neg.txt
    

    OpenCV中级联分类器Cascade训练方法(Linux)_第9张图片

  • 3)利用opencv_traincascade训练生成.xml

    sudo ./opencv_traincascade -data classifier -vec pos.vec -bg neg.txt -numStages 20 -minHitRate 0.999 -maxFalseAlarmRate 0.5 -numPos 29 -numNeg 30 -w 92 -h 112 -mode ALL -precalcValBufSize 1024   -precalcIdxBufSize 1024 -featureType LBP
    

    OpenCV中级联分类器Cascade训练方法(Linux)_第10张图片

    • data 目录名,如不存在训练程序会创建它,用于存放训练好的分类器。

    • vec 包含正样本的vec文件名(由opencv_createsamples程序生成)。

    • bg 背景描述文件,也就是包含负样本文件名的那个描述文件。

    • numPos 每级分类器训练时所用的正样本数目。

    • numNeg 每级分类器训练时所用的负样本数目,可以大于 -bg 指定的图片数目。

    • numStages 训练的分类器的级数。

    • precalcValBufSize缓存大小,用于存储预先计算的特征值(feature values),单位为MB。

    • precalcIdxBufSize缓存大小,用于存储预先计算的特征索引(feature indices),单位为MB。

    • baseFormatSave这个参数仅在使用Haar特征时有效。如果指定这个参数,那么级联分类器将以老的格式存储。

    • 其他人经验:(precalcValBufSize和precalcIdxBufSize这两个参数不知道是给整个训练过程分配的内存大小还是每个stage分配的内存大小,还有一些说法说这个是给每个样本分配的内存。。。我尝试设置为1024的时候报错内存不足,后来改成了256就可以了,但是设置得太小会导致训练的时间过长,建议多尝试几次尽量把这两个参数设置得大一点)

      训练完毕生成的cascade.xml:
      OpenCV中级联分类器Cascade训练方法(Linux)_第11张图片

5.检测代码(仅供参考)

#include
#include
#include
#include

#define IMAGE_NUM 136

using namespace std;
using namespace cv;
int main() {
	Mat image_src, image_gray, image_sample;
	static int sample_num = 1;
	for(int i = 1;i<=IMAGE_NUM;i++){
		string imagePath = cv::format("../src_data/%d.jpg",i);
		image_src = imread(imagePath);
		cout<<imagePath<<endl;
		cvtColor(image_src, image_gray, CV_BGR2GRAY);
		equalizeHist(image_gray, image_gray);	//直方图均衡化,增加对比度
		
		String face_cascade_name = "../cascade.xml";
		CascadeClassifier face_cascade, eyes_cascade;	//载入分类器
		if (!face_cascade.load(face_cascade_name)) {	//加载脸部分类器失败
			cout << "Load haarcascade_face.xml failed!" << endl;
			return 0;
		}

		vector<Rect> faceRect;
		face_cascade.detectMultiScale(image_gray, faceRect, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30));//检测

		for (size_t i = 0; i < faceRect.size(); i++){
			rectangle(image_src, faceRect[i], Scalar(0, 0, 255));      //用矩形画出检测到的位置
			image_sample = image_gray(faceRect[i]);
			string samplePath = cv::format("../data/%d.jpg",sample_num++);
			imwrite(samplePath,image_sample);	//保存样本
		}
		cv::imshow("src", image_src);         //显示当前帧
		string imagePath_save = cv::format("../data/image/%d.jpg",i);
		imwrite(imagePath_save,image_src);
		cv::waitKey(50);
	}
	cv::waitKey(0);
	return 0;
}

你可能感兴趣的:(机器学习)