最近做了一个目标检测的应用,通过大量的待检测目标的样本进行训练,得到分类器;然后输入测试视频,看分类器的检测结果。主要应用了OpenCV自带的工具:
1.opencv\build\x86\vc10\bin下的opencv_createsamples.exe
2.opencv\build\x86\vc10\bin下的opencv_traincascade.exe
训练的算法是adaboost级联分类器。关于adaboost的算法讲解,这篇博文有不错的讲解:http://blog.csdn.net/mousever/article/details/52038198
opencv_traincascade提供了三种不同的特征提取方式:Haar,LBP,HOG。在本次应用中,待检测的目标如下图蓝色椭圆中所示:
整体的步骤流程如下:
1、 正负样本的创建;
2、 利用样本,训练分类器;
3、 利用训练好的分类器进行目标检测。
训练样本分为正样本和反样本。正样本是指待检测的目标样本(例如汽车,人脸,标志物等),反例样本指不包含正样本的背景图片(需要注意的是,这些反例样本尽量是出现正样本场景中的背景图片),所有的样本图片都被归一化为同样的尺寸大小(例如,20x20、20x30)。 在本次演示工程中,通过摄像头在不同光照、不同旋转角度下拍摄待检测物体的照片,然后对图片进行抠图,得到正样本。得到的样本数量约为500~1000个。然后拍摄正样本可能的背景画面,得到负样本数量约为1000~1500个。得到正样本后,利用OpenCV函数库中的CreateSamples.exe工具生成正样本描述文件。这个文件是接下来训练要用的,可以看做是正样本特征的第一步提取。关于opencv_createsamples.exe的运行命令参数,可以查询此篇博客:http://blog.csdn.net/wuxiaoyao12/article/details/39227189。opencv_createsamples命令为:
opencv_createsamples.exe -info info.txt -vec vector.vec -num 700 -w 20 -h 30训练阶段算法采用了adaboost(自适应增强)算法,这是一种级联分类器的算法,将多个性能较弱的分类器级联,组合成一种性能强的分类器。它的自适应在于:前一个基本分类器分错的样本会得到加强,加权后的全体样本再次被用来训练下一个基本分类器。同时,在每一轮中加入一个新的弱分类器,直到达到某个预定的足够小的错误率或达到预先指定的最大迭代次数。OpenCV提供了开源的adaboost算法库,可以比较方便的调用;而对于应用,比较重要的一点是特征的选取。通常,样本可以用Haar、HOG、LBP等特征描述,例如人脸,用Haar效果比较好,而对于行人,用HOG效果比较好。在应用时,应分析物体特征,选择较好的特征提取算法。opencv_traincascade命令为:
opencv_traincascade.exe -data data/cascade -vec data/vector.vec -bg negative/infofile.txt -numPos 600 -numNeg 1000 -numStages 20 -featureType LBP -mode ALL -w 20 -h 30值得注意的是其中的numPos参数,并非为createsamples阶段采用的700,否则会报错。建议为num的0.8~0.9。另外经过测试,对于featureType参数,LBP比HAAR效果相差无几,但训练速度快了很多。
经过训练可以得到分类器。测试训练效果如图所示,蓝色椭圆框内为检测到的物体:
最后,附上源码。
1)产生正样本图片的源码。通过摄像头采集图像,在图像中设置一个矩形框并绘制在图像中,作为采集样本的区域。按s键保存样本图片,并归一化成20*30大小。
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
#include
using namespace cv;
using namespace std;
Mat image;
bool selectObje
selection &= Rect(0, 0, image.cols, image.rows);
}
switch( event )
{
case CV_EVENT_LBUTTONDOWN:
origin = Point(x,y);
selection = Rect(x,y,0,0);
selectObject = true;
break;
case CV_EVENT_LBUTTONUP:
selectObject = false;
write_en = true;
break;
}
}
int main( int argc, const char** argv )
{
VideoCapture cap;
cap.open(1);
if( !cap.isOpened() )
{
cout << "***Could not initialize capturing...***\n";
cout << "Current parameter's value: \n";
return -1;
}
namedWindow( "CamShift Demo",1 );
selection = Rect(300,300,100,160);
Mat frame, cp_roi;
bool paused = false;
int cnt = 0;
for(;;)
{
cap >> frame;
// cout << "frame.cols:"<< frame.cols << endl;
// cout << "frame.rows:"<< frame.rows << endl;
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), 3, 8);
char c = (char)waitKey(10);
if(c=='s')
{
roi.copyTo(cp_roi);
resize(cp_roi,cp_roi,Size(20,30));
char filename[30];
sprintf_s(filename,"../positive/%d.bmp",cnt);
cv::imwrite(filename,cp_roi);
cnt++;
}
imshow( "CamShift Demo", image );
if( c == 27 )
break;
}
return 0;
}
2)利用训练好的分类器进行检测的代码:
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
#include
using namespace std;
using namespace cv;
/** Function Headers */
void detectAndDisplay( Mat frame );
/** Global variables */
//-- Note, either copy these two files from opencv/data/haarscascades to your current folder, or change these locations
String face_cascade_name = "cascade_LBP_Stages20.xml";
CascadeClassifier face_cascade;
string window_name = "Capture - Face detection";
RNG rng(12345);
/**
* @function main
*/
int main( void )
{
VideoCapture capture;
Mat frame;
//-- 1. Load the cascades
if( !face_cascade.load( face_cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
//-- 2. Read the video stream
capture.open( 1 );
if( capture.isOpened() )
{
for(;;)
{
capture >> frame;
//-- 3. Apply the classifier to the frame
if( !frame.empty() )
{ detectAndDisplay( frame ); }
else
{ printf(" --(!) No captured frame -- Break!"); break; }
int c = waitKey(10);
if( (char)c == 'c' ) { break; }
}
}
return 0;
}
/**
* @function detectAndDisplay
*/
void detectAndDisplay( Mat frame )
{
std::vector faces;
std::vector faces2;
Mat frame_gray;
cvtColor( frame, frame_gray, COLOR_BGR2GRAY );
//-- Detect faces
face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE|CV_HAAR_FIND_BIGGEST_OBJECT, Size(60, 90),Size(160, 240) );
for( size_t i = 0; i < faces.size(); i++ )
{
Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );
ellipse( frame, center, Size( faces[i].width/2, faces[i].height/2), 0, 0, 360, Scalar( 255, 0, 0 ), 3, 8, 0 );
}
//-- Show what you got
imshow( window_name, frame );
}