《Mastering Opencv ...读书笔记系列》车牌识别(I)

http://blog.csdn.net/jinshengtao/article/details/17883075/  《Mastering Opencv ...读书笔记系列》车牌识别(I)

http://blog.csdn.net/jinshengtao/article/details/17954427   《Mastering Opencv ...读书笔记系列》车牌识别(II)

Mastering Opencv ...读书笔记系列》车牌识别(I)

标签: 车牌分割svm西班牙
 分类:
 
图像处理(25) 

一、ANPR简介:

  Automatic Number Plate Recognition (ANPR),,是一种使用Optical Character Recognition (OCR)和其他分割、检测方法来读取汽车注册牌照的算法。最好的ANPR算法结果是由红外线照相机拍摄图片得到的。因为车牌的特殊材质,夜间会有逆反射效果,看不清车牌。但是现在我们不使用IR图片,我们使用常规图片,这样就增加了我们检测错误和识别错误的等级,以显示我们的算法有多牛逼【老外的意思,有逆反射的图片我没试过】。下面给出,反射、散射、逆反射的示意图:

  每个国家的车牌规格都不一样,这里使用西班牙的车牌,左边4个为数字,右边2个为字母,车牌以白色为背景。具体字符间隔如下图所示:

ANPR算法大体分为两个步骤:

1.车牌检测:检测车牌在图像中的位置

2.车牌识别:使用OCR算法检测车牌上的字母数字字符

这篇博文今天只讲车牌检测【提取车牌、SVM如何训练】,车牌识别为下一篇博文,搬到android系统为下下篇博文

 

二、车牌检测

  大体也分为两个步骤:

1.图像分割:采用一系列不同的滤波器、形态学操作、轮廓算法和验证算法,提取图像中可能包含车牌的区域。

2.图像分类:对每个图像块使用支持向量机SVM分类,并由代码自动创建正负样本【正:有车牌,负:无车牌】(车牌规格统一:800像素宽,拍摄位置大概离车2-4米远)

 

整个车牌检测部分,会涉及以下内容:

 Sobel filter

 Threshold operation

 Close morphologic operation

 Mask of one filled area

 Possible detected plates marked in red (features images)

 Detected plates after the SVM classifier

 

   假设车牌图片没有旋转和变形,则车牌分割的一个重要特征是车牌中有大量的垂直边缘。这个特征可以通过在第一阶段剔除没有任何垂直边缘的区域来提取。车牌原图:

具体算法步骤如下:

1.将彩色图像转化为灰度图,并采用5*5模版对图像进行高斯模糊来退出由照相机或其他环境噪声(如果不这么做,我们会得到很多垂直边缘,导致错误检测。)

2.使用Sobel滤波器求一阶水平方向导数,以此寻找垂直边缘

3.使用Otsu自适应阈值算法获得图像二值化的阈值,并由此得到一副二值画图片

4.采用闭操作,去除每个垂直边缘线之间的空白空格,并连接所有包含 大量边缘的区域(这步过后,我们将有许多包含车牌的候选区域)

5.由于大多数区域并不包含车牌,我们使用轮廓外接矩形的纵横比和区域面积,对这些区域进行区分。

a.首先使用findContours找到外部轮廓

b.使用minAreaRect获得这些轮廓的最小外接矩形,存储在vector向量中

c.使用面积和长宽比,作基本的验证【阈值:长宽比为4.727272,允许误差范围正负40%,面积范围15*15至125*125】

经过判断后的轮廓图:

6.由于每个车牌都包含白色背景属性。我们为了更精确的裁剪图像,可以使用floodfill算法【用指定颜色填充某一密闭区域,相当于油漆桶的功能】来提取那些旋转的矩形。

不会翻译,不怎么明白,各位这步直接看代码吧

第一步的原文:get several seeds near the last rotated rectangle center. Then get the minimum size of plate between the width and height, and use it to generate random seeds near the patch center.】总之,得到每个矩形的中心,然后求每个矩形各自长宽的较小值,再用随机数和这个较小值得到中心附近的种子点

第二步的原文:for each seed, we use a floodFill function to draw a new mask image to store the new closest cropping region:

第三部的翻译:对这些裁剪区域,再次用纵横比和区域面积进行验证,再去除图像的旋转,并裁剪图像到统一尺寸,均衡化图像的灰度

下面,分别给出这三步的结果图:

