OpenCV训练级联分类器检测物体
OpenCV级联分类器操作流程
级联分类器在Android用的,先上效果图
第一次学用,参考了各位前辈的博客,十分感谢!
踩了一些坑,最后做出了一些成果,决定分享记录下来。
以下是参考:
https://blog.csdn.net/wuxiaoyao12/article/details/39227189
https://blog.csdn.net/LZY272942518/article/details/76512473
https://msd.misuland.com/pd/3148108429789233942 (解决我踩的第一个坑)
一、准备正负样本
新建用于存放正样本(pos)、负样本(neg)、训练结果(data)的文件夹,并将opencv中自带的创建样本程序 opencv_createsamples.exe 和训练程序opencv_traincascade.exe,放入文件夹,如下图。
(两个程序的路径: opencv\build\x86\vc15\bin\opencv_createsamples.exe,opencv\build\x86\vc15\bin\opencv_traincascade.exe)。
作为第一次学的小白,个人理解如下。如有不对之处,欢迎留言指出。指教就是给我帮忙,在下必然虚心接受。
正样本:只包含待检测物体的图像。
负样本:不包含样本的背景图像,需要比正样本分辨率大。
正样本可以通过VS,OpenCV,USB摄像头采集图像。负样本也可以通过网络批量下载图片,通过以下代码实现记录。
(源码参考:https://blog.csdn.net/LZY272942518/article/details/76512473)
// An highlighted block
#include<opencv2\opencv.hpp>
#include<highgui.h>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <ctype.h>
using namespace cv;
using namespace std;
Mat image;
int main(int argc, const char** argv)
{
VideoCapture cap;
cap.open(1);
if (!cap.isOpened())
{
cout << "Could not initialize capturing...\n";
return -1;
}
namedWindow("CamShift Demo", 1);
Rect selection = Rect(150, 150, 250, 250);
Mat frame, cp_roi;
bool paused = false;
int cnt = 1;
for (;;)
{
cap >> frame;
frame.copyTo(image);
Mat roi(frame, selection);
rectangle(image, Point(selection.x, selection.y), Point(selection.x + selection.width, selection.y + selection.height), Scalar(255, 0, 0), 1, 8);
char c = (char)waitKey(10);
//按下数字键0,将蓝框中图像(作为正样本)拷贝到指定文件夹
if (c == '0')
{
roi.copyTo(cp_roi);
resize(cp_roi, cp_roi, Size(40, 60));
string filename_pos = "E:/A_YHK/2_mechanical_engineering/AAAAAA/Pickup_Tool/pos/";
string str_pos = ".bmp";
char filename1[50];
filename_pos = filename_pos + to_string(cnt) + str_pos;
cv::imwrite(filename_pos, cp_roi);
cout << filename_pos << endl;
cnt++;
}
//按下数字键9,将整改图像(作为负样本)拷贝到指定文件夹
if (c == '9')
{
string filename_neg = "E:/A_YHK/2_mechanical_engineering/AAAAAA/Pickup_Tool/neg/";
string str_neg = "neg.bmp";
char filename1[50];
filename_neg = filename_neg + to_string(cnt) + str_neg;
cv::imwrite(filename_neg, frame);
cout << filename_neg << endl;
cnt++;
}
imshow("CamShift Demo", image);
if (c == 27)
break;
}
return 0;
}
于是文件夹中就有了正负样本的图像了。
注意:正样本图像需归一化为同样的尺寸大小(这里是40X60)。
二、制作训练数据集
在pos文件夹中新建文本文件,输入
dir /b > pos.dat
(不建议直接在网上复制,否则在保存的时候会出现编码问题)
修改后缀为 .bat ,相信大家都知道是为什么,双击执行就好了。
之后出现 .dat 文件,用记事本打开,删掉非图片的文字行,通过文字替换来描述正样本(描述参数的含义请参考前辈的博客)
同样的操作,在负样本文件夹 neg 中新建 .bat 文件,输入以下代码之后执行
DIR . /S/ON/B>neg.txt
删掉非图片的文字行,之后不用再替换描述语句了。
这里说明一下我掉进的坑,负样本中因使用绝对路径,所以应该使用以上的命令而不是dir /b > neg.dat
否则在最后训练的时候会出现以下错误:
Train dataset for temp stage can not be filled. Branch training terminated.
回到上一级目录,新建 .bat 文件,输入以下指令以创建正样本数据集(num 表示正样本图片的数量):
opencv_createsamples.exe -info pos\pos.dat -vec pos\pos.vec -num 397 -w 40 -h 60
pause
执行。
pos文件夹中生成 .vec 文件
现在pos文件夹下应该有三个非图片的文件:
三、训练
新建 .bat 文件,输入
opencv_traincascade.exe -data “E:\A_YHK\2_mechanical_engineering\AAAAAA\Pickup_Tool\data” -vec “E:\A_YHK\2_mechanical_engineering\AAAAAA\Pickup_Tool\pos\pos.vec” -bg “E:\A_YHK\2_mechanical_engineering\AAAAAA\Pickup_Tool\neg\neg.txt” -numPos 350 -numNeg 500 -numStages 14 -mem 2048 -featureType LBP -minHitRate 0.99 -maxFalseAlarmRate 0.5 -mode ALL -w 40 -h 60
pause
为了保险起见,我所有的文件都用的绝对路径。执行即可训练出分类器(各种参数原理,见前辈博客)
这里再一次说明一下我掉进的坑:numPos 参数表示每一个弱分类器所用的图片数量,我当时一共采了397张图片,但是输入397之后发现报错
于是乎我减少了numPos 的值,就成功了。
受前辈博客的启发,numPos 的值约为 numNeg 值的0.6~0.8倍左右,所以本人采用了0.7倍的值。
在data文件夹中可以看到结果,第一个文件是训练好的分类器。