《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西班牙

2014-01-05 15:28 18303人阅读 评论(23) 收藏 举报

 分类:

图像处理(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

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

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

[cpp] view plain copy

  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

  1. Mat classes;//(numPlates+numNoPlates, 1, CV_32FC1);
  2. Mat trainingData;//(numPlates+numNoPlates, imageWidth*imageHeight, CV_32FC1 );
  3. Mat trainingImages;
  4. vector<int> trainingLabels;
  5. for(int i=0; i< numPlates; i++)
  6. {
  7. stringstream ss(stringstream::in | stringstream::out);
  8. ss << path_Plates << i << ".jpg";
  9. Mat img=imread(ss.str(), 0);
  10. img= img.reshape(1, 1);
  11. trainingImages.push_back(img);
  12. trainingLabels.push_back(1);
  13. }
  14. for(int i=0; i< numNoPlates; i++)
  15. {
  16. stringstream ss(stringstream::in | stringstream::out);
  17. ss << path_NoPlates << i << ".jpg";
  18. Mat img=imread(ss.str(), 0);
  19. img= img.reshape(1, 1);
  20. trainingImages.push_back(img);
  21. trainingLabels.push_back(0);
  22. }
  23. Mat(trainingImages).copyTo(trainingData);
  24. //trainingData = trainingData.reshape(1,trainingData.rows);
  25. trainingData.convertTo(trainingData, CV_32FC1);
  26. Mat(trainingLabels).copyTo(classes);
  27. FileStorage fs("SVM.xml", FileStorage::WRITE);
  28. fs << "TrainingData" << trainingData;
  29. fs << "classes" << classes;
  30. fs.release();

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

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

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

[cpp] view plain copy

  1. #ifndef Plate_h
  2. #define Plate_h
  3. #include <string.h>
  4. #include <vector>
  5. #include <cv.h>
  6. #include <highgui.h>
  7. #include <cvaux.h>
  8. using namespace std;
  9. using namespace cv;
  10. class Plate{
  11. public:
  12. Plate();
  13. Plate(Mat img, Rect pos);
  14. string str();
  15. Rect position;
  16. Mat plateImg;
  17. vector<char> chars;
  18. vector<Rect> charsPos;
  19. };
  20. #endif

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

[cpp] view plain copy

  1. Plate::Plate(Mat img, Rect pos){
  2. plateImg=img;
  3. position=pos;
  4. }

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

[cpp] view plain copy

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

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

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

时间: 2024-12-11 10:57:11

《Mastering Opencv ...读书笔记系列》车牌识别(I)的相关文章

C#刨根究底:《你必须知道的.NET》读书笔记系列

一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP-王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心得和感悟,将技术问题以生动易懂的语言展开,层层深入,以例说理.全书主要,包括了.NET基础知识及其深度分析,以.NET Framework和CLR研究为核心展开.NET本质论述,涵盖了.NET基本知识几乎所有的重点内容.全书分为5个部分,第1部分讲述.NET与面向对象,从底层实现角度分析了.NET如何实现面向

Web高级征程:《大型网站技术架构》读书笔记系列

一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计案例,为读者呈现一幅包括技术选型.架构设计.性能优化.Web安全.系统发布.运维监控等在内的大型网站开发全景视图. 本书不仅适用于指导网站工程师.架构师进行网站技术架构设计,也可用于指导产品经理.项目经理.测试运维人员等了解网站技术架构的基础概念:还可供包括企业系统开发人员在内的各类软件开发从业人员借鉴,了解

C#温故知新:《C#图解教程》读书笔记系列

一.此书到底何方神圣? 本书是广受赞誉C#图解教程的最新版本.作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式.朴实简洁的文字,并辅之以大量表格和代码示例,全面.直观地阐述了C#语言的各种特性.新版本除了精心修订旧版内容外,还全面涵盖了C# 5.0的新增特性,比如异步编程.调用者信息.case表达式.带参数的泛型构造函数.支持null类型运算等.通过本书,读者能够快速.深入地理解C#,为自己的编程生涯打下良好的基础. 本书是C#入门的经典好书,适合对C#感兴趣的所有读者.Daniel

OpenCV学习之路——车牌识别之车牌定位

去年七月份因为学校项目需要开始接触图像处理,但那时候只是到网上找车牌识别代码,然后加入到自己的项目中,不清楚细节原理. 现在自己重新一步步实现车牌识别. 高斯模糊: 1 Mat Gaussian(Mat &img) { 2 Mat out; 3 GaussianBlur(img, out, Size(3, 3), 4 0, 0, BORDER_DEFAULT); 5 return out; 6 7 } 灰度化: 1 Mat Grayscale(Mat &img) { 2 Mat out;

读书笔记系列之&mdash;&mdash;《把时间当做朋友》

0x00 前言 今天我的单词还没背呢.我的文案还没有写完,明天要交了.下午我要去健身房健身,几个月没去了,又长胖了几斤.很多朋友看到这些,就会默默的想到,这不正是现在我的状态吗?是啊,我们的时间都去哪了,年纪越来越大,时间也过的越来越快.不知不觉中我早已落在了时间的后面,望着他的背影渐渐离去.刚开始还有些勇气去追逐,奔跑.试图追上他的步伐,渐渐的距离越来越远,我们甚至连抬起头的勇气都没有了,任凭时间消磨着我们的意志,像旋涡一样吞噬着你.可是明显有一些人,明显在数量上并不是大多数人,他们在用另一种

《Master Opencv...读书笔记》非刚性人脸跟踪 III

上篇文章中,我们获得了人脸的各种表情模式,也就是一堆标注点的形变参数.这次我们需要训练一中人脸特征(团块模型),它能够对人脸的不同部位(即"标注点")分别进行描述,作为后面人脸跟踪.表情识别的区分依据.本次博文的主要内容: a.      介绍下人脸特征检测器大概有哪些类别 b.      详细介绍随机梯度法,并介绍在人脸团块特征提取时的应用 c.      为了提高训练/跟踪的健壮性,利用上一讲对输入的图像进行大小.角度的约束 人脸特征检测器综述 人脸特征检测与普通的物体检测非常相似

《Master Opencv...读书笔记》非刚性人脸跟踪 IV (终)

一.我们目前为止拥有什么 为了有一个连续完整的认识,在介绍最后一节前,先梳理下至今我们训练了哪些数据特征,并且训练它们的目的是什么. 1.      ft_data:利用手工标注工具,获取最原始的样本训练数据,包括以下内容: 图像名称集合imnames:表明在哪幅图像上标注特征点: 二维坐标集合points:手工标准点,后续更高级别特征均围绕这些特征点展开: 对称坐标索引集合symmetry:标注样本图像的镜像图像上的特征点,扩大样本库: 连接索引集合connections:描述手工标注的人脸特

《HTTP权威指南》读书笔记-客户端识别与Cookie机制

一.Cookie的类型 可以笼统地将Cookie分为两大类:会话Cookie和持久Cookie. 会话Cookie是一种临时Cookie,它记录了用户访问站点时的设置和偏好.用户退出浏览器时,会话Cookie就被删除了. 持久Cookie的生存时间更长一些:它们存储在硬盘上,浏览器退出,计算机重启时它们仍然存在.通常会用持久Cookie维护某个用户会周期性访问的站点的配置文件或登录名.两者的唯一区别在于它们的过期时间. Cookie中可以包含任意信息,但它们通过都只包含一个服务器为了进行跟踪而产

EC读书笔记系列之3:条款5、条款6、条款7

条款5:了解C++默默编写并调用哪些函数 记住: ★编译器可以(仅仅是可以,并非必须,仅当程序中有这样的用法时才会这么做!!!)暗自为class创建default构造函数,copy构造函数,copy assignment操作符以及析构函数. ---------------------------------------------------------------------------------------------------------------------------------