第一步的图像,绿色为矩形中心,黄色为种子点,不知道大家是否能看清楚:

第二步的图片,上图有5处种子区域,故有5个模版mask图像【代表最近邻接区域】:

第三步的结果图,注意:这里的结果就是训练SVM的正负样本,只要人工挑选一下:

     

 

 

 

下面给出以上部分的完整代码【我讨厌一段段的写:)】

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. // Car_plate.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include<iostream>  
  6. #include <cv.h>  
  7. #include <highgui.h>  
  8. #include <cvaux.h>  
  9.   
  10. using namespace std;  
  11. using namespace cv;  
  12.   
  13. //对minAreaRect获得的最小外接矩形,用纵横比进行判断  
  14. bool verifySizes(RotatedRect mr)  
  15. {  
  16.     float error=0.4;  
  17.     //Spain car plate size: 52x11 aspect 4,7272  
  18.     float aspect=4.7272;  
  19.     //Set a min and max area. All other patchs are discarded  
  20.     int min= 15*aspect*15; // minimum area  
  21.     int max= 125*aspect*125; // maximum area  
  22.     //Get only patchs that match to a respect ratio.  
  23.     float rmin= aspect-aspect*error;  
  24.     float rmax= aspect+aspect*error;  
  25.   
  26.     int area= mr.size.height * mr.size.width;  
  27.     float r= (float)mr.size.width / (float)mr.size.height;  
  28.     if(r<1)  
  29.         r= (float)mr.size.height / (float)mr.size.width;  
  30.   
  31.     if(( area < min || area > max ) || ( r < rmin || r > rmax )){  
  32.         return false;  
  33.     }else{  
  34.         return true;  
  35.     }  
  36.   
  37. }  
  38.   
  39. //直方图均衡化  
  40. Mat histeq(Mat in)  
  41. {  
  42.     Mat out(in.size(), in.type());  
  43.     if(in.channels()==3){  
  44.         Mat hsv;  
  45.         vector<Mat> hsvSplit;  
  46.         cvtColor(in, hsv, CV_BGR2HSV);  
  47.         split(hsv, hsvSplit);  
  48.         equalizeHist(hsvSplit[2], hsvSplit[2]);  
  49.         merge(hsvSplit, hsv);  
  50.         cvtColor(hsv, out, CV_HSV2BGR);  
  51.     }else if(in.channels()==1){  
  52.         equalizeHist(in, out);  
  53.     }  
  54.   
  55.     return out;  
  56.   
  57. }  
  58.   
  59. int _tmain(int argc, _TCHAR* argv[])  
  60. {  
  61.     Mat img_gray = imread("test.jpg",CV_LOAD_IMAGE_GRAYSCALE);  
  62.     Mat input = imread("test.jpg");  
  63.     //char res[20];  
  64.   
  65.     //apply a Gaussian blur of 5 x 5 and remove noise  
  66.     blur(img_gray,img_gray,Size(5,5));  
  67.   
  68.     //Finde vertical edges. Car plates have high density of vertical lines  
  69.     Mat img_sobel;  
  70.     Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, BORDER_DEFAULT);//xorder=1,yorder=0,kernelsize=3  
  71.   
  72.     //apply a threshold filter to obtain a binary image through Otsu's method  
  73.     Mat img_threshold;  
  74.     threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);  
  75.   
  76.     //Morphplogic operation close:remove blank spaces and connect all regions that have a high number of edges  
  77.     Mat element = getStructuringElement(MORPH_RECT, Size(17, 3) );  
  78.     morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);  
  79.   
  80.      //Find 轮廓 of possibles plates  
  81.     vector< vector< Point> > contours;  
  82.     findContours(img_threshold,  
  83.         contours, // a vector of contours  
  84.         CV_RETR_EXTERNAL, // 提取外部轮廓  
  85.         CV_CHAIN_APPROX_NONE); // all pixels of each contours  
  86.   
  87.     //Start to iterate to each contour founded  
  88.     vector<vector<Point> >::iterator itc= contours.begin();  
  89.     vector<RotatedRect> rects;  
  90.   
  91.     //Remove patch that are no inside limits of aspect ratio and area.      
  92.     while (itc!=contours.end()) {  
  93.         //Create bounding rect of object  
  94.         RotatedRect mr= minAreaRect(Mat(*itc));  
  95.         if( !verifySizes(mr)){  
  96.             itc= contours.erase(itc);  
  97.         }else{  
  98.             ++itc;  
  99.             rects.push_back(mr);  
  100.         }  
  101.     }  
  102.   
  103.     // Draw blue contours on a white image  
  104.     cv::Mat result;  
  105.     //input.copyTo(result);  
  106.     //cv::drawContours(result,contours,  
  107.     //  -1, // draw all contours  
  108.     //  cv::Scalar(0,0,255), // in blue  
  109.     //  3); // with a thickness of 1  
  110.   
  111.   
  112.     for(int i=0; i< rects.size(); i++)  
  113.     {  
  114.         //For better rect cropping for each posible box  
  115.         //Make floodfill algorithm because the plate has white background  
  116.         //And then we can retrieve more clearly the contour box  
  117.         circle(result, rects[i].center, 3, Scalar(0,255,0), -1);  
  118.         //get the min size between width and height  
  119.         float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height;  
  120.         minSize=minSize-minSize*0.5;  
  121.         //initialize rand and get 5 points around center for floodfill algorithm  
  122.         srand ( time(NULL) );  
  123.         //Initialize floodfill parameters and variables  
  124.         Mat mask;  
  125.         mask.create(input.rows + 2, input.cols + 2, CV_8UC1);  
  126.         mask= Scalar::all(0);  
  127.         int loDiff = 30;  
  128.         int upDiff = 30;  
  129.         int connectivity = 4;  
  130.         int newMaskVal = 255;  
  131.         int NumSeeds = 10;  
  132.         Rect ccomp;  
  133.         int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;  
  134.         for(int j=0; j<NumSeeds; j++){  
  135.             Point seed;  
  136.             seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);  
  137.             seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);  
  138.             circle(result, seed, 1, Scalar(0,255,255), -1);  
  139.             int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);  
  140.         }  
  141.         //sprintf(res,"result%d.jpg",i);  
  142.         //imwrite(res,mask);  
  143.   
  144.         //Check new floodfill mask match for a correct patch.  
  145.         //Get all points detected for get Minimal rotated Rect  
  146.         vector<Point> pointsInterest;  
  147.         Mat_<uchar>::iterator itMask= mask.begin<uchar>();  
  148.         Mat_<uchar>::iterator end= mask.end<uchar>();  
  149.         for( ; itMask!=end; ++itMask)  
  150.             if(*itMask==255)  
  151.                 pointsInterest.push_back(itMask.pos());  
  152.   
  153.         RotatedRect minRect = minAreaRect(pointsInterest);  
  154.   
  155.         if(verifySizes(minRect)){  
  156.             // rotated rectangle drawing   
  157.             Point2f rect_points[4]; minRect.points( rect_points );  
  158.             for( int j = 0; j < 4; j++ )  
  159.                 line( result, rect_points[j], rect_points[(j+1)%4], Scalar(0,0,255), 1, 8 );      
  160.   
  161.             //Get rotation matrix  
  162.             float r= (float)minRect.size.width / (float)minRect.size.height;  
  163.             float angle=minRect.angle;      
  164.             if(r<1)  
  165.                 angle=90+angle;  
  166.             Mat rotmat= getRotationMatrix2D(minRect.center, angle,1);  
  167.   
  168.             //Create and rotate image  
  169.             Mat img_rotated;  
  170.             warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC);  
  171.   
  172.             //Crop image  
  173.             Size rect_size=minRect.size;  
  174.             if(r < 1)  
  175.                 swap(rect_size.width, rect_size.height);  
  176.             Mat img_crop;  
  177.             getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);  
  178.   
  179.             Mat resultResized;  
  180.             resultResized.create(33,144, CV_8UC3);  
  181.             resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);  
  182.             //Equalize croped image  
  183.             Mat grayResult;  
  184.             cvtColor(resultResized, grayResult, CV_BGR2GRAY);   
  185.             blur(grayResult, grayResult, Size(3,3));  
  186.             grayResult=histeq(grayResult);  
  187.         /*  if(1){  
  188.                 stringstream ss(stringstream::in | stringstream::out); 
  189.                 ss << "haha" << "_" << i << ".jpg"; 
  190.                 imwrite(ss.str(), grayResult); 
  191.             }*/  
  192.             //output.push_back(Plate(grayResult,minRect.boundingRect()));  
  193.         }  
  194.     }  
  195.     //imshow("car_plate",result);  
  196.     waitKey(0);  
  197.     return 0;  
  198. }  

