使用opencv的SVM和神经网络实现车牌识别(高质量的文章******)

本文转载自:http://blog.csdn.net/ap1005834/article/details/51340831

一、前言

本文参考自《深入理解OpenCV 实用计算机视觉项目解析》中的自动车牌识别项目,并对其中的方法理解后,再进行实践。深刻认识到实际上要完成车牌区域准确定位、车牌区域中字符的准确分割,字符准确识别这一系列步骤的困难。所以最后的识别效果也是有待进一步提高。

二、程序流程

程序流程如下所示:


相应的main函数如下

[cpp]  view plain  copy
 print ?
  1. #include "carID_Detection.h"  
  2.   
  3.   
  4. int main()  
  5. {      
  6.     Mat img_input = imread("testCarID.jpg");  
  7.     //如果读入图像失败  
  8.     if(img_input.empty())  
  9.     {  
  10.         fprintf(stderr, "Can not load image %s\n""testCarID.jpg");  
  11.         return -1;  
  12.     }  
  13.   
  14.     Mat hsvImg ;  
  15.     cvtColor(img_input,hsvImg,CV_BGR2HSV);  
  16.     vector<Mat> planes;  
  17.     split(hsvImg,planes);  
  18.     Mat sImg;  
  19.     sImg = planes[1];  //获得红色分量  
  20.     blur(sImg,sImg,Size(3,3));   //3*3高斯滤波  
  21.     vector <RotatedRect>  rects_sImg;  
  22.     posDetect(sImg ,rects_sImg);          
  23.   
  24.   
  25.     Mat grayImg;  
  26.     RgbConvToGray(img_input ,grayImg);  
  27.     medianBlur(grayImg,grayImg,3);   //3*3中值滤波  
  28.   
  29.     vector <RotatedRect>  rects_grayImg;  
  30.     posDetect(grayImg ,rects_grayImg);  
  31.   
  32.     vector <RotatedRect>  rects_closeImg;  //车牌区域较为贴近  
  33.     posDetect_closeImg(sImg ,rects_closeImg);  
  34.   
  35.     vector <RotatedRect>  rects_optimal;  
  36.     optimPosDetect(rects_sImg,rects_grayImg,rects_closeImg,rects_optimal);  
  37.   
  38.     vector <Mat> output_area;  
  39.     normalPosArea(img_input ,rects_optimal,output_area);  //获得144*33的候选车牌区域output_area  
  40.   
  41.     CvSVM  svmClassifier;  
  42.               
  43.     svm_train(svmClassifier);  //使用SVM对正负样本进行训练  
  44.    
  45.     vector<Mat> plates_svm;   //需要把候选车牌区域output_area图像中每个像素点作为一行特征向量,后进行预测  
  46.     for(int i=0;i< output_area.size(); ++i)  
  47.     {  
  48.         Mat img = output_area[i];  
  49.         Mat p = img.reshape(1,1);  
  50.         p.convertTo(p,CV_32FC1);  
  51.         int response = (int)svmClassifier.predict( p );  
  52.         if (response == 1)  
  53.             plates_svm.push_back(output_area[i]);    //保存预测结果  
  54.     }  
  55.       
  56.     if(plates_svm.size() != 0)    
  57.     {  
  58.         imshow("Test", plates_svm[0]);     //正确预测的话,就只有一个结果plates_svm[0]  
  59.         waitKey(0);  
  60.     }  
  61.     else  
  62.     {  
  63.         std::cout<<"定位失败";  
  64.         return -1;  
  65.     }  
  66.           
  67.     //从SVM预测获得得车牌区域中分割得字符区域  
  68.     vector <Mat> char_seg;  
  69.     char_segment(plates_svm[0],char_seg);   
  70.   
  71.     //获得7个字符矩阵的相应特征矩阵  
  72.     vector <Mat> char_feature;  
  73.     char_feature.resize(7);  
  74.     for (int i =0;i<char_seg.size() ;++ i)  
  75.         features(char_seg[i], char_feature[i],5);  
  76.   
  77.     //神经网络训练  
  78.     CvANN_MLP ann_classify;  
  79.     ann_train(ann_classify, 34 ,48);   //34为样本的类别数,48为隐藏层的神经元数  
  80.   
  81.     //字符预测  
  82.     vector<int>  char_result;  
  83.     classify(ann_classify,char_feature,char_result);  
  84.   
  85.   
  86.     //此函数等待按键,按键盘任意键就返回  
  87.     svmClassifier.clear();  
  88.   
  89.     return 0;  
  90. }  

