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

【原文:http://blog.csdn.net/jinshengtao/article/details/17954427

继上一篇文章后,现在要做的就是从车牌图像上使用optical character recognition算法将字符提取出来。对于每一块被检测的车牌,使用带监督的神经网络机器学习算法来识别字符。

本文内容:

1.字符分割 

2.神经网络训练方法

3.使用神经网络预测字符


一、字符分割【OCR Segment】

在使用神经网络对每个字符进行预测之前,我们必须从车牌图像中扣取改字符图片,因此有如下步骤:

本文的输入图像为上一篇文章的车牌:


a.二值化车牌


b.求轮廓


c.求最小外接矩形


d.用纵横比及面积,筛选外接矩形


e.调整统一矩形大小并保存每个字符的图片【注意:分割得到顺序和车牌字符顺序无关,可能不同】



代码:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // car_plate_ann.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <cv.h>  
  6. #include <highgui.h>  
  7. #include <cvaux.h>  
  8. #include <ml.h>  
  9. #define HORIZONTAL    1  
  10. #define VERTICAL    0  
  11. using namespace std;  
  12. using namespace cv;  
  13.   
  14. //typedef struct CharSegment{  
  15. //  Mat img;  
  16. //  Rect mr;  
  17. //  CharSegment(Mat a,Rect b){  
  18. //      img=a;  
  19. //      mr=b;  
  20. //  }  
  21. //};  
  22.   
  23. bool verifySizes(Mat r){  
  24.     //Char sizes 45x77  
  25.     float aspect=45.0f/77.0f;  
  26.     float charAspect= (float)r.cols/(float)r.rows;  
  27.     float error=0.35;  
  28.     float minHeight=15;  
  29.     float maxHeight=28;  
  30.     //We have a different aspect ratio for number 1, and it can be ~0.2  
  31.     float minAspect=0.2;  
  32.     float maxAspect=aspect+aspect*error;  
  33.     //area of pixels  
  34.     float area=countNonZero(r);  
  35.     //bb area  
  36.     float bbArea=r.cols*r.rows;  
  37.     //% of pixel in area  
  38.     float percPixels=area/bbArea;  
  39.   
  40.     /*if(DEBUG) 
  41.         cout << "Aspect: "<< aspect << " ["<< minAspect << "," << maxAspect << "] "  << "Area "<< percPixels <<" Char aspect " << charAspect  << " Height char "<< r.rows << "\n";*/  
  42.     if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeight && r.rows < maxHeight)  
  43.         return true;  
  44.     else  
  45.         return false;  
  46.   
  47. }  
  48.   
  49. Mat preprocessChar(Mat in){  
  50.     //Remap image  
  51.     int h=in.rows;  
  52.     int w=in.cols;  
  53.     int charSize=20;    //统一每个字符的大小  
  54.     Mat transformMat=Mat::eye(2,3,CV_32F);  
  55.     int m=max(w,h);  
  56.     transformMat.at<float>(0,2)=m/2 - w/2;  
  57.     transformMat.at<float>(1,2)=m/2 - h/2;  
  58.   
  59.     Mat warpImage(m,m, in.type());  
  60.     warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0) );  
  61.   
  62.     Mat out;  
  63.     resize(warpImage, out, Size(charSize, charSize) );   
  64.   
  65.     return out;  
  66. }  
  67.   
  68. //create the accumulation histograms,img is a binary image, t is 水平或垂直  
  69. Mat ProjectedHistogram(Mat img, int t)  
  70. {  
  71.     int sz=(t)?img.rows:img.cols;  
  72.     Mat mhist=Mat::zeros(1,sz,CV_32F);  
  73.   
  74.     for(int j=0; j<sz; j++){  
  75.         Mat data=(t)?img.row(j):img.col(j);  
  76.         mhist.at<float>(j)=countNonZero(data);    //统计这一行或一列中,非零元素的个数,并保存到mhist中  
  77.     }  
  78.   
  79.     //Normalize histogram  
  80.     double min, max;  
  81.     minMaxLoc(mhist, &min, &max);  
  82.   
  83.     if(max>0)  
  84.         mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方图中的最大值,归一化直方图  
  85.   
  86.     return mhist;  
  87. }  
  88.   
  89. Mat getVisualHistogram(Mat *hist, int type)  
  90. {  
  91.   
  92.     int size=100;  
  93.     Mat imHist;  
  94.   
  95.   
  96.     if(type==HORIZONTAL){  
  97.         imHist.create(Size(size,hist->cols), CV_8UC3);  
  98.     }else{  
  99.         imHist.create(Size(hist->cols, size), CV_8UC3);  
  100.     }  
  101.   
  102.     imHist=Scalar(55,55,55);  
  103.   
  104.     for(int i=0;i<hist->cols;i++){  
  105.         float value=hist->at<float>(i);  
  106.         int maxval=(int)(value*size);  
  107.   
  108.         Point pt1;  
  109.         Point pt2, pt3, pt4;  
  110.   
  111.         if(type==HORIZONTAL){  
  112.             pt1.x=pt3.x=0;  
  113.             pt2.x=pt4.x=maxval;  
  114.             pt1.y=pt2.y=i;  
  115.             pt3.y=pt4.y=i+1;  
  116.   
  117.             line(imHist, pt1, pt2, CV_RGB(220,220,220),1,8,0);  
  118.             line(imHist, pt3, pt4, CV_RGB(34,34,34),1,8,0);  
  119.   
  120.             pt3.y=pt4.y=i+2;  
  121.             line(imHist, pt3, pt4, CV_RGB(44,44,44),1,8,0);  
  122.             pt3.y=pt4.y=i+3;  
  123.             line(imHist, pt3, pt4, CV_RGB(50,50,50),1,8,0);  
  124.         }else{  
  125.   
  126.             pt1.x=pt2.x=i;  
  127.             pt3.x=pt4.x=i+1;  
  128.             pt1.y=pt3.y=100;  
  129.             pt2.y=pt4.y=100-maxval;  
  130.   
  131.   
  132.             line(imHist, pt1, pt2, CV_RGB(220,220,220),1,8,0);  
  133.             line(imHist, pt3, pt4, CV_RGB(34,34,34),1,8,0);  
  134.   
  135.             pt3.x=pt4.x=i+2;  
  136.             line(imHist, pt3, pt4, CV_RGB(44,44,44),1,8,0);  
  137.             pt3.x=pt4.x=i+3;  
  138.             line(imHist, pt3, pt4, CV_RGB(50,50,50),1,8,0);  
  139.   
  140.         }  
  141.   
  142.     }  
  143.   
  144.     return imHist ;  
  145. }  
  146.   
  147. void drawVisualFeatures(Mat character, Mat hhist, Mat vhist, Mat lowData,int count){  
  148.     Mat img(121, 121, CV_8UC3, Scalar(0,0,0));  
  149.     Mat ch;  
  150.     Mat ld;  
  151.     char res[20];  
  152.   
  153.     cvtColor(character, ch, CV_GRAY2RGB);  
  154.   
  155.     resize(lowData, ld, Size(100, 100), 0, 0, INTER_NEAREST );//将ld从15*15扩大到100*100  
  156.     cvtColor(ld,ld,CV_GRAY2RGB);  
  157.   
  158.     Mat hh=getVisualHistogram(&hhist, HORIZONTAL);  
  159.     Mat hv=getVisualHistogram(&vhist, VERTICAL);  
  160.   
  161.     //Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height)  
  162.     Mat subImg=img(Rect(0,101,20,20));//ch:20*20  
  163.     ch.copyTo(subImg);  
  164.   
  165.     subImg=img(Rect(21,101,100,20));//hh:100*hist.cols  
  166.     hh.copyTo(subImg);  
  167.   
  168.     subImg=img(Rect(0,0,20,100));//hv:hist.cols*100  
  169.     hv.copyTo(subImg);  
  170.   
  171.     subImg=img(Rect(21,0,100,100));//ld:100*100  
  172.     ld.copyTo(subImg);  
  173.   
  174.     line(img, Point(0,100), Point(121,100), Scalar(0,0,255));  
  175.     line(img, Point(20,0), Point(20,121), Scalar(0,0,255));  
  176.   
  177.     sprintf(res,"hist%d.jpg",count);  
  178.     imwrite(res,img);  
  179.     //imshow("Visual Features", img);  
  180.   
  181.     cvWaitKey(0);  
  182. }  
  183.   
  184.   
  185. Mat features(Mat in, int sizeData,int count){  
  186.     //Histogram features  
  187.     Mat vhist=ProjectedHistogram(in,VERTICAL);  
  188.     Mat hhist=ProjectedHistogram(in,HORIZONTAL);  
  189.   
  190.     //Low data feature  
  191.     Mat lowData;  
  192.     resize(in, lowData, Size(sizeData, sizeData) );  
  193.   
  194.     //画出直方图  
  195.     drawVisualFeatures(in, hhist, vhist, lowData,count);  
  196.   
  197.   
  198.   
  199.     //Last 10 is the number of moments components  
  200.     int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;  
  201.   
  202.     Mat out=Mat::zeros(1,numCols,CV_32F);  
  203.     //Asign values to feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量  
  204.     int j=0;  
  205.     for(int i=0; i<vhist.cols; i++)  
  206.     {  
  207.         out.at<float>(j)=vhist.at<float>(i);  
  208.         j++;  
  209.     }  
  210.     for(int i=0; i<hhist.cols; i++)  
  211.     {  
  212.         out.at<float>(j)=hhist.at<float>(i);  
  213.         j++;  
  214.     }  
  215.     for(int x=0; x<lowData.cols; x++)  
  216.     {  
  217.         for(int y=0; y<lowData.rows; y++){  
  218.             out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);  
  219.             j++;  
  220.         }  
  221.     }  
  222.     //if(DEBUG)  
  223.     //  cout << out << "\n===========================================\n";  
  224.     return out;  
  225. }  
  226.   
  227. int _tmain(int argc, _TCHAR* argv[])  
  228. {  
  229.     Mat input = imread("haha_1.jpg",CV_LOAD_IMAGE_GRAYSCALE);  
  230.     char res[20];  
  231.     int i = 0;  
  232.     //vector<CharSegment> output;  
  233.   
  234.     //Threshold input image  
  235.     Mat img_threshold;  
  236.     threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV);  
  237.   
  238.     Mat img_contours;  
  239.     img_threshold.copyTo(img_contours);  
  240.     //Find contours of possibles characters  
  241.     vector< vector< Point> > contours;  
  242.     findContours(img_contours,  
  243.         contours, // a vector of contours  
  244.         CV_RETR_EXTERNAL, // retrieve the external contours  
  245.         CV_CHAIN_APPROX_NONE); // all pixels of each contours  
  246.   
  247.     // Draw blue contours on a white image  
  248.     cv::Mat result;  
  249.     input.copyTo(result);  
  250.     cvtColor(result, result, CV_GRAY2RGB);  
  251.     //cv::drawContours(result,contours,  
  252.     //  -1, // draw all contours  
  253.     //  cv::Scalar(0,0,255), // in blue  
  254.     //  1); // with a thickness of 1  
  255.       
  256.     //Start to iterate to each contour founded  
  257.     vector<vector<Point> >::iterator itc= contours.begin();  
  258.   
  259.     //Remove patch that are no inside limits of aspect ratio and area.      
  260.     while (itc!=contours.end()) {  
  261.   
  262.         //Create bounding rect of object  
  263.         Rect mr= boundingRect(Mat(*itc));  
  264.         //rectangle(result, mr, Scalar(255,0,0),2);  
  265.         //Crop image  
  266.         Mat auxRoi(img_threshold, mr);  
  267.         if(verifySizes(auxRoi)){  
  268.             auxRoi=preprocessChar(auxRoi);  
  269.             //output.push_back(CharSegment(auxRoi, mr));  
  270.               
  271.             //保存每个字符图片  
  272.             sprintf(res,"train_data_%d.jpg",i);  
  273.             i++;  
  274.             imwrite(res,auxRoi);  
  275.             rectangle(result, mr, Scalar(0,0,255),2);  
  276.   
  277.   
  278.             //对每一个小方块,提取直方图特征  
  279.             Mat f=features(auxRoi,15,i);  
  280.         }  
  281.         ++itc;  
  282.     }  
  283.   
  284.     imwrite("result1.jpg",result);  
  285.     imshow("car_plate",result);  
  286.     waitKey(0);  
  287.     return 0;  
  288. }  