注意上述代码末尾的注释部分:

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. <span style="white-space:pre">      </span>if(1){   
  2.                 stringstream ss(stringstream::in | stringstream::out);  
  3.                 ss << "haha" << "_" << i << ".jpg";  
  4.                 imwrite(ss.str(), grayResult);  
  5.             }  


 

以上部分,就是自动生成正负样本的代码。比人工去QQ截图好多了:)

 

在介绍SVM车牌分类之前,我介绍怎么训练SVM【注意:SVM的实现是个庞大的工程,我一直没有自己弄过,这里使用的还是opencv封装的SVM】

如何训练:
  正样本75张包含车牌的图像和35张不包含车牌的144*33图像。【还有其他更好的特征来训练SVM,PCA,傅立叶变换,纹理分析等等】。
如何获取样本及存放训练数据。
   通过上述图像分割步骤,我们可以得到车牌及非车牌图像,我们把二者都执行reshaple(1,1),再存放到trainImage的矩阵中,并修改对应trainLables矩阵的0-1值,然后把trainData改为32为浮点数系,再把trainData和trainLabel直接写进xml文件【也就是说xml中包含了样本图像的像素值和样本分类标记】

具体代码:

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. Mat classes;//(numPlates+numNoPlates, 1, CV_32FC1);  
  2. Mat trainingData;//(numPlates+numNoPlates, imageWidth*imageHeight, CV_32FC1 );  
  3.   
  4. Mat trainingImages;  
  5. vector<int> trainingLabels;  
  6.   
  7. for(int i=0; i< numPlates; i++)  
  8. {  
  9.   
  10.     stringstream ss(stringstream::in | stringstream::out);  
  11.     ss << path_Plates << i << ".jpg";  
  12.     Mat img=imread(ss.str(), 0);  
  13.     img= img.reshape(1, 1);  
  14.     trainingImages.push_back(img);  
  15.     trainingLabels.push_back(1);  
  16. }  
  17.   
  18. for(int i=0; i< numNoPlates; i++)  
  19. {  
  20.     stringstream ss(stringstream::in | stringstream::out);  
  21.     ss << path_NoPlates << i << ".jpg";  
  22.     Mat img=imread(ss.str(), 0);  
  23.     img= img.reshape(1, 1);  
  24.     trainingImages.push_back(img);  
  25.     trainingLabels.push_back(0);  
  26.   
  27. }  
  28.   
  29. Mat(trainingImages).copyTo(trainingData);  
  30. //trainingData = trainingData.reshape(1,trainingData.rows);  
  31. trainingData.convertTo(trainingData, CV_32FC1);  
  32. Mat(trainingLabels).copyTo(classes);  
  33.   
  34. FileStorage fs("SVM.xml", FileStorage::WRITE);  
  35. fs << "TrainingData" << trainingData;  
  36. fs << "classes" << classes;  
  37. fs.release();  

 

 

