本系列文章旨在学习如何在opencv中基于haar-like特征训练自己的分类器,并且用该分类器用于模式识别。该过程大致可以分为一下几个大步骤:
1.准备训练样本图片,包括正例及反例样本
2.生成样本描述文件
3.训练样本
4.目标识别
=================
本文主要对步骤1、步骤2进行说明。
1.准备训练样本图片,包括正例及反例样本
1)正样本的采集:
所谓正样本,是指只包含待识别的物体的图片,一般是一些局部的图片,且最好能转化为灰度图。比如,若你想识别人脸,则正样本应尽可能只包含人脸,可以留一点周边的背景但不要过多。在正样本的采集上,我们有两种图形标定工具可以使用:(1)opencv的imageClipper (2)objectMarker。这两个工具都支持傻瓜式地对图片中的物体进行矩形标定,可以自动生成样本说明文件,自动逐帧读取文件夹内的下一帧。我用的是objectMarker。如果你找不到这个软件,可以留下邮箱,我发给你。
在标定的时候尽量保持长宽比例一致,也就是尽量用接近正方形的矩形去标定待识别的物体,至于正方形的大小影响并不大。尽管OpenCV推荐训练样本的最佳尺寸是20x20,但是在下一步生成样本描述文件时可以轻松地将其它尺寸缩放到20x20。标定完成后生成的样本说明文件info.txt内容举例如下:
rawdata/ (1).bmp 1 118 26 81 72 rawdata/ (10).bmp 2 125 72 48 46 0 70 35 43 rawdata/ (11).bmp 1 105 87 43 42 rawdata/ (12).bmp 2 1 70 34 38 105 87 41 44 ...
其中rawdata文件夹存放了所有待标定的大图,objectMarker.exe与rawdata文件夹同级。这个描述文件的格式已经很接近opencv所要求的了。
2)负样本的采集:
所谓负样本,是指不包含待识别物体的任何图片,因此你可以将天空、海滩、大山等所有东西都拿来当负样本。但是,很多时候你这样做是事倍功半的。大多数模式识别问题都是用在视频监控领域,摄像机的角度跟高度都相对固定。如果你知道你的项目中摄像机一般都在拍什么,那负样本可以非常有针对性地选取,而且可以事半功倍。举个例子,你现在想做火车站广场的异常行为检测,在这个课题中行人检测是必须要做的。而视频帧的背景基本都是广场的地板、建筑物等。那你可以在人空旷的时候选择取一张图,不同光照不同时段下各取一张图,然后在这些图上随机取图像块,每个块20x20,每个块就是一个负样本。这几张图就能缠上数以千计数以万计的负样本!而且针对性强。因为海洋、大山等东西对你的识别一点帮助也没有,还会增加训练的时间,吃力不讨好的事还是少做为好。我写了一段小程序,功能是根据背景图片自动随机生成指定数量指定尺寸的负样本:
1 #include "stdafx.h" 2 #include "cv.h" 3 #include "highgui.h" 4 #include <iostream> 5 #include <string> 6 7 using namespace std; 8 using namespace cv; 9 10 //从背景图片中随机抽取图像块,多用于生成负样本 11 #define kImageBlockWidth 40 //图像块大小 12 #define kImageBlockHeight 40 13 #define kLoopTimes 1000 //期望样本数 14 15 int _tmain(int argc, _TCHAR* argv[]) 16 { 17 int originX = 0, originY = 0; 18 int width_limited = 0, height_limited = 0; 19 int width = 0, height = 0; 20 IplImage *bgImage = cvLoadImage("neg\bg1.bmp"); 21 IplImage *blockImage = cvCreateImage(cvSize(kImageBlockWidth, kImageBlockHeight), bgImage->depth, bgImage->nChannels); 22 width = bgImage->width; 23 height = bgImage->height; 24 width_limited = width - kImageBlockWidth; 25 height_limited = height - kImageBlockHeight; 26 cout<<width_limited<<" "<<height_limited; 27 for (int i = 0; i < kLoopTimes; i++) 28 { 29 originX = rand() % width_limited; 30 originY = rand() % height_limited; 31 cvZero(blockImage); 32 CvPoint2D32f center_block = cvPoint2D32f(originX + kImageBlockWidth / 2, originY + kImageBlockHeight / 2); 33 cvGetRectSubPix(bgImage, blockImage, center_block); 34 char saveFileName[100] = {'