繼上一篇文章後,現在要做的就是從車牌圖像上使用optical character recognition算法將字符提取出來。對於每一塊被檢測的車牌,使用帶監督的神經網絡機器學習算法來識別字符。
本文內容:
1.字符分割
2.神經網絡訓練方法
3.使用神經網絡預測字符
一、字符分割【OCR Segment】
在使用神經網絡對每個字符進行預測之前,我們必須從車牌圖像中扣取改字符圖片,因此有如下步驟:
本文的輸入圖像爲上一篇文章的車牌:
a.二值化車牌
b.求輪廓
c.求最小外接矩形
d.用縱橫比及面積,篩選外接矩形
e.調整統一矩形大小並保存每個字符的圖片【注意:分割得到順序和車牌字符順序無關,可能不同】
代碼:
- // car_plate_ann.cpp : 定義控制檯應用程序的入口點。
- //
- #include "stdafx.h"
- #include <cv.h>
- #include <highgui.h>
- #include <cvaux.h>
- #include <ml.h>
- #define HORIZONTAL 1
- #define VERTICAL 0
- using namespace std;
- using namespace cv;
- //typedef struct CharSegment{
- // Mat img;
- // Rect mr;
- // CharSegment(Mat a,Rect b){
- // img=a;
- // mr=b;
- // }
- //};
- bool verifySizes(Mat r){
- //Char sizes 45x77
- float aspect=45.0f/77.0f;
- float charAspect= (float)r.cols/(float)r.rows;
- float error=0.35;
- float minHeight=15;
- float maxHeight=28;
- //We have a different aspect ratio for number 1, and it can be ~0.2
- float minAspect=0.2;
- float maxAspect=aspect+aspect*error;
- //area of pixels
- float area=countNonZero(r);
- //bb area
- float bbArea=r.cols*r.rows;
- //% of pixel in area
- float percPixels=area/bbArea;
- /*if(DEBUG)
- cout << "Aspect: "<< aspect << " ["<< minAspect << "," << maxAspect << "] " << "Area "<< percPixels <<" Char aspect " << charAspect << " Height char "<< r.rows << "\n";*/
- if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeight && r.rows < maxHeight)
- return true;
- else
- return false;
- }
- Mat preprocessChar(Mat in){
- //Remap image
- int h=in.rows;
- int w=in.cols;
- int charSize=20; //統一每個字符的大小
- Mat transformMat=Mat::eye(2,3,CV_32F);
- int m=max(w,h);
- transformMat.at<float>(0,2)=m/2 - w/2;
- transformMat.at<float>(1,2)=m/2 - h/2;
- Mat warpImage(m,m, in.type());
- warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0) );
- Mat out;
- resize(warpImage, out, Size(charSize, charSize) );
- return out;
- }
- //create the accumulation histograms,img is a binary image, t is 水平或垂直
- Mat ProjectedHistogram(Mat img, int t)
- {
- int sz=(t)?img.rows:img.cols;
- Mat mhist=Mat::zeros(1,sz,CV_32F);
- for(int j=0; j<sz; j++){
- Mat data=(t)?img.row(j):img.col(j);
- mhist.at<float>(j)=countNonZero(data); //統計這一行或一列中,非零元素的個數,並保存到mhist中
- }
- //Normalize histogram
- double min, max;
- minMaxLoc(mhist, &min, &max);
- if(max>0)
- mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方圖中的最大值,歸一化直方圖
- return mhist;
- }
- Mat getVisualHistogram(Mat *hist, int type)
- {
- int size=100;
- Mat imHist;
- if(type==HORIZONTAL){
- imHist.create(Size(size,hist->cols), CV_8UC3);
- }else{
- imHist.create(Size(hist->cols, size), CV_8UC3);
- }
- imHist=Scalar(55,55,55);
- for(int i=0;i<hist->cols;i++){
- float value=hist->at<float>(i);
- int maxval=(int)(value*size);
- Point pt1;
- Point pt2, pt3, pt4;
- if(type==HORIZONTAL){
- pt1.x=pt3.x=0;
- pt2.x=pt4.x=maxval;
- pt1.y=pt2.y=i;
- pt3.y=pt4.y=i+1;
- line(imHist, pt1, pt2, CV_RGB(220,220,220),1,8,0);
- line(imHist, pt3, pt4, CV_RGB(34,34,34),1,8,0);
- pt3.y=pt4.y=i+2;
- line(imHist, pt3, pt4, CV_RGB(44,44,44),1,8,0);
- pt3.y=pt4.y=i+3;
- line(imHist, pt3, pt4, CV_RGB(50,50,50),1,8,0);
- }else{
- pt1.x=pt2.x=i;
- pt3.x=pt4.x=i+1;
- pt1.y=pt3.y=100;
- pt2.y=pt4.y=100-maxval;
- line(imHist, pt1, pt2, CV_RGB(220,220,220),1,8,0);
- line(imHist, pt3, pt4, CV_RGB(34,34,34),1,8,0);
- pt3.x=pt4.x=i+2;
- line(imHist, pt3, pt4, CV_RGB(44,44,44),1,8,0);
- pt3.x=pt4.x=i+3;
- line(imHist, pt3, pt4, CV_RGB(50,50,50),1,8,0);
- }
- }
- return imHist ;
- }
- void drawVisualFeatures(Mat character, Mat hhist, Mat vhist, Mat lowData,int count){
- Mat img(121, 121, CV_8UC3, Scalar(0,0,0));
- Mat ch;
- Mat ld;
- char res[20];
- cvtColor(character, ch, CV_GRAY2RGB);
- resize(lowData, ld, Size(100, 100), 0, 0, INTER_NEAREST );//將ld從15*15擴大到100*100
- cvtColor(ld,ld,CV_GRAY2RGB);
- Mat hh=getVisualHistogram(&hhist, HORIZONTAL);
- Mat hv=getVisualHistogram(&vhist, VERTICAL);
- //Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height)
- Mat subImg=img(Rect(0,101,20,20));//ch:20*20
- ch.copyTo(subImg);
- subImg=img(Rect(21,101,100,20));//hh:100*hist.cols
- hh.copyTo(subImg);
- subImg=img(Rect(0,0,20,100));//hv:hist.cols*100
- hv.copyTo(subImg);
- subImg=img(Rect(21,0,100,100));//ld:100*100
- ld.copyTo(subImg);
- line(img, Point(0,100), Point(121,100), Scalar(0,0,255));
- line(img, Point(20,0), Point(20,121), Scalar(0,0,255));
- sprintf(res,"hist%d.jpg",count);
- imwrite(res,img);
- //imshow("Visual Features", img);
- cvWaitKey(0);
- }
- Mat features(Mat in, int sizeData,int count){
- //Histogram features
- Mat vhist=ProjectedHistogram(in,VERTICAL);
- Mat hhist=ProjectedHistogram(in,HORIZONTAL);
- //Low data feature
- Mat lowData;
- resize(in, lowData, Size(sizeData, sizeData) );
- //畫出直方圖
- drawVisualFeatures(in, hhist, vhist, lowData,count);
- //Last 10 is the number of moments components
- int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;
- Mat out=Mat::zeros(1,numCols,CV_32F);
- //Asign values to feature,ANN的樣本特徵爲水平、垂直直方圖和低分辨率圖像所組成的矢量
- int j=0;
- for(int i=0; i<vhist.cols; i++)
- {
- out.at<float>(j)=vhist.at<float>(i);
- j++;
- }
- for(int i=0; i<hhist.cols; i++)
- {
- out.at<float>(j)=hhist.at<float>(i);
- j++;
- }
- for(int x=0; x<lowData.cols; x++)
- {
- for(int y=0; y<lowData.rows; y++){
- out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);
- j++;
- }
- }
- //if(DEBUG)
- // cout << out << "\n===========================================\n";
- return out;
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- Mat input = imread("haha_1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
- char res[20];
- int i = 0;
- //vector<CharSegment> output;
- //Threshold input image
- Mat img_threshold;
- threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV);
- Mat img_contours;
- img_threshold.copyTo(img_contours);
- //Find contours of possibles characters
- vector< vector< Point> > contours;
- findContours(img_contours,
- contours, // a vector of contours
- CV_RETR_EXTERNAL, // retrieve the external contours
- CV_CHAIN_APPROX_NONE); // all pixels of each contours
- // Draw blue contours on a white image
- cv::Mat result;
- input.copyTo(result);
- cvtColor(result, result, CV_GRAY2RGB);
- //cv::drawContours(result,contours,
- // -1, // draw all contours
- // cv::Scalar(0,0,255), // in blue
- // 1); // with a thickness of 1
- //Start to iterate to each contour founded
- vector<vector<Point> >::iterator itc= contours.begin();
- //Remove patch that are no inside limits of aspect ratio and area.
- while (itc!=contours.end()) {
- //Create bounding rect of object
- Rect mr= boundingRect(Mat(*itc));
- //rectangle(result, mr, Scalar(255,0,0),2);
- //Crop image
- Mat auxRoi(img_threshold, mr);
- if(verifySizes(auxRoi)){
- auxRoi=preprocessChar(auxRoi);
- //output.push_back(CharSegment(auxRoi, mr));
- //保存每個字符圖片
- sprintf(res,"train_data_%d.jpg",i);
- i++;
- imwrite(res,auxRoi);
- rectangle(result, mr, Scalar(0,0,255),2);
- //對每一個小方塊,提取直方圖特徵
- Mat f=features(auxRoi,15,i);
- }
- ++itc;
- }
- imwrite("result1.jpg",result);
- imshow("car_plate",result);
- waitKey(0);
- return 0;
- }
圖片顯示可以自己邊註釋邊顯示,另外提前給出了第二部分的累計水平垂直直方圖和低分辨率採樣的方法,並把這種特徵的圖像保存下來了。
二、神經網絡訓練
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】:
- 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};
(1)統計水平、垂直方向直方圖,比如水平直方圖,掃描圖像每一行,統計每行非零元素的個數,這樣就構成1*row矩陣,然後用該矩陣的最大值,規範化該矩陣。
(2)使用resize函數,按照不同分辨率得到圖像矩陣
(3)將垂直直方圖,水平直方圖,低分辨率圖像【按行讀取】,都存進一個1*(vcol+hcol+h*w)的矩陣
(4)將上述矩陣連同標記一起寫入xml文件中
累計直方圖及低分辨率圖像效果如下:【左下角爲字符圖像原始大小20*20,由上角爲低分辨率採樣後放大的圖像100*100,右下角爲水平直方圖,左上角爲垂直直方圖】
具體訓練代碼爲:
- // Main entry code OpenCV
- #include <cv.h>
- #include <highgui.h>
- #include <cvaux.h>
- #include <iostream>
- #include <vector>
- #define HORIZONTAL 1
- #define VERTICAL 0
- using namespace std;
- using namespace cv;
- //西班牙車牌共30種字符,下面爲每個字符的圖片個數【沒給,需人工挑選】
- 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};
- 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'};
- const int numCharacters=30;
- Mat features(Mat in, int sizeData,int count){
- //Histogram features
- Mat vhist=ProjectedHistogram(in,VERTICAL);
- Mat hhist=ProjectedHistogram(in,HORIZONTAL);
- //Low data feature
- Mat lowData;
- resize(in, lowData, Size(sizeData, sizeData) );
- //Last 10 is the number of moments components
- int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;
- Mat out=Mat::zeros(1,numCols,CV_32F);
- //Asign values to feature,ANN的樣本特徵爲水平、垂直直方圖和低分辨率圖像所組成的矢量
- int j=0;
- for(int i=0; i<vhist.cols; i++)
- {
- out.at<float>(j)=vhist.at<float>(i);
- j++;
- }
- for(int i=0; i<hhist.cols; i++)
- {
- out.at<float>(j)=hhist.at<float>(i);
- j++;
- }
- for(int x=0; x<lowData.cols; x++)
- {
- for(int y=0; y<lowData.rows; y++){
- out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);
- j++;
- }
- }
- //if(DEBUG)
- // cout << out << "\n===========================================\n";
- return out;
- }
- int main ( int argc, char** argv )
- {
- cout << "OpenCV Training OCR Automatic Number Plate Recognition\n";
- cout << "\n";
- char* path;
- //Check if user specify image to process
- if(argc >= 1 )
- {
- path= argv[1];
- }else{
- cout << "Usage:\n" << argv[0] << " <path to chars folders files> \n";
- return 0;
- }
- Mat classes;
- Mat trainingDataf5;
- Mat trainingDataf10;
- Mat trainingDataf15;
- Mat trainingDataf20;
- vector<int> trainingLabels;
- OCR ocr;
- for(int i=0; i< numCharacters; i++)
- {
- int numFiles=numFilesChars[i];
- for(int j=0; j< numFiles; j++){
- cout << "Character "<< strCharacters[i] << " file: " << j << "\n";
- stringstream ss(stringstream::in | stringstream::out);
- ss << path << strCharacters[i] << "/" << j << ".jpg";
- Mat img=imread(ss.str(), 0);
- Mat f5=features(img, 5);
- Mat f10=features(img, 10);
- Mat f15=features(img, 15);
- Mat f20=features(img, 20);
- trainingDataf5.push_back(f5);
- trainingDataf10.push_back(f10);
- trainingDataf15.push_back(f15);
- trainingDataf20.push_back(f20);
- trainingLabels.push_back(i); //每一幅字符圖片所對應的字符類別索引下標
- }
- }
- trainingDataf5.convertTo(trainingDataf5, CV_32FC1);
- trainingDataf10.convertTo(trainingDataf10, CV_32FC1);
- trainingDataf15.convertTo(trainingDataf15, CV_32FC1);
- trainingDataf20.convertTo(trainingDataf20, CV_32FC1);
- Mat(trainingLabels).copyTo(classes);
- FileStorage fs("OCR.xml", FileStorage::WRITE);
- fs << "TrainingDataF5" << trainingDataf5;
- fs << "TrainingDataF10" << trainingDataf10;
- fs << "TrainingDataF15" << trainingDataf15;
- fs << "TrainingDataF20" << trainingDataf20;
- fs << "classes" << classes;
- fs.release();
- return 0;
- }
三、使用神經網絡檢測字符
a.讀取一張車牌圖像
b.配置神經網絡參數,並使用xml文件訓練神經網絡【參數配置上述已經說過了】
c.提取該車牌圖像的累計直方圖和低分辨率圖像特徵矩陣
d.將該特徵矩陣作爲神經網絡輸入,經網絡計算,得到預測結果【字符索引】
e.按照每個字符圖像的相對位置,進行字符重新排序
f.得到最終字符【和書中不同的是,我的輸入圖像是車牌而不是整幅圖像,因此絕對座標是不同的,但字符間的相對位置還是對的,只是不能在車牌照片上顯示數字而已,我直接答應到控制檯上】
具體代碼:
又用到了車牌類,這裏面有車牌字符相對位置調整的函數,都給出來吧:
Plate.h:
- #ifndef Plate_h
- #define Plate_h
- #include <string.h>
- #include <vector>
- #include <cv.h>
- #include <highgui.h>
- #include <cvaux.h>
- using namespace std;
- using namespace cv;
- class Plate{
- public:
- Plate();
- Plate(Mat img, Rect pos);
- string str();
- Rect position;
- Mat plateImg;
- vector<char> chars;
- vector<Rect> charsPos;
- };
- #endif
Plate.cpp:
- /*****************************************************************************
- * Number Plate Recognition using SVM and Neural Networks
- ******************************************************************************
- * by David Mill醤 Escriv? 5th Dec 2012
- * http://blog.damiles.com
- ******************************************************************************
- * Ch5 of the book "Mastering OpenCV with Practical Computer Vision Projects"
- * Copyright Packt Publishing 2012.
- * http://www.packtpub.com/cool-projects-with-opencv/book
- *****************************************************************************/
- #include "Plate.h"
- Plate::Plate(){
- }
- Plate::Plate(Mat img, Rect pos){
- plateImg=img;
- position=pos;
- }
- string Plate::str(){
- string result="";
- //Order numbers
- vector<int> orderIndex;
- vector<int> xpositions;
- for(int i=0; i< charsPos.size(); i++){
- orderIndex.push_back(i);
- xpositions.push_back(charsPos[i].x);
- }
- float min=xpositions[0];
- int minIdx=0;
- for(int i=0; i< xpositions.size(); i++){
- min=xpositions[i];
- minIdx=i;
- for(int j=i; j<xpositions.size(); j++){
- if(xpositions[j]<min){
- min=xpositions[j];
- minIdx=j;
- }
- }
- int aux_i=orderIndex[i];
- int aux_min=orderIndex[minIdx];
- orderIndex[i]=aux_min;
- orderIndex[minIdx]=aux_i;
- float aux_xi=xpositions[i];
- float aux_xmin=xpositions[minIdx];
- xpositions[i]=aux_xmin;
- xpositions[minIdx]=aux_xi;
- }
- for(int i=0; i<orderIndex.size(); i++){
- result=result+chars[orderIndex[i]];
- }
- return result;
- }
主要處理的函數:
- // car_plate_classify.cpp : 定義控制檯應用程序的入口點。
- //
- #include "stdafx.h"
- #include <cv.h>
- #include <highgui.h>
- #include <cvaux.h>
- #include <ml.h>
- #include <iostream>
- #include <vector>
- #include "Plate.h"
- #define HORIZONTAL 1
- #define VERTICAL 0
- using namespace std;
- using namespace cv;
- CvANN_MLP ann;
- 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'};
- const int numCharacters=30;
- bool verifySizes(Mat r){
- //Char sizes 45x77
- float aspect=45.0f/77.0f;
- float charAspect= (float)r.cols/(float)r.rows;
- float error=0.35;
- float minHeight=15;
- float maxHeight=28;
- //We have a different aspect ratio for number 1, and it can be ~0.2
- float minAspect=0.2;
- float maxAspect=aspect+aspect*error;
- //area of pixels
- float area=countNonZero(r);
- //bb area
- float bbArea=r.cols*r.rows;
- //% of pixel in area
- float percPixels=area/bbArea;
- /*if(DEBUG)
- cout << "Aspect: "<< aspect << " ["<< minAspect << "," << maxAspect << "] " << "Area "<< percPixels <<" Char aspect " << charAspect << " Height char "<< r.rows << "\n";*/
- if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeight && r.rows < maxHeight)
- return true;
- else
- return false;
- }
- Mat preprocessChar(Mat in){
- //Remap image
- int h=in.rows;
- int w=in.cols;
- int charSize=20; //統一每個字符的大小
- Mat transformMat=Mat::eye(2,3,CV_32F);
- int m=max(w,h);
- transformMat.at<float>(0,2)=m/2 - w/2;
- transformMat.at<float>(1,2)=m/2 - h/2;
- Mat warpImage(m,m, in.type());
- warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0) );
- Mat out;
- resize(warpImage, out, Size(charSize, charSize) );
- return out;
- }
- //create the accumulation histograms,img is a binary image, t is 水平或垂直
- Mat ProjectedHistogram(Mat img, int t)
- {
- int sz=(t)?img.rows:img.cols;
- Mat mhist=Mat::zeros(1,sz,CV_32F);
- for(int j=0; j<sz; j++){
- Mat data=(t)?img.row(j):img.col(j);
- mhist.at<float>(j)=countNonZero(data); //統計這一行或一列中,非零元素的個數,並保存到mhist中
- }
- //Normalize histogram
- double min, max;
- minMaxLoc(mhist, &min, &max);
- if(max>0)
- mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方圖中的最大值,歸一化直方圖
- return mhist;
- }
- Mat features(Mat in, int sizeData){
- //Histogram features
- Mat vhist=ProjectedHistogram(in,VERTICAL);
- Mat hhist=ProjectedHistogram(in,HORIZONTAL);
- //Low data feature
- Mat lowData;
- resize(in, lowData, Size(sizeData, sizeData) );
- //Last 10 is the number of moments components
- int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;
- Mat out=Mat::zeros(1,numCols,CV_32F);
- //Asign values to feature,ANN的樣本特徵爲水平、垂直直方圖和低分辨率圖像所組成的矢量
- int j=0;
- for(int i=0; i<vhist.cols; i++)
- {
- out.at<float>(j)=vhist.at<float>(i);
- j++;
- }
- for(int i=0; i<hhist.cols; i++)
- {
- out.at<float>(j)=hhist.at<float>(i);
- j++;
- }
- for(int x=0; x<lowData.cols; x++)
- {
- for(int y=0; y<lowData.rows; y++){
- out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);
- j++;
- }
- }
- return out;
- }
- int classify(Mat f){
- int result=-1;
- Mat output(1, 30, CV_32FC1); //西班牙車牌只有30種字符
- ann.predict(f, output);
- Point maxLoc;
- double maxVal;
- minMaxLoc(output, 0, &maxVal, 0, &maxLoc);
- //We need know where in output is the max val, the x (cols) is the class.
- return maxLoc.x;
- }
- void train(Mat TrainData, Mat classes, int nlayers){
- Mat layers(1,3,CV_32SC1);
- layers.at<int>(0)= TrainData.cols;
- layers.at<int>(1)= nlayers;
- layers.at<int>(2)= 30;
- ann.create(layers, CvANN_MLP::SIGMOID_SYM, 1, 1);
- //Prepare trainClases
- //Create a mat with n trained data by m classes
- Mat trainClasses;
- trainClasses.create( TrainData.rows, 30, CV_32FC1 );
- for( int i = 0; i < trainClasses.rows; i++ )
- {
- for( int k = 0; k < trainClasses.cols; k++ )
- {
- //If class of data i is same than a k class
- if( k == classes.at<int>(i) )
- trainClasses.at<float>(i,k) = 1;
- else
- trainClasses.at<float>(i,k) = 0;
- }
- }
- Mat weights( 1, TrainData.rows, CV_32FC1, Scalar::all(1) );
- //Learn classifier
- ann.train( TrainData, trainClasses, weights );
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- Mat input = imread("test.jpg",CV_LOAD_IMAGE_GRAYSCALE);
- Plate mplate;
- //Read file storage.
- FileStorage fs;
- fs.open("OCR.xml", FileStorage::READ);
- Mat TrainingData;
- Mat Classes;
- fs["TrainingDataF15"] >> TrainingData;
- fs["classes"] >> Classes;
- //訓練神經網絡
- train(TrainingData, Classes, 10);
- //dealing image and save each character image into vector<CharSegment>
- //Threshold input image
- Mat img_threshold;
- threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV);
- Mat img_contours;
- img_threshold.copyTo(img_contours);
- //Find contours of possibles characters
- vector< vector< Point> > contours;
- findContours(img_contours,
- contours, // a vector of contours
- CV_RETR_EXTERNAL, // retrieve the external contours
- CV_CHAIN_APPROX_NONE); // all pixels of each contours
- //Start to iterate to each contour founded
- vector<vector<Point> >::iterator itc= contours.begin();
- //Remove patch that are no inside limits of aspect ratio and area.
- while (itc!=contours.end()) {
- //Create bounding rect of object
- Rect mr= boundingRect(Mat(*itc));
- //rectangle(result, mr, Scalar(255,0,0),2);
- //Crop image
- Mat auxRoi(img_threshold, mr);
- if(verifySizes(auxRoi)){
- auxRoi=preprocessChar(auxRoi);
- //對每一個小方塊,提取直方圖特徵
- Mat f=features(auxRoi,15);
- //For each segment feature Classify
- int character=classify(f);
- mplate.chars.push_back(strCharacters[character]);
- mplate.charsPos.push_back(mr);
- //printf("%c ",strCharacters[character]);
- }
- ++itc;
- }
- string licensePlate=mplate.str();
- cout<<licensePlate<<endl;
- return 0;
- }
這邊運行時間略長,大概10s以下吧。這就是android不能做太多圖像處理的原因,運行速度不給力啊。
上上後面做的評估是對隱層神經元數量和不同分辨率的一種統計,沒多大花頭,以後要用再看吧。而且車牌識別已經做爛了,沒什麼動力了~~
好吧,下一篇嘗試將車牌檢測與識別都移植到android上試試。
源自:http://blog.csdn.net/jinshengtao/article/details/17954427