以上代码,可以自己另外建一个工程,认为设置一下正负样本的数量numPlates和numNoPlates,正负样本存储的路径path_Plates和path_NoPlates。这样我们就得到了存放正负样本的SVM.XML文件了。

最后,给出使用Opencv提供的SVM分类器,对图像进行分了的完整代码【对一副图像判断其中是否含有西班牙车牌】:

劳什子外国人搞了车牌类,好吧,我挑和本文有关的都贴出来吧

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #ifndef Plate_h  
  2. #define Plate_h  
  3.   
  4. #include <string.h>  
  5. #include <vector>  
  6.   
  7. #include <cv.h>  
  8. #include <highgui.h>  
  9. #include <cvaux.h>  
  10.   
  11. using namespace std;  
  12. using namespace cv;  
  13.   
  14. class Plate{  
  15.     public:  
  16.         Plate();  
  17.         Plate(Mat img, Rect pos);  
  18.         string str();  
  19.         Rect position;  
  20.         Mat plateImg;  
  21.         vector<char> chars;  
  22.         vector<Rect> charsPos;          
  23. };  
  24.   
  25. #endif  

这里,我们只要实现上述Plate类的构造函数就行了

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. Plate::Plate(Mat img, Rect pos){  
  2.     plateImg=img;  
  3.     position=pos;  
  4. }  