图片显示可以自己边注释边显示,另外提前给出了第二部分的累计水平垂直直方图和低分辨率采样的方法,并把这种特征的图像保存下来了。


二、神经网络训练

1.多层感知机简介:

多层感知机结构:【隐层数量为1层或多层,实际上自从引入了深度学习后,才有多层】


其中,每个神经元结构如下:


每个神经元都是相似的且每个神经元都有自己的判定边界,有多个输入和多个输出。不同权重的输入结合激励函数得到不同的输出。常见的激励函数有S型、高斯型、上图的hadrlim型。单层的单个神经元可以将输入向量分为两类,而一个有S个神经元的感知机,可以将输入向量分为2^S类


2.获取训练数据

和上一篇训练SVM所使用的特征不同,现在使用每个字符的累计直方图和低分辨率采样图像构成的高维向量作为训练神经网络的特征。训练的样本矩阵P为N*M,其中N(行)代表各个样本图片的融合特征,M(列)为类别。从书中给的已经训练好的orc.xml看,N有675行,M有30列,30列代表西班牙车牌有30种字符0-9和20个英文字母组成,675是这么来的,比如字符0有35张图片样本,对应产生35行高维向量,字符1有40张样本图片,对应产生40行高维向量,然后按照不同分辨率5*5、10*10、15*15、20*20采样【书中ocr.xml只有675,只采用5*5分辨率】。矩阵P实际上是对每一种高维向量的类别标注:


  在Opencv中使用多层感知机需要配置training data矩阵、classes矩阵、隐层神经元数量。其中,训练数据矩阵和列别标识矩阵均从ocr.xml文件获取【下文会介绍】,这里只采用单隐层,包含10个神经元,输入层为675行,输出层为30行。

  计算ocr.xml文件具体步骤:

a.将上一步分割得到的每个字符进行人工分类【可放在不同目录下】,比如最终字符0有35张图片,字符a有30张图片并定义数组【这些数字之和为675】:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. const int numFilesChars[]={35, 40, 42, 41, 42, 33, 30, 31, 49, 44, 30, 24, 21, 20, 34, 9, 10, 3, 11, 3, 15, 4, 9, 12, 10, 21, 18, 8, 15, 7};  
b.读取某个字符目录下的一张图片,提取累计直方图特征和不同低分辨率图像,具体如下:
(1)统计水平、垂直方向直方图,比如水平直方图,扫描图像每一行,统计每行非零元素的个数,这样就构成1*row矩阵,然后用该矩阵的最大值,规范化该矩阵。
(2)使用resize函数,按照不同分辨率得到图像矩阵
(3)将垂直直方图,水平直方图,低分辨率图像【按行读取】,都存进一个1*(vcol+hcol+h*w)的矩阵
(4)将上述矩阵连同标记一起写入xml文件中
累计直方图及低分辨率图像效果如下:【左下角为字符图像原始大小20*20,由上角为低分辨率采样后放大的图像100*100,右下角为水平直方图,左上角为垂直直方图】


具体训练代码为:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Main entry code OpenCV  
  2.   
  3. #include <cv.h>  
  4. #include <highgui.h>  
  5. #include <cvaux.h>  
  6.   
  7. #include <iostream>  
  8. #include <vector>  
  9. #define HORIZONTAL    1  
  10. #define VERTICAL    0  
  11. using namespace std;  
  12. using namespace cv;  
  13.   
  14. //西班牙车牌共30种字符,下面为每个字符的图片个数【没给,需人工挑选】  
  15. const int numFilesChars[]={35, 40, 42, 41, 42, 33, 30, 31, 49, 44, 30, 24, 21, 20, 34, 9, 10, 3, 11, 3, 15, 4, 9, 12, 10, 21, 18, 8, 15, 7};  
  16. const char strCharacters[] = {'0','1','2','3','4','5','6','7','8','9','B''C''D''F''G''H''J''K''L''M''N''P''R''S''T''V''W''X''Y''Z'};  
  17. const int numCharacters=30;  
  18.   
  19.   
  20. Mat features(Mat in, int sizeData,int count){  
  21.     //Histogram features  
  22.     Mat vhist=ProjectedHistogram(in,VERTICAL);  
  23.     Mat hhist=ProjectedHistogram(in,HORIZONTAL);  
  24.   
  25.     //Low data feature  
  26.     Mat lowData;  
  27.     resize(in, lowData, Size(sizeData, sizeData) );  
  28.   
  29.     //Last 10 is the number of moments components  
  30.     int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;  
  31.   
  32.     Mat out=Mat::zeros(1,numCols,CV_32F);  
  33.     //Asign values to feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量  
  34.     int j=0;  
  35.     for(int i=0; i<vhist.cols; i++)  
  36.     {  
  37.         out.at<float>(j)=vhist.at<float>(i);  
  38.         j++;  
  39.     }  
  40.     for(int i=0; i<hhist.cols; i++)  
  41.     {  
  42.         out.at<float>(j)=hhist.at<float>(i);  
  43.         j++;  
  44.     }  
  45.     for(int x=0; x<lowData.cols; x++)  
  46.     {  
  47.         for(int y=0; y<lowData.rows; y++){  
  48.             out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);  
  49.             j++;  
  50.         }  
  51.     }  
  52.     //if(DEBUG)  
  53.     //  cout << out << "\n===========================================\n";  
  54.     return out;  
  55. }  
  56.   
  57. int main ( int argc, char** argv )  
  58. {  
  59.     cout << "OpenCV Training OCR Automatic Number Plate Recognition\n";  
  60.     cout << "\n";  
  61.   
  62.     char* path;  
  63.       
  64.     //Check if user specify image to process  
  65.     if(argc >= 1 )  
  66.     {  
  67.         path= argv[1];  
  68.       
  69.     }else{  
  70.         cout << "Usage:\n" << argv[0] << " <path to chars folders files> \n";  
  71.         return 0;  
  72.     }          
  73.   
  74.   
  75.   
  76.   
  77.   
  78.   
  79.     Mat classes;  
  80.     Mat trainingDataf5;  
  81.     Mat trainingDataf10;  
  82.     Mat trainingDataf15;  
  83.     Mat trainingDataf20;  
  84.   
  85.     vector<int> trainingLabels;  
  86.     OCR ocr;  
  87.   
  88.     for(int i=0; i< numCharacters; i++)  
  89.     {  
  90.         int numFiles=numFilesChars[i];  
  91.         for(int j=0; j< numFiles; j++){  
  92.             cout << "Character "<< strCharacters[i] << " file: " << j << "\n";  
  93.             stringstream ss(stringstream::in | stringstream::out);  
  94.             ss << path << strCharacters[i] << "/" << j << ".jpg";  
  95.             Mat img=imread(ss.str(), 0);  
  96.             Mat f5=features(img, 5);  
  97.             Mat f10=features(img, 10);  
  98.             Mat f15=features(img, 15);  
  99.             Mat f20=features(img, 20);  
  100.   
  101.             trainingDataf5.push_back(f5);  
  102.             trainingDataf10.push_back(f10);  
  103.             trainingDataf15.push_back(f15);  
  104.             trainingDataf20.push_back(f20);  
  105.             trainingLabels.push_back(i);            //每一幅字符图片所对应的字符类别索引下标  
  106.         }  
  107.     }  
  108.   
  109.       
  110.     trainingDataf5.convertTo(trainingDataf5, CV_32FC1);  
  111.     trainingDataf10.convertTo(trainingDataf10, CV_32FC1);  
  112.     trainingDataf15.convertTo(trainingDataf15, CV_32FC1);  
  113.     trainingDataf20.convertTo(trainingDataf20, CV_32FC1);  
  114.     Mat(trainingLabels).copyTo(classes);  
  115.   
  116.     FileStorage fs("OCR.xml", FileStorage::WRITE);  
  117.     fs << "TrainingDataF5" << trainingDataf5;  
  118.     fs << "TrainingDataF10" << trainingDataf10;  
  119.     fs << "TrainingDataF15" << trainingDataf15;  
  120.     fs << "TrainingDataF20" << trainingDataf20;  
  121.     fs << "classes" << classes;  
  122.     fs.release();  
  123.   
  124.     return 0;  
  125. }  

三、使用神经网络检测字符
a.读取一张车牌图像
b.配置神经网络参数,并使用xml文件训练神经网络【参数配置上述已经说过了】
c.提取该车牌图像的累计直方图和低分辨率图像特征矩阵
d.将该特征矩阵作为神经网络输入,经网络计算,得到预测结果【字符索引】
e.按照每个字符图像的相对位置,进行字符重新排序
f.得到最终字符【和书中不同的是,我的输入图像是车牌而不是整幅图像,因此绝对坐标是不同的,但字符间的相对位置还是对的,只是不能在车牌照片上显示数字而已,我直接答应到控制台上】


具体代码:

又用到了车牌类,这里面有车牌字符相对位置调整的函数,都给出来吧:

Plate.h:

[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:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /***************************************************************************** 
  2. *   Number Plate Recognition using SVM and Neural Networks 
  3. ****************************************************************************** 
  4. *   by David Mill醤 Escriv? 5th Dec 2012 
  5. *   http://blog.damiles.com 
  6. ****************************************************************************** 
  7. *   Ch5 of the book "Mastering OpenCV with Practical Computer Vision Projects" 
  8. *   Copyright Packt Publishing 2012. 
  9. *   http://www.packtpub.com/cool-projects-with-opencv/book 
  10. *****************************************************************************/  
  11.   
  12. #include "Plate.h"  
  13.   
  14. Plate::Plate(){  
  15. }  
  16.   
  17. Plate::Plate(Mat img, Rect pos){  
  18.     plateImg=img;  
  19.     position=pos;  
  20. }  
  21.   
  22. string Plate::str(){  
  23.     string result="";  
  24.     //Order numbers  
  25.     vector<int> orderIndex;  
  26.     vector<int> xpositions;  
  27.     for(int i=0; i< charsPos.size(); i++){  
  28.         orderIndex.push_back(i);  
  29.         xpositions.push_back(charsPos[i].x);  
  30.     }  
  31.     float min=xpositions[0];  
  32.     int minIdx=0;  
  33.     for(int i=0; i< xpositions.size(); i++){  
  34.         min=xpositions[i];  
  35.         minIdx=i;  
  36.         for(int j=i; j<xpositions.size(); j++){  
  37.             if(xpositions[j]<min){  
  38.                 min=xpositions[j];  
  39.                 minIdx=j;  
  40.             }  
  41.         }  
  42.         int aux_i=orderIndex[i];  
  43.         int aux_min=orderIndex[minIdx];  
  44.         orderIndex[i]=aux_min;  
  45.         orderIndex[minIdx]=aux_i;  
  46.           
  47.         float aux_xi=xpositions[i];  
  48.         float aux_xmin=xpositions[minIdx];  
  49.         xpositions[i]=aux_xmin;  
  50.         xpositions[minIdx]=aux_xi;  
  51.     }  
  52.     for(int i=0; i<orderIndex.size(); i++){  
  53.         result=result+chars[orderIndex[i]];  
  54.     }  
  55.     return result;  
  56. }  

主要处理的函数:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // car_plate_classify.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <cv.h>  
  6. #include <highgui.h>  
  7. #include <cvaux.h>  
  8. #include <ml.h>  
  9.   
  10. #include <iostream>  
  11. #include <vector>  
  12. #include "Plate.h"  
  13. #define HORIZONTAL    1  
  14. #define VERTICAL    0  
  15. using namespace std;  
  16. using namespace cv;  
  17.   
  18. CvANN_MLP  ann;  
  19. const char strCharacters[] = {'0','1','2','3','4','5','6','7','8','9','B''C''D''F''G''H''J''K''L''M''N''P''R''S''T''V''W''X''Y''Z'};  
  20. const int numCharacters=30;  
  21.   
  22. bool verifySizes(Mat r){  
  23.     //Char sizes 45x77  
  24.     float aspect=45.0f/77.0f;  
  25.     float charAspect= (float)r.cols/(float)r.rows;  
  26.     float error=0.35;  
  27.     float minHeight=15;  
  28.     float maxHeight=28;  
  29.     //We have a different aspect ratio for number 1, and it can be ~0.2  
  30.     float minAspect=0.2;  
  31.     float maxAspect=aspect+aspect*error;  
  32.     //area of pixels  
  33.     float area=countNonZero(r);  
  34.     //bb area  
  35.     float bbArea=r.cols*r.rows;  
  36.     //% of pixel in area  
  37.     float percPixels=area/bbArea;  
  38.   
  39.     /*if(DEBUG) 
  40.     cout << "Aspect: "<< aspect << " ["<< minAspect << "," << maxAspect << "] "  << "Area "<< percPixels <<" Char aspect " << charAspect  << " Height char "<< r.rows << "\n";*/  
  41.     if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeight && r.rows < maxHeight)  
  42.         return true;  
  43.     else  
  44.         return false;  
  45.   
  46. }  
  47.   
  48. Mat preprocessChar(Mat in){  
  49.     //Remap image  
  50.     int h=in.rows;  
  51.     int w=in.cols;  
  52.     int charSize=20;    //统一每个字符的大小  
  53.     Mat transformMat=Mat::eye(2,3,CV_32F);  
  54.     int m=max(w,h);  
  55.     transformMat.at<float>(0,2)=m/2 - w/2;  
  56.     transformMat.at<float>(1,2)=m/2 - h/2;  
  57.   
  58.     Mat warpImage(m,m, in.type());  
  59.     warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0) );  
  60.   
  61.     Mat out;  
  62.     resize(warpImage, out, Size(charSize, charSize) );   
  63.   
  64.     return out;  
  65. }  
  66.   
  67. //create the accumulation histograms,img is a binary image, t is 水平或垂直  
  68. Mat ProjectedHistogram(Mat img, int t)  
  69. {  
  70.     int sz=(t)?img.rows:img.cols;  
  71.     Mat mhist=Mat::zeros(1,sz,CV_32F);  
  72.   
  73.     for(int j=0; j<sz; j++){  
  74.         Mat data=(t)?img.row(j):img.col(j);  
  75.         mhist.at<float>(j)=countNonZero(data);    //统计这一行或一列中,非零元素的个数,并保存到mhist中  
  76.     }  
  77.   
  78.     //Normalize histogram  
  79.     double min, max;  
  80.     minMaxLoc(mhist, &min, &max);  
  81.   
  82.     if(max>0)  
  83.         mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方图中的最大值,归一化直方图  
  84.   
  85.     return mhist;  
  86. }  
  87.   
  88. Mat features(Mat in, int sizeData){  
  89.     //Histogram features  
  90.     Mat vhist=ProjectedHistogram(in,VERTICAL);  
  91.     Mat hhist=ProjectedHistogram(in,HORIZONTAL);  
  92.   
  93.     //Low data feature  
  94.     Mat lowData;  
  95.     resize(in, lowData, Size(sizeData, sizeData) );  
  96.   
  97.     //Last 10 is the number of moments components  
  98.     int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;  
  99.   
  100.     Mat out=Mat::zeros(1,numCols,CV_32F);  
  101.     //Asign values to feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量  
  102.     int j=0;  
  103.     for(int i=0; i<vhist.cols; i++)  
  104.     {  
  105.         out.at<float>(j)=vhist.at<float>(i);  
  106.         j++;  
  107.     }  
  108.     for(int i=0; i<hhist.cols; i++)  
  109.     {  
  110.         out.at<float>(j)=hhist.at<float>(i);  
  111.         j++;  
  112.     }  
  113.     for(int x=0; x<lowData.cols; x++)  
  114.     {  
  115.         for(int y=0; y<lowData.rows; y++){  
  116.             out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);  
  117.             j++;  
  118.         }  
  119.     }  
  120.       
  121.     return out;  
  122. }  
  123.   
  124.   
  125. int classify(Mat f){  
  126.     int result=-1;  
  127.     Mat output(1, 30, CV_32FC1); //西班牙车牌只有30种字符  
  128.     ann.predict(f, output);  
  129.     Point maxLoc;  
  130.     double maxVal;  
  131.     minMaxLoc(output, 0, &maxVal, 0, &maxLoc);  
  132.     //We need know where in output is the max val, the x (cols) is the class.  
  133.   
  134.     return maxLoc.x;  
  135. }  
  136.   
  137. void train(Mat TrainData, Mat classes, int nlayers){  
  138.     Mat layers(1,3,CV_32SC1);  
  139.     layers.at<int>(0)= TrainData.cols;  
  140.     layers.at<int>(1)= nlayers;  
  141.     layers.at<int>(2)= 30;  
  142.     ann.create(layers, CvANN_MLP::SIGMOID_SYM, 1, 1);  
  143.   
  144.     //Prepare trainClases  
  145.     //Create a mat with n trained data by m classes  
  146.     Mat trainClasses;  
  147.     trainClasses.create( TrainData.rows, 30, CV_32FC1 );  
  148.     forint i = 0; i <  trainClasses.rows; i++ )  
  149.     {  
  150.         forint k = 0; k < trainClasses.cols; k++ )  
  151.         {  
  152.             //If class of data i is same than a k class  
  153.             if( k == classes.at<int>(i) )  
  154.                 trainClasses.at<float>(i,k) = 1;  
  155.             else  
  156.                 trainClasses.at<float>(i,k) = 0;  
  157.         }  
  158.     }  
  159.     Mat weights( 1, TrainData.rows, CV_32FC1, Scalar::all(1) );  
  160.   
  161.     //Learn classifier  
  162.     ann.train( TrainData, trainClasses, weights );  
  163. }  
  164.   
  165. int _tmain(int argc, _TCHAR* argv[])  
  166. {  
  167.     Mat input = imread("test.jpg",CV_LOAD_IMAGE_GRAYSCALE);  
  168.     Plate mplate;  
  169.     //Read file storage.  
  170.     FileStorage fs;  
  171.     fs.open("OCR.xml", FileStorage::READ);  
  172.     Mat TrainingData;  
  173.     Mat Classes;  
  174.     fs["TrainingDataF15"] >> TrainingData;  
  175.     fs["classes"] >> Classes;  
  176.     //训练神经网络  
  177.     train(TrainingData, Classes, 10);  
  178.   
  179. //dealing image and save each character image into vector<CharSegment>  
  180.     //Threshold input image  
  181.     Mat img_threshold;  
  182.     threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV);  
  183.   
  184.     Mat img_contours;  
  185.     img_threshold.copyTo(img_contours);  
  186.     //Find contours of possibles characters  
  187.     vector< vector< Point> > contours;  
  188.     findContours(img_contours,  
  189.         contours, // a vector of contours  
  190.         CV_RETR_EXTERNAL, // retrieve the external contours  
  191.         CV_CHAIN_APPROX_NONE); // all pixels of each contours  
  192.     //Start to iterate to each contour founded  
  193.     vector<vector<Point> >::iterator itc= contours.begin();  
  194.   
  195.     //Remove patch that are no inside limits of aspect ratio and area.      
  196.     while (itc!=contours.end()) {  
  197.   
  198.         //Create bounding rect of object  
  199.         Rect mr= boundingRect(Mat(*itc));  
  200.         //rectangle(result, mr, Scalar(255,0,0),2);  
  201.         //Crop image  
  202.         Mat auxRoi(img_threshold, mr);  
  203.         if(verifySizes(auxRoi)){  
  204.             auxRoi=preprocessChar(auxRoi);  
  205.   
  206.             //对每一个小方块,提取直方图特征  
  207.             Mat f=features(auxRoi,15);  
  208.             //For each segment feature Classify  
  209.             int character=classify(f);  
  210.             mplate.chars.push_back(strCharacters[character]);  
  211.             mplate.charsPos.push_back(mr);  
  212.             //printf("%c ",strCharacters[character]);  
  213.         }  
  214.         ++itc;  
  215.     }  
  216.     string licensePlate=mplate.str();  
  217.     cout<<licensePlate<<endl;  
  218.       
  219.     return 0;  
  220. }  


这边运行时间略长,大概10s以下吧。这就是android不能做太多图像处理的原因,运行速度不给力啊。

上上后面做的评估是对隐层神经元数量和不同分辨率的一种统计,没多大花头,以后要用再看吧。而且车牌识别已经做烂了,没什么动力了~~

好吧,下一篇尝试将车牌检测与识别都移植到android上试试。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值