三、代码简介

本文编程没有使用类的概念,纯属面向过程编程,以下为主要使用的函数
[cpp]  view plain  copy
 print ?
  1. //carID_Detection.h  
  2. #include "opencv2/core/core.hpp"  
  3. #include "opencv2//highgui/highgui.hpp"  
  4. #include "opencv2/imgproc/imgproc.hpp"  
  5. #include "opencv2/ml/ml.hpp"  
  6. #include <time.h>  
  7. #include <stdlib.h>  
  8.   
  9. #include <iostream>  
  10. using namespace std;  
  11.   
  12. using namespace cv;  
  13.   
  14.   
  15. void RgbConvToGray(const Mat& inputImage,Mat & outpuImage); //rgb转为灰度  
  16.   
  17.   
  18. void posDetect(Mat &, vector <RotatedRect> &); //粗步选取候选车牌区域  
  19. bool verifySizes(const RotatedRect & );  //车牌区域需要满足的条件  
  20. void posDetect_closeImg(Mat &inputImage , vector <RotatedRect> & rects  ) ;  //考虑到车牌距离非常近的时候的情况  
  21. bool verifySizes_closeImg(const RotatedRect & candidate); //距离近时的车牌区域需要满足的条件       
  22.   
  23.   
  24. void optimPosDetect(vector <RotatedRect> & rects_sImg , vector <RotatedRect> & rects_grayImg, //车牌区域进一步定位  
  25.     vector <RotatedRect> & rects_closeImg,vector <RotatedRect> & rects_optimal );  
  26. float calOverlap(const Rect& box1,const Rect& box2);  //计算2个矩阵的重叠比例  
  27.   
  28.   
  29. void normalPosArea(Mat &intputImg, vector<RotatedRect> &rects_optimal, vector <Mat>& output_area );  //车牌裁剪,标准化为144*33  
  30.   
  31.   
  32. void svm_train(CvSVM & );  //取出SVM.xml中的特征矩阵和标签矩阵进行训练  
  33.   
  34.   
  35. void char_segment(const Mat & inputImg,vector <Mat>&); //对车牌区域中的字符进行分割  
  36. bool char_verifySizes(const RotatedRect &);   //字符区域需要满足的条件  
  37. void char_sort(vector <RotatedRect > & in_char ); //对字符区域进行排序  
  38.   
  39.   
  40. void features(const Mat & in , Mat & out ,int sizeData);  //获得一个字符矩阵对应的特征向量  
  41. Mat projectHistogram(const Mat& img ,int t);             //计算水平或累计直方图,取决于t为0还是1  
  42.   
  43.   
  44. void ann_train(CvANN_MLP &ann ,int numCharacters, int nlayers); //取出ann_xml中的数据,并进行神经网络训练  
  45. void classify(CvANN_MLP& ann, vector<Mat> &char_feature , vector<int> & char_result); //使用神经网络模型预测车牌字符,并打印至屏幕  