下面我再次给出完整代码,不过大家重点关注如何配置SVM就行了:

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. // car_plate_svm.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include<iostream>  
  6. #include <cv.h>  
  7. #include <highgui.h>  
  8. #include <cvaux.h>  
  9. #include "Plate.h"  
  10.   
  11. using namespace std;  
  12. using namespace cv;  
  13.   
  14.   
  15.   
  16.   
  17. //对minAreaRect获得的最小外接矩形,用纵横比进行判断  
  18. bool verifySizes(RotatedRect mr)  
  19. {  
  20.     float error=0.4;  
  21.     //Spain car plate size: 52x11 aspect 4,7272  
  22.     float aspect=4.7272;  
  23.     //Set a min and max area. All other patchs are discarded  
  24.     int min= 15*aspect*15; // minimum area  
  25.     int max= 125*aspect*125; // maximum area  
  26.     //Get only patchs that match to a respect ratio.  
  27.     float rmin= aspect-aspect*error;  
  28.     float rmax= aspect+aspect*error;  
  29.   
  30.     int area= mr.size.height * mr.size.width;  
  31.     float r= (float)mr.size.width / (float)mr.size.height;  
  32.     if(r<1)  
  33.         r= (float)mr.size.height / (float)mr.size.width;  
  34.   
  35.     if(( area < min || area > max ) || ( r < rmin || r > rmax )){  
  36.         return false;  
  37.     }else{  
  38.         return true;  
  39.     }  
  40.   
  41. }  
  42.   
  43. Mat histeq(Mat in)  
  44. {  
  45.     Mat out(in.size(), in.type());  
  46.     if(in.channels()==3){  
  47.         Mat hsv;  
  48.         vector<Mat> hsvSplit;  
  49.         cvtColor(in, hsv, CV_BGR2HSV);  
  50.         split(hsv, hsvSplit);  
  51.         equalizeHist(hsvSplit[2], hsvSplit[2]);  
  52.         merge(hsvSplit, hsv);  
  53.         cvtColor(hsv, out, CV_HSV2BGR);  
  54.     }else if(in.channels()==1){  
  55.         equalizeHist(in, out);  
  56.     }  
  57.   
  58.     return out;  
  59.   
  60. }  
  61.   
  62. vector<Plate> segment(Mat input){  
  63.     vector<Plate> output;  
  64.     //char res[20];  
  65.   
  66.     //apply a Gaussian blur of 5 x 5 and remove noise  
  67.     Mat img_gray;  
  68.     cvtColor(input, img_gray, CV_BGR2GRAY);  
  69.     blur(img_gray, img_gray, Size(5,5));      
  70.   
  71.     //Finde vertical edges. Car plates have high density of vertical lines  
  72.     Mat img_sobel;  
  73.     Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, BORDER_DEFAULT);//xorder=1,yorder=0,kernelsize=3  
  74.   
  75.     //apply a threshold filter to obtain a binary image through Otsu's method  
  76.     Mat img_threshold;  
  77.     threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);  
  78.   
  79.     //Morphplogic operation close:remove blank spaces and connect all regions that have a high number of edges  
  80.     Mat element = getStructuringElement(MORPH_RECT, Size(17, 3) );  
  81.     morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);  
  82.   
  83.     //Find 轮廓 of possibles plates  
  84.     vector< vector< Point> > contours;  
  85.     findContours(img_threshold,  
  86.         contours, // a vector of contours  
  87.         CV_RETR_EXTERNAL, // 提取外部轮廓  
  88.         CV_CHAIN_APPROX_NONE); // all pixels of each contours  
  89.   
  90.     //Start to iterate to each contour founded  
  91.     vector<vector<Point> >::iterator itc= contours.begin();  
  92.     vector<RotatedRect> rects;  
  93.   
  94.     //Remove patch that are no inside limits of aspect ratio and area.      
  95.     while (itc!=contours.end()) {  
  96.         //Create bounding rect of object  
  97.         RotatedRect mr= minAreaRect(Mat(*itc));  
  98.         if( !verifySizes(mr)){  
  99.             itc= contours.erase(itc);  
  100.         }else{  
  101.             ++itc;  
  102.             rects.push_back(mr);  
  103.         }  
  104.     }  
  105.   
  106.      Draw blue contours on a white image  
  107.     cv::Mat result;  
  108.     input.copyTo(result);  
  109.     //cv::drawContours(result,contours,  
  110.     //  -1, // draw all contours  
  111.     //  cv::Scalar(255,0,0), // in blue  
  112.     //  1); // with a thickness of 1  
  113.   
  114.   
  115.     for(int i=0; i< rects.size(); i++)  
  116.     {  
  117.         //For better rect cropping for each posible box  
  118.         //Make floodfill algorithm because the plate has white background  
  119.         //And then we can retrieve more clearly the contour box  
  120.         circle(result, rects[i].center, 3, Scalar(0,255,0), -1);  
  121.         //get the min size between width and height  
  122.         float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height;  
  123.         minSize=minSize-minSize*0.5;  
  124.         //initialize rand and get 5 points around center for floodfill algorithm  
  125.         srand ( time(NULL) );  
  126.         //Initialize floodfill parameters and variables  
  127.         Mat mask;  
  128.         mask.create(input.rows + 2, input.cols + 2, CV_8UC1);  
  129.         mask= Scalar::all(0);  
  130.         int loDiff = 30;  
  131.         int upDiff = 30;  
  132.         int connectivity = 4;  
  133.         int newMaskVal = 255;  
  134.         int NumSeeds = 10;  
  135.         Rect ccomp;  
  136.         int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;  
  137.         for(int j=0; j<NumSeeds; j++){  
  138.             Point seed;  
  139.             seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);  
  140.             seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);  
  141.             circle(result, seed, 1, Scalar(0,255,255), -1);  
  142.             int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);  
  143.         }  
  144.         //sprintf(res,"result%d.jpg",i);  
  145.         //imwrite(res,mask);  
  146.   
  147.         //Check new floodfill mask match for a correct patch.  
  148.         //Get all points detected for get Minimal rotated Rect  
  149.         vector<Point> pointsInterest;  
  150.         Mat_<uchar>::iterator itMask= mask.begin<uchar>();  
  151.         Mat_<uchar>::iterator end= mask.end<uchar>();  
  152.         for( ; itMask!=end; ++itMask)  
  153.             if(*itMask==255)  
  154.                 pointsInterest.push_back(itMask.pos());  
  155.   
  156.         RotatedRect minRect = minAreaRect(pointsInterest);  
  157.   
  158.         if(verifySizes(minRect)){  
  159.             // rotated rectangle drawing   
  160.             Point2f rect_points[4]; minRect.points( rect_points );  
  161.             for( int j = 0; j < 4; j++ )  
  162.                 line( result, rect_points[j], rect_points[(j+1)%4], Scalar(0,0,255), 1, 8 );      
  163.   
  164.             //Get rotation matrix  
  165.             float r= (float)minRect.size.width / (float)minRect.size.height;  
  166.             float angle=minRect.angle;      
  167.             if(r<1)  
  168.                 angle=90+angle;  
  169.             Mat rotmat= getRotationMatrix2D(minRect.center, angle,1);  
  170.   
  171.             //Create and rotate image  
  172.             Mat img_rotated;  
  173.             warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC);  
  174.   
  175.             //Crop image  
  176.             Size rect_size=minRect.size;  
  177.             if(r < 1)  
  178.                 swap(rect_size.width, rect_size.height);  
  179.             Mat img_crop;  
  180.             getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);  
  181.   
  182.             Mat resultResized;  
  183.             resultResized.create(33,144, CV_8UC3);  
  184.             resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);  
  185.             //Equalize croped image  
  186.             Mat grayResult;  
  187.             cvtColor(resultResized, grayResult, CV_BGR2GRAY);   
  188.             blur(grayResult, grayResult, Size(3,3));  
  189.             grayResult=histeq(grayResult);  
  190.             /*  if(1){  
  191.             stringstream ss(stringstream::in | stringstream::out); 
  192.             ss << "haha" << "_" << i << ".jpg"; 
  193.             imwrite(ss.str(), grayResult); 
  194.             }*/  
  195.             output.push_back(Plate(grayResult,minRect.boundingRect()));  
  196.         }  
  197.     }  
  198.     //imshow("car_plate",result);  
  199.     //waitKey(0);  
  200.     return output;  
  201. }  
  202.   
  203. int _tmain(int argc, _TCHAR* argv[])  
  204. {  
  205.     Mat input = imread("test.jpg");  
  206.     vector<Plate> posible_regions = segment(input);  
  207.       
  208.     //SVM for each plate region to get valid car plates  
  209.     //Read file storage.  
  210.     FileStorage fs;  
  211.     fs.open("SVM.xml", FileStorage::READ);  
  212.     Mat SVM_TrainingData;  
  213.     Mat SVM_Classes;  
  214.     fs["TrainingData"] >> SVM_TrainingData;  
  215.     fs["classes"] >> SVM_Classes;  
  216.     //Set SVM params  
  217.     CvSVMParams SVM_params;  
  218.     SVM_params.svm_type = CvSVM::C_SVC;  
  219.     SVM_params.kernel_type = CvSVM::LINEAR; //CvSVM::LINEAR;  
  220.     SVM_params.degree = 0;  
  221.     SVM_params.gamma = 1;  
  222.     SVM_params.coef0 = 0;  
  223.     SVM_params.C = 1;  
  224.     SVM_params.nu = 0;  
  225.     SVM_params.p = 0;  
  226.     SVM_params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.01);  
  227.     //Train SVM  
  228.     CvSVM svmClassifier(SVM_TrainingData, SVM_Classes, Mat(), Mat(), SVM_params);  
  229.   
  230.     //For each possible plate, classify with svm if it's a plate or no  
  231.     vector<Plate> plates;  
  232.     for(int i=0; i< posible_regions.size(); i++)  
  233.     {  
  234.         Mat img=posible_regions[i].plateImg;  
  235.         Mat p= img.reshape(1, 1);  
  236.         p.convertTo(p, CV_32FC1);  
  237.   
  238.         int response = (int)svmClassifier.predict( p );  
  239.         /*if(response==1) 
  240.             plates.push_back(posible_regions[i]);*/  
  241.         printf("%d.jpg分类结果:%d\n",i,response);  
  242.     }  
  243.     return 0;  
  244. }  

