本文主要介绍目前主流的adaboost目标检测算法,和CNN卷积神经网络字符识别算法。以扑克牌识别技术为题介绍相关的开发流程和经验。
整个系统包括,
1、摄像头采集,这里以USB摄像头通过directShow采集为例进行介绍。一个线程做采集,一个线程做检测识别。
2、字符检测正负样本得取。后面会详细介绍怎么在只有视频的情况下,
(1)自己写个鼠标拉框手工割取样本的软件,采用OpenCV的鼠标相应控件很容易实现。
(2)或者通过灰度化,自适应二值化,ROI找轮廓,轮廓筛选,ROI轮廓分割,自动割取样本;
(3)以及通过pictureRelate进行重复高样本自动剔除等等筛选。
3、adaboost字符检测:
(1)用adaboost+haar特征训练第2步中得取的样本。
(2)进一步将测试分割出的误识别样本,进行筛选,重复2,3两步。直到获得满意的检测率。
4、识别样本得取
(1)字符识别样本读取,分为0-10,JQK,共14个类别。
(2)花色样本得取,共桃杏梅方四个类。
5、采用CNN组进行样本训练识别。这里为了提高识别率,采用两个CNN分别识别4.1和4.2,采用两个CNN并行思路提升识别速度。
(1)针对4.1的14各类,训练一个CNN分类器;
(2)针对4.2的4各类训练一个CNN分类器。
在介绍系统之前先对本文涉及的两大算法做简要的介绍:
1、adaboost算法实例介绍,这里不上理论,直接来实例对照着学boosting的思路。
http://www.sigvc.org/bbs/thread-727-1-1.html
2、CNN算法详细介绍。
http://www.cnblogs.com/tornadomeet/archive/2013/05/05/3061457.html
第二部分主要讲解做目标检测时候,怎么得取正负样本以及如何对正负样本进行筛选。
(1)自己写个鼠标拉框手工割取样本的软件,采用OpenCV的鼠标相应控件很容易实现。
前面已经通过directShow实现了视频采集和保存了,下面将用OpenCV实现一个手动拉框,自动保存ROI区域的工具来获取样本。
OpenCV里面采用setMouseCallback(window_name,mouse_function,&mouse); 来回调mouse_function,其中mouse_function里面通过CV_EVENT_LBUTTONDOWN等事件判断相应不同的操作。比如以下代码,可以在窗口画框,这时候你只要将框CvRect坐标保存,就可以实现cvSetImageROI截取区域咯:
- #include <cv.h>
- #include <highgui.h>
-
- // Define our callback which we will install for
- // mouse events.
- //
- void my_mouse_callback(
- int event, int x, int y, int flags, void* param
- );
-
- CvRect box;
- bool drawing_box = false;
-
- // A litte subroutine to draw a box onto an image
- //
- void draw_box( IplImage* img, CvRect rect ) {
- cvRectangle (
- img,
- cvPoint(box.x,box.y),
- cvPoint(box.x+box.width,box.y+box.height),
- cvScalar(0xff,0x00,0x00) /* red */
- );
- }
-
- int main( int argc, char* argv[] ) {
-
- box = cvRect(-1,-1,0,0);
-
- IplImage* image = cvCreateImage(
- cvSize(200,200),
- IPL_DEPTH_8U,
- 3
- );
- cvZero( image );
- IplImage* temp = cvCloneImage( image );
-
- cvNamedWindow( "Box Example" );
-
- // Here is the crucial moment that we actually install
- // the callback. Note that we set the value ‘param’ to
- // be the image we are working with so that the callback
- // will have the image to edit.
- //
- cvSetMouseCallback(
- "Box Example",
- my_mouse_callback,
- (void*) image
- );
-
- // The main program loop. Here we copy the working image
- // to the ‘temp’ image, and if the user is drawing, then
- // put the currently contemplated box onto that temp image.
- // display the temp image, and wait 15ms for a keystroke,
- // then repeat…
- //
- while( 1 ) {
-
- cvCopyImage( image, temp );
- if( drawing_box ) draw_box( temp, box );
- cvShowImage( "Box Example", temp );
-
- if( cvWaitKey( 15 )==27 ) break;
- }
-
- // Be tidy
- //
- cvReleaseImage( &image );
- cvReleaseImage( &temp );
- cvDestroyWindow( "Box Example" );
- }
-
- // This is our mouse callback. If the user
- // presses the left button, we start a box.
- // when the user releases that button, then we
- // add the box to the current image. When the
- // mouse is dragged (with the button down) we
- // resize the box.
- //
- void my_mouse_callback(
- int event, int x, int y, int flags, void* param )
- {
-
- IplImage* image = (IplImage*) param;
-
- switch( event ) {
- case CV_EVENT_MOUSEMOVE: {
- if( drawing_box ) {
- box.width = x-box.x;
- box.height = y-box.y;
- }
- }
- break;
- case CV_EVENT_LBUTTONDOWN: {
- drawing_box = true;
- box = cvRect( x, y, 0, 0 );
- }
- break;
- case CV_EVENT_LBUTTONUP: {
- drawing_box = false;
- if( box.width<0 ) {
- box.x+=box.width;
- box.width *=-1;
- }
- if( box.height<0 ) {
- box.y+=box.height;
- box.height*=-1;
- }
- draw_box( image, box );
- }
- break;
- }
- }
(2)或者通过灰度化,自适应二值化,ROI找轮廓,轮廓筛选,ROI轮廓分割,自动割取样本;
(3)以及通过pictureRelate进行重复高样本自动剔除等等筛选。
第二部分主要讲解做目标检测时候,怎么得取正负样本以及如何对正负样本进行筛选。
(1)自己写个鼠标拉框手工割取样本的软件,采用OpenCV的鼠标相应控件很容易实现。
前面已经通过directShow实现了视频采集和保存了,下面将用OpenCV实现一个手动拉框,自动保存ROI区域的工具来获取样本。
OpenCV里面采用setMouseCallback(window_name,mouse_function,&mouse); 来回调mouse_function,其中mouse_function里面通过CV_EVENT_LBUTTONDOWN等事件判断相应不同的操作。比如以下代码,可以在窗口画框,这时候你只要将框CvRect坐标保存,就可以实现cvSetImageROI截取区域咯:
- #include <cv.h>
- #include <highgui.h>
- // Define our callback which we will install for
- // mouse events.
- //
- void my_mouse_callback(
- int event, int x, int y, int flags, void* param
- );
- CvRect box;
- bool drawing_box = false;
- // A litte subroutine to draw a box onto an image
- //
- void draw_box( IplImage* img, CvRect rect ) {
- cvRectangle (
- img,
- cvPoint(box.x,box.y),
- cvPoint(box.x+box.width,box.y+box.height),
- cvScalar(0xff,0x00,0x00) /* red */
- );
- }
- int main( int argc, char* argv[] ) {
- box = cvRect(-1,-1,0,0);
- IplImage* image = cvCreateImage(
- cvSize(200,200),
- IPL_DEPTH_8U,
- 3
- );
- cvZero( image );
- IplImage* temp = cvCloneImage( image );
- cvNamedWindow( "Box Example" );
- // Here is the crucial moment that we actually install
- // the callback. Note that we set the value ‘param’ to
- // be the image we are working with so that the callback
- // will have the image to edit.
- //
- cvSetMouseCallback(
- "Box Example",
- my_mouse_callback,
- (void*) image
- );
- // The main program loop. Here we copy the working image
- // to the ‘temp’ image, and if the user is drawing, then
- // put the currently contemplated box onto that temp image.
- // display the temp image, and wait 15ms for a keystroke,
- // then repeat…
- //
- while( 1 ) {
- cvCopyImage( image, temp );
- if( drawing_box ) draw_box( temp, box );
- cvShowImage( "Box Example", temp );
- if( cvWaitKey( 15 )==27 ) break;
- }
- // Be tidy
- //
- cvReleaseImage( &image );
- cvReleaseImage( &temp );
- cvDestroyWindow( "Box Example" );
- }
- // This is our mouse callback. If the user
- // presses the left button, we start a box.
- // when the user releases that button, then we
- // add the box to the current image. When the
- // mouse is dragged (with the button down) we
- // resize the box.
- //
- void my_mouse_callback(
- int event, int x, int y, int flags, void* param )
- {
- IplImage* image = (IplImage*) param;
- switch( event ) {
- case CV_EVENT_MOUSEMOVE: {
- if( drawing_box ) {
- box.width = x-box.x;
- box.height = y-box.y;
- }
- }
- break;
- case CV_EVENT_LBUTTONDOWN: {
- drawing_box = true;
- box = cvRect( x, y, 0, 0 );
- }
- break;
- case CV_EVENT_LBUTTONUP: {
- drawing_box = false;
- if( box.width<0 ) {
- box.x+=box.width;
- box.width *=-1;
- }
- if( box.height<0 ) {
- box.y+=box.height;
- box.height*=-1;
- }
- draw_box( image, box );
- }
- break;
- }
- }
(2)或者通过灰度化,自适应二值化,ROI找轮廓,轮廓筛选,ROI轮廓分割,自动割取样本;
(3)以及通过pictureRelate进行重复高样本自动剔除等等筛选。
在样本数量比较少的情况下,可以采用HOG、SVM对样本进行初步的筛选出,正负样本,本文接着上一节二值化出来部分样本后,用pictureRelate做初步筛选出正负样本各500,准确训练。
1、pictureRelate使用http://www.walthelm.net/picture-relate/
可以用来比较图片的相似程度,或找出类似的图片文件的图像处理工具。在同一个视窗里浏览不同文件夹和硬盘驱动器中的图片文件#支持查看,改名,删除,剪贴,拖动,切换至文件管理器等操作#可处理多达50000多张图片!支持JPG, BMP, PNG, TIFF, PPM, PGM, PBM, RAS等格式#可以通过导入过滤命令行读取任何其它格式。
这里HOG LBP特征可以采用VLfeat(http://www.vlfeat.org/)这个开源库里面的特征,或者用OpenCV里面的也可以,分类器SVM采用的是OpenCV里面的。
2、下面介绍下VLfeat里面的HOG特征
左图是原图,右图是HOG变化后的可视化效果。可以仔细对比,房子的轮廓还是可见的。
3、HOG+SVM训练目标检测的流程(OpenCV自带):
http://blog.youkuaiyun.com/carson2005/article/details/7841443
(1)准备训练样本集合;包括正样本集和负样本集;
(2)收集到足够的训练样本之后,你需要手动裁剪样本。例如,你想用Hog+SVM来对商业步行街的监控画面中进行行人检测,那么,你就应该用收集到的训练样本集合,手动裁剪画面中的行人(可以写个简单程序,只需要鼠标框选一下,就将框选区域保存下来--这一步在上一节有介绍)。
(3)裁剪得到训练样本之后,将所有正样本放在一个文件夹中;将所有负样本放在另一个文件夹中;并将所有训练样本缩放到同样的尺寸大小。本文是采用90*24的区域训练的。
(4)提取所有正样本的Hog特征;
(5)提取所有负样本的Hog特征;
(6)对所有正负样本赋予样本标签;例如,所有正样本标记为1,所有负样本标记为0;
(7)将正负样本的Hog特征,正负样本的标签,都输入到SVM中进行训练;Dalal在论文中考虑到速度问题,建议采用线性SVM进行训练。这里,不妨也采用线性SVM;
(8)SVM训练之后,将结果保存为文本文件。
(9)线性SVM进行训练之后得到的文本文件里面,有一个数组,叫做support vector,还有一个数组,叫做alpha,有一个浮点数,叫做rho;将alpha矩阵同support vector相乘,注意,alpha*supportVector,将得到一个列向量。之后,再该列向量的最后添加一个元素rho。如此,变得到了一个分类器,利用该分类器,直接替换opencv中行人检测默认的那个分类器(cv::HOGDescriptor::setSVMDetector()),就可以利用你的训练样本训练出来的分类器进行检测了。
效果显示: