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

2024-02-07 16:38

本文主要是介绍《Mastering Opencv ...读书笔记系列》车牌识别(II),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 继上一篇文章后,现在要做的就是从车牌图像上使用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上试试。



1
0





文章转载自:http://blog.csdn.net/jinshengtao/article/details/17954427


查看评论
7楼  raceyan 6天前 12:28发表 [回复]
能分享下源代码?附上OCR.xml, 谢谢 邮箱854657981@qq.com
6楼  chen1955 2016-07-01 17:12发表 [回复]
楼主 为毛我这里编译有错误啊
错误 41 error C3861: “ProjectedHistogram”: 找不到标识符 e:\czx\015\015.cpp 77 1 015
错误 42 error C3861: “ProjectedHistogram”: 找不到标识符 e:\czx\015\015.cpp 78 1 015
43 IntelliSense: 未定义标识符 "ProjectedHistogram" e:\czx\015\015.cpp 77 14 015
5楼  ZaCks雪 2016-04-07 16:49发表 [回复]
楼主 为毛我这里编译有错误啊
4楼  captaineven 2016-03-10 11:13发表 [回复]
请问亲爱的作者能否给出训练的数据集和训练模型文件的链接?
3楼  z815825527 2014-04-28 12:40发表 [回复]
请问一下:训练生成OCR.xml的图片文件种类和规格分别是什么?为什么我训练出来的OCR.xml拿到main.cpp去运行会出现读入的矩阵不匹配?帮帮忙,谢谢了!
2楼  木头杭 2014-04-13 17:00发表 [回复]
同问,如果要自己找样本,这些样本有什么要求吗?谢谢!
1楼  woshicuit 2014-03-17 20:48发表 [回复] [引用] [举报]
你这些训练样本从哪儿来的,貌似网上配套的图片只有12张,根本凑不够那么多的训练样本,特别是字符训练的时候,完全没那么多样本。
Re:  木头杭 2014-04-13 17:01发表 [回复]
回复woshicuit:想问下您的问题解决没?是怎么解决的,我也在训练样本上遇到麻烦了,不知道怎么找样本。谢谢!
Re:  taotao1233 2014-03-17 21:32发表 [回复] [引用] [举报]
回复woshicuit:是不够的,所以我直接用他的xml文件了。
Re:  ZaCks雪 2016-04-07 16:50发表 [回复] [引用] [举报]
回复jinshengtao:你这个编译成功了吗

这篇关于《Mastering Opencv ...读书笔记系列》车牌识别(II)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/688311

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

opencv 滚动条

参数介绍:createTrackbar( trackbarname , "hello" , &alpha_slider ,alpha_max ,  on_trackbar )  ;在标签中显示的文字(提示滑动条的用途) TrackbarName创建的滑动条要放置窗体的名字 “hello”滑动条的取值范围从 0 到 alpha_max (最小值只能为 zero).滑动后的值存放在

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

从0到1,AI我来了- (7)AI应用-ComfyUI-II(进阶)

上篇comfyUI 入门 ,了解了TA是个啥,这篇,我们通过ComfyUI 及其相关Lora 模型,生成一些更惊艳的图片。这篇主要了解这些内容:         1、哪里获取模型?         2、实践如何画一个美女?         3、附录:               1)相关SD(稳定扩散模型的组成部分)               2)模型放置目录(重要)