好吧,今天到此为止了。还是那张原图,由于图像分割后产生3个候选车牌,所以SVM分类结果为:

 

这里关于OPENCV的各种函数配置,我一点没提,因为如果不懂原理,就不要用人家成熟的东西,否则永远被动,被opencv牵着走。

 

转载于:https://www.cnblogs.com/donaldlee2008/p/5215722.html

使用VC++6.0做开发工具, 采用简单的SDI框架结构 ,一次处理一幅位图(有兴趣的可以作成MDI) 1)位图信息的数据是从左下往右下为一行,一行一行往上排的。 2)每行像素应该是4的倍数,不足的地方用空点补齐,读的时候注意跳过冗余点。 3)主要数据都存在Doc里面,BMP的主要数据存在一个由ImgData指向的BYTE型的内存空间(根据位图的大小,动态分配的)。 4)数据读进来以后,注 意向内存中贴图,以保证刷新的效率。 5)程序执行流程 应用程序生成--》打开--》CDipView的OnFileOpen 函数--》 调用CDipDoc的FileOpen 函数--》并使用myDoc->UpdateAllViews(NULL); 刷新 自动调用CDipView的OnPaint函数--》调用CDipView的OnDraw函数----一个像素点一个像素点的画 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 比较重要的地方 读BMP文件,只能打开256色 (可以是灰度) 显示和内存贴图技术 关于调色板: 调色板实际上是一个数组,4个BYTE 分别是 B,G,R,和 Reserved 每一个像素点都有一个相应的数组。 关于VC和windows 的绘图机制: 使用GDI(图形设备接口)对象,通常使用CDC 类,CPaintDC也一样(device-context)设备上下文 windows下的MFC编程机制,消息驱动,事件等待! 全局的app(应用程序对象) 注意 手工分配内存的清除 和CDC对象的删除 以释放系统的GDI资源 每一个new操作符都要对应一个delete 虽然已经弄出来了,还是希望大家好好读读源程序。 你们以后的工作: 在菜单中添加菜单项,通过ClassWizzard 生成消息响应函数(当然也可手动添加), 所有的操作应当是对 BYTE* ImgData;进行的。 在完成相应的功能后 将 isnewfile 和 isnewiamge 置为真 ,并使用myDoc->UpdateAllViews(NULL); 刷新 当然,可以更加有个性化一点,有能力的同学可以自己完成。 随着课程的进行,菜单功能逐渐丰富,最后完成基本的数字图像处理的功能,而不必最后一下完成一个大的作业。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值