[cpp]  view plain  copy
 print ?
  1. //carID_Detection.cpp  
  2.   
  3. #include "carID_Detection.h"  
  4.   
  5.   
  6.   
  7. void RgbConvToGray(const Mat& inputImage,Mat & outpuImage)  //g = 0.3R+0.59G+0.11B  
  8. {  
  9.     outpuImage = Mat(inputImage.rows ,inputImage.cols ,CV_8UC1);    
  10.   
  11.     for (int i = 0 ;i<inputImage.rows ;++ i)  
  12.     {  
  13.         uchar *ptrGray = outpuImage.ptr<uchar>(i);   
  14.         const Vec3b * ptrRgb = inputImage.ptr<Vec3b>(i);  
  15.         for (int j = 0 ;j<inputImage.cols ;++ j)  
  16.         {  
  17.             ptrGray[j] = 0.3*ptrRgb[j][2]+0.59*ptrRgb[j][1]+0.11*ptrRgb[j][0];    
  18.         }  
  19.     }  
  20. }  
  21.   
  22. void posDetect_closeImg(Mat &inputImage , vector <RotatedRect> & rects  )   //初步找到候选区域 rects  
  23. {  
  24.       
  25.     Mat img_canny;  
  26.     Canny(inputImage, img_canny, 150, 220);  
  27.     Mat img_threshold;  
  28.     threshold(img_canny , img_threshold,0,255 , CV_THRESH_OTSU+CV_THRESH_BINARY); //otsu算法自动获得阈值  
  29.   
  30.     Mat element = getStructuringElement(MORPH_RECT ,Size(15 ,3));  //闭形态学的结构元素  
  31.     morphologyEx(img_threshold ,img_threshold,CV_MOP_CLOSE,element);  //形态学处理  
  32.   
  33.     //寻找车牌区域的轮廓  
  34.     vector< vector <Point> > contours;  
  35.     findContours(img_threshold ,contours,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//只检测外轮廓  
  36.     //对候选的轮廓进行进一步筛选  
  37.     vector< vector <Point> > ::iterator itc = contours.begin();  
  38.   
  39.     while( itc != contours.end())  
  40.     {  
  41.         RotatedRect mr = minAreaRect(Mat( *itc )); //返回每个轮廓的最小有界矩形区域  
  42.         if(!verifySizes_closeImg(mr))  //判断矩形轮廓是否符合要求  
  43.         {  
  44.             itc = contours.erase(itc);  
  45.         }  
  46.         else       
  47.         {  
  48.               
  49.             rects.push_back(mr);  
  50.             ++itc;  
  51.         }        
  52.     }  
  53.   
  54. }  
  55.   
  56.   
  57. bool verifySizes_closeImg(const RotatedRect & candidate)  
  58. {  
  59.     float error = 0.4;  
  60.     const float aspect = 44/14; //长宽比  
  61.     int min = 100*aspect*100; //最小区域  
  62.     int max = 180*aspect*180;  //最大区域  
  63.     float rmin = aspect - aspect*error; //考虑误差后的最小长宽比  
  64.     float rmax = aspect + aspect*error; //考虑误差后的最大长宽比  
  65.   
  66.     int area = candidate.size.height * candidate.size.width;  
  67.     float r = (float)candidate.size.width/(float)candidate.size.height;  
  68.     if(r <1)  
  69.         r = 1/r;  
  70.   
  71.     if( (area < min || area > max) || (r< rmin || r > rmax)  )  
  72.         return false;  
  73.     else  
  74.         return true;  
  75. }  
  76.   
  77. void posDetect(Mat &inputImage , vector <RotatedRect> & rects  )   //初步找到候选区域 rects  
  78. {  
  79.     Mat img_sobel;  
  80.     Sobel(inputImage , img_sobel , CV_8U, 1,0,3,1,0);  
  81.   
  82.     Mat img_threshold;  
  83.     threshold(img_sobel , img_threshold,0,255 , CV_THRESH_OTSU+CV_THRESH_BINARY); //otsu算法自动获得阈值  
  84.   
  85.     Mat element = getStructuringElement(MORPH_RECT ,Size(15 ,3));  //闭形态学的结构元素  
  86.     morphologyEx(img_threshold ,img_threshold,CV_MOP_CLOSE,element);    
  87.   
  88.     //寻找车牌区域的轮廓  
  89.     vector< vector <Point> > contours;  
  90.     findContours(img_threshold ,contours,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//只检测外轮廓  
  91.     //对候选的轮廓进行进一步筛选  
  92.     vector< vector <Point> > ::iterator itc = contours.begin();  
  93.   
  94.     while( itc != contours.end())  
  95.     {  
  96.         RotatedRect mr = minAreaRect(Mat( *itc )); //返回每个轮廓的最小有界矩形区域  
  97.         if(!verifySizes(mr))  //判断矩形轮廓是否符合要求  
  98.         {  
  99.             itc = contours.erase(itc);  
  100.         }  
  101.         else       
  102.         {  
  103.             rects.push_back(mr);  
  104.             ++itc;  
  105.         }        
  106.     }  
  107. }  
  108.   
  109. bool verifySizes(const RotatedRect & candidate)  
  110. {  
  111.     float error = 0.4;  
  112.     const float aspect = 44/14; //长宽比  
  113.     int min = 20*aspect*20; //最小区域  
  114.     int max = 180*aspect*180;  //最大区域  
  115.     float rmin = aspect - 2*aspect*error; //考虑误差后的最小长宽比  
  116.     float rmax = aspect + 2*aspect*error; //考虑误差后的最大长宽比  
  117.       
  118.     int area = candidate.size.height * candidate.size.width;  
  119.     float r = (float)candidate.size.width/(float)candidate.size.height;  
  120.     if(r <1)  
  121.         r = 1/r;  
  122.   
  123.     if( (area < min || area > max) || (r< rmin || r > rmax)  ) //满足该条件才认为该candidate为车牌区域  
  124.         return false;  
  125.     else  
  126.         return true;  
  127. }  
  128.   
  129. void optimPosDetect(vector <RotatedRect> & rects_sImg , vector <RotatedRect> & rects_grayImg,  
  130.     vector <RotatedRect> & rects_closeImg,vector <RotatedRect> & rects_optimal )  
  131. {  
  132.     for (int i=0;i<rects_sImg.size() ;++ i)  
  133.     {  
  134.         for (int j=0;j<rects_grayImg.size() ; ++j)  
  135.         {  
  136.             if (calOverlap(rects_sImg[i].boundingRect() , rects_grayImg[j].boundingRect()) > 0.2)  
  137.             {  
  138.                 if(rects_sImg[i].boundingRect().width * rects_sImg[i].boundingRect().height  
  139.                     >= rects_grayImg[j].boundingRect().width * rects_grayImg[j].boundingRect().height)  
  140.                     rects_optimal.push_back(rects_sImg[i]);  
  141.                 else  
  142.                     rects_optimal.push_back(rects_grayImg[j]);    
  143.             }  
  144.         }  
  145.     }  
  146.   
  147.     if (rects_closeImg.size()<2 )  //只考虑1个,为了速度  
  148.     {  
  149.         for (int i =0;i < rects_optimal.size();++ i )  
  150.             for (int j =0;j < rects_closeImg.size();++ j)  
  151.             {             
  152.                 if ((    calOverlap(rects_optimal[i].boundingRect() , rects_closeImg[j].boundingRect()) < 0.2 &&  
  153.                     calOverlap(rects_optimal[i].boundingRect() , rects_closeImg[j].boundingRect()) > 0.05))  
  154.                 {  
  155.                     rects_optimal.push_back(rects_closeImg[j]);  
  156.                 }  
  157.             }  
  158.     }  
  159.   
  160. }  
  161.   
  162. float calOverlap(const Rect& box1,const Rect& box2)  
  163. {  
  164.     if (box1.x > box2.x+box2.width) { return 0.0; }  
  165.     if (box1.y > box2.y+box2.height) { return 0.0; }  
  166.     if (box1.x+box1.width < box2.x) { return 0.0; }  
  167.     if (box1.y+box1.height < box2.y) { return 0.0; }  
  168.     float colInt =  min(box1.x+box1.width,box2.x+box2.width) - max(box1.x, box2.x);  
  169.     float rowInt =  min(box1.y+box1.height,box2.y+box2.height) - max(box1.y,box2.y);  
  170.     float intersection = colInt * rowInt;  
  171.     float area1 = box1.width*box1.height;  
  172.     float area2 = box2.width*box2.height;  
  173.     return intersection / (area1 + area2 - intersection);  
  174. }  
  175.   
  176. void normalPosArea(Mat &intputImg, vector<RotatedRect> &rects_optimal, vector <Mat>& output_area )  
  177. {  
  178.     float r,angle;  
  179.     for (int i = 0 ;i< rects_optimal.size() ; ++i)  
  180.     {  
  181.         //旋转区域  
  182.         angle = rects_optimal[i].angle;  
  183.         r = (float)rects_optimal[i].size.width / (float) (float)rects_optimal[i].size.height;  
  184.         if(r<1)  
  185.             angle = 90 + angle;  
  186.         Mat rotmat = getRotationMatrix2D(rects_optimal[i].center , angle,1);//获得变形矩阵对象  
  187.         Mat img_rotated;  
  188.         warpAffine(intputImg ,img_rotated,rotmat, intputImg.size(),CV_INTER_CUBIC);  
  189.   
  190.         //裁剪图像  
  191.         Size rect_size = rects_optimal[i].size;  
  192.         if(r<1)  
  193.             swap(rect_size.width, rect_size.height);  
  194.         Mat  img_crop;  
  195.         getRectSubPix(img_rotated ,rect_size,rects_optimal[i].center , img_crop );  
  196.   
  197.         //用光照直方图调整所有裁剪得到的图像,使具有相同宽度和高度,适用于训练和分类  
  198.         Mat resultResized;  
  199.         resultResized.create(33,144,CV_8UC3);  
  200.         resize(img_crop , resultResized,resultResized.size() , 0,0,INTER_CUBIC);  
  201.         Mat grayResult;  
  202.         RgbConvToGray(resultResized ,grayResult);  
  203.         //blur(grayResult ,grayResult,Size(3,3));  
  204.         equalizeHist(grayResult,grayResult);  
  205.   
  206.         output_area.push_back(grayResult);  
  207.     }  
  208. }  
  209.   
  210. void svm_train(CvSVM & svmClassifier)  
  211. {  
  212.     FileStorage fs;  
  213.   
  214.     fs.open("SVM.xml" , FileStorage::READ);  
  215.     Mat SVM_TrainningData;  
  216.     Mat SVM_Classes;      
  217.   
  218.     fs["TrainingData"] >>SVM_TrainningData;  
  219.     fs["classes"] >>SVM_Classes;  
  220.     CvSVMParams SVM_params;  
  221.     SVM_params.kernel_type = CvSVM::LINEAR;  
  222.   
  223.     svmClassifier.train(SVM_TrainningData,SVM_Classes ,Mat(),Mat(),SVM_params); //SVM训练模型  
  224.     fs.release();  
  225. }  
  226.   
  227. void char_segment(const Mat & inputImg,vector <Mat>& dst_mat)//得到20*20的标准字符分割图像  
  228. {  
  229.     Mat img_threshold;  
  230.     threshold(inputImg ,img_threshold , 180,255 ,CV_THRESH_BINARY );  
  231.     Mat img_contours;  
  232.     img_threshold.copyTo(img_contours);  
  233.       
  234.     vector < vector <Point> > contours;  
  235.     findContours(img_contours ,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);  
  236.   
  237.     vector< vector <Point> > ::iterator itc = contours.begin();  
  238.     vector<RotatedRect> char_rects;  
  239.       
  240.     while( itc != contours.end())  
  241.     {  
  242.         RotatedRect minArea = minAreaRect(Mat( *itc )); //返回每个轮廓的最小有界矩形区域  
  243.   
  244.         Point2f vertices[4];  
  245.         minArea.points(vertices);  
  246.   
  247.         if(!char_verifySizes(minArea))  //判断矩形轮廓是否符合要求  
  248.         {  
  249.             itc = contours.erase(itc);  
  250.         }  
  251.         else       
  252.         {  
  253.             ++itc;   
  254.             char_rects.push_back(minArea);    
  255.   
  256.         }             
  257.     }  
  258.     char_sort(char_rects); //对字符排序  
  259.   
  260.     vector <Mat> char_mat;  
  261.     for (int i = 0; i<char_rects.size() ;++i )  
  262.     {  
  263.         char_mat.push_back( Mat(img_threshold,char_rects[i].boundingRect())) ;  
  264.   
  265.     }  
  266.       
  267.     Mat train_mat(2,3,CV_32FC1);  
  268.     int length ;  
  269.     dst_mat.resize(7);  
  270.     Point2f srcTri[3];    
  271.     Point2f dstTri[3];  
  272.   
  273.     for (int i = 0; i< char_mat.size();++i)  
  274.     {  
  275.         srcTri[0] = Point2f( 0,0 );    
  276.         srcTri[1] = Point2f( char_mat[i].cols - 1, 0 );    
  277.         srcTri[2] = Point2f( 0, char_mat[i].rows - 1 );  
  278.         length = char_mat[i].rows > char_mat[i].cols?char_mat[i].rows:char_mat[i].cols;  
  279.         dstTri[0] = Point2f( 0.0, 0.0 );    
  280.         dstTri[1] = Point2f( length, 0.0 );    
  281.         dstTri[2] = Point2f( 0.0, length );   
  282.         train_mat = getAffineTransform( srcTri, dstTri );  
  283.         dst_mat[i]=Mat::zeros(length,length,char_mat[i].type());          
  284.         warpAffine(char_mat[i],dst_mat[i],train_mat,dst_mat[i].size(),INTER_LINEAR,BORDER_CONSTANT,Scalar(0));  
  285.         resize(dst_mat[i],dst_mat[i],Size(20,20));  //尺寸调整为20*20  
  286.   
  287.     }  
  288.   
  289. }  
  290.   
  291. bool char_verifySizes(const RotatedRect & candidate)  
  292. {  
  293.     float aspect = 33.0f/20.0f;  
  294.     float charAspect = (float) candidate.size.width/ (float)candidate.size.height; //宽高比  
  295.     float error = 0.35;  
  296.     float minHeight = 11;  //最小高度11  
  297.     float maxHeight = 33;  //最大高度33  
  298.   
  299.     float minAspect = 0.20;  //考虑到数字1,最小长宽比为0.15  
  300.     float maxAspect = aspect + aspect*error;  
  301.   
  302.     if( charAspect > minAspect && charAspect < maxAspect  
  303.         && candidate.size.height >= minHeight && candidate.size.width< maxHeight) //非0像素点数、长宽比、高度需满足条件  
  304.         return true;  
  305.     else  
  306.         return false;  
  307. }  
  308.   
  309. void char_sort(vector <RotatedRect > & in_char ) //对字符区域进行排序  
  310. {  
  311.     vector <RotatedRect >  out_char;  
  312.     const int length = 7;           //7个字符  
  313.     int index[length] = {0,1,2,3,4,5,6};  
  314.     float centerX[length];  
  315.     for (int i=0;i < length ; ++ i)  
  316.     {  
  317.         centerX[i] = in_char[i].center.x;  
  318.     }  
  319.   
  320.     for (int j=0;j <length;j++) {  
  321.         for (int i=length-2;i >= j;i--)  
  322.             if (centerX[i] > centerX[i+1])  
  323.             {  
  324.                 float t=centerX[i];  
  325.                 centerX[i]=centerX[i+1];  
  326.                 centerX[i+1]=t;  
  327.   
  328.                 int tt = index[i];  
  329.                 index[i] = index[i+1];  
  330.                 index[i+1] = tt;  
  331.             }  
  332.     }  
  333.       
  334.     for(int i=0;i<length ;i++)  
  335.         out_char.push_back(in_char[(index[i])]);  
  336.   
  337.     in_char.clear();     //清空in_char  
  338.     in_char = out_char; //将排序好的字符区域向量重新赋值给in_char  
  339. }  
  340.   
  341. void features(const Mat & in , Mat & out ,int sizeData)  
  342. {  
  343.     Mat vhist = projectHistogram(in , 1); //水平直方图  
  344.     Mat hhist = projectHistogram(in , 0);  //垂直直方图  
  345.   
  346.     Mat lowData;  
  347.     resize(in , lowData ,Size(sizeData ,sizeData ));  
  348.     int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;  
  349.     out = Mat::zeros(1, numCols , CV_32F);  
  350.   
  351.     int j = 0;  
  352.     for (int i =0 ;i<vhist.cols ; ++i)  
  353.     {  
  354.         out.at<float>(j) = vhist.at<float>(i);  
  355.         j++;  
  356.     }  
  357.     for (int i=0 ; i < hhist.cols ;++i)  
  358.     {  
  359.         out.at<float>(j) = hhist.at<float>(i);  
  360.     }  
  361.     for(int x =0 ;x<lowData.rows ;++x)  
  362.     {  
  363.         for (int y =0 ;y < lowData.cols ;++ y)  
  364.         {  
  365.             out.at<float>(j) = (float)lowData.at<unsigned char>(x,y);  
  366.             j++;  
  367.         }  
  368.     }  
  369.   
  370. }  
  371.   
  372. Mat projectHistogram(const Mat& img ,int t)  //水平或垂直直方图,0为按列统计  
  373. {                                            //1为按行统计  
  374.     int sz = (t)? img.rows: img.cols;  
  375.     Mat mhist = Mat::zeros(1, sz ,CV_32F);  
  376.   
  377.     for(int j = 0 ;j < sz; j++ )  
  378.     {  
  379.         Mat data = (t)?img.row(j):img.col(j);  
  380.         mhist.at<float>(j) = countNonZero(data);  
  381.     }  
  382.   
  383.     double min,max;  
  384.     minMaxLoc(mhist , &min ,&max);  
  385.   
  386.     if(max > 0)  
  387.         mhist.convertTo(mhist ,-1,1.0f/max , 0);  
  388.   
  389.     return mhist;  
  390. }  
  391.   
  392. void ann_train(CvANN_MLP &ann ,int numCharacters, int nlayers)  
  393. {  
  394.     Mat trainData ,classes;  
  395.     FileStorage fs;  
  396.     fs.open("ann_xml.xml" , FileStorage::READ);  
  397.       
  398.     fs["TrainingData"] >>trainData;  
  399.     fs["classes"] >>classes;  
  400.   
  401.     Mat layerSizes(1,3,CV_32SC1);  
  402.     layerSizes.at<int>( 0 ) = trainData.cols;  
  403.     layerSizes.at<int>( 1 ) = nlayers; //隐藏神经元数,可设为3  
  404.     layerSizes.at<int>( 2 ) = numCharacters; //样本类数为34  
  405.     ann.create(layerSizes , CvANN_MLP::SIGMOID_SYM ,1,1);  //初始化ann  
  406.   
  407.     Mat trainClasses;  
  408.     trainClasses.create(trainData.rows , numCharacters ,CV_32FC1);  
  409.     for (int i =0;i< trainData.rows; i++)  
  410.     {  
  411.         for (int k=0 ; k< trainClasses.cols ; k++ )  
  412.         {  
  413.             if ( k == (int)classes.at<uchar> (i))  
  414.             {  
  415.                 trainClasses.at<float>(i,k)  = 1 ;  
  416.             }  
  417.             else  
  418.                 trainClasses.at<float>(i,k)  = 0;           
  419.         }         
  420.     }  
  421.   
  422.     Mat weights(1 , trainData.rows , CV_32FC1 ,Scalar::all(1) );  
  423.     ann.train( trainData ,trainClasses , weights);  
  424. }  
  425.   
  426. void classify(CvANN_MLP& ann, vector<Mat> &char_feature , vector<int> & char_result)  
  427. {  
  428.     char_result.resize(char_feature.size());  
  429.     for (int i=0;i<char_feature.size(); ++i)  
  430.     {  
  431.         Mat output(1 ,34 , CV_32FC1); //1*34矩阵  
  432.         ann.predict(char_feature[i] ,output);  
  433.         Point maxLoc;  
  434.         double maxVal;  
  435.         minMaxLoc(output , 0 ,&maxVal , 0 ,&maxLoc);  
  436.         char_result[i] =  maxLoc.x;  
  437.     }  
  438.   
  439.     std::cout<<"该车牌后6位为:";  
  440.     char  s[] = {'0','1','2','3','4','5','6','7','8','9','A','B',  
  441.         'C','D','E','F','G','H','J','K','L','M','N','P','Q',  
  442.         'R','S','T','U','V','W','X','Y','Z'};  
  443.     for (int i=1;i<char_result.size(); ++i)   //第一位是汉字,这里没实现对汉字的预测  
  444.     {  
  445.         std::cout<<s[char_result[i]];  
  446.     }  
  447.     std::cout<<'\n';  
  448. }  

四、关于SVM.xml与ann_xml.xml

SVM.xml中保存的是用于SVM训练的训练矩阵和类别矩阵数据,标签为"TrainingData"对应训练矩阵,为195*4752大小,195表示有195个训练样本,4752表示每个样本的特征向量维度,因为每个图片大小为144*33,将其转为一行,也即将每个像素值都作为一个特征值,则可得到4752个特征值,保存为1行,所使用的样本图片有75个正样本和120个负样本,保存如下:


标签为"classes"对应类别矩阵,为195*1矩阵,前75个值对应正样本为1.0,后120个值对应负样本为-1.0。

       同理,ann_xml.xml保存的是用于神经网络训练的数据,标签为"TrainingData"对应训练矩阵,为1700*65大小,1700表示训练样本数,因为车牌字符有34类,每类有50个,故总数1700,65为每个样本提取的特征向量。标签为"classes"对应类别矩阵,为1700*1大小,标记负样本数是哪一类的,训练样本取自


五、结果及分析

       勉强找到2张图片,可以完整地识别出车牌的后6为字符,效果如下。


故该系统的性能仍有待提升,不过我认为在车牌定位方面可以有所改进,采用其他更好的车牌定位算法会更好,以及可以增加神经网络的训练样本。

六、完整代码文件下载

下载链接为:http://download.csdn.NET/detail/ap1005834/9513328

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值