1. 程式人生 > >分享自己寫的車牌字元分割程式(opencv)

分享自己寫的車牌字元分割程式(opencv)

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
//////////////////////////////////本程式只適用於檢測八個車牌字元,帶點,,出現連粘和斷裂的(也就是不是8個字元)會出錯
using namespace cv;
using namespace std;
Mat jianqietu,jieguo;
float Valueo=0;
int main()
{
Mat srcImage=imread("15.jpg");
//imshow("原圖",srcImage);


//Mat dstImage;
cvtColor(srcImage,srcImage,CV_RGB2GRAY);
//blur(srcImage,srcImage,Size(3,3));
threshold( srcImage, srcImage, 0, 255,CV_THRESH_OTSU);//大津法,自動閾值,圖片12,13,14,15,16,17,18,19可以用自動閾值法
//return Valueo  = threshold( srcImage, srcImage, 0, 255,CV_THRESH_OTSU);//希望返回大津法給的自動閾值


//threshold(srcImage,srcImage,130,255,CV_THRESH_BINARY);//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$用圖片10閾值要改為90,用圖片14閾值要改為160,用圖片15閾值要改為130,
medianBlur(srcImage,srcImage,7);
imshow("二值化圖",srcImage);
//imshow("s",dstImage);


int *rowwidth=new int[srcImage.rows];//這裡用小括號還不行
//int *colheight =new int[srcImage.cols]; 


memset(rowwidth,0,srcImage.rows*4);//############################################3
//memset(colheight,0,srcImage.cols*4);


int value,k=0,m=0,n=0;
for(int i=0;i<srcImage.rows;i++)  
{
        for(int j=0;j<srcImage.cols;j++)    
        {    
   //           k++;//總的畫素點k=i*512+j
//if(srcImage.at<uchar>(i,j)==255)//這裡是我寫的在控制面板輸出每一行白點個數,
//{
//m++;//總的白點數
//if(k%512==511){
//printf("第%3d",i);
//printf("行=%3d     ",m-n);}
//if(k%512==0) 
//{
//n=m;
//
//}
//}
            //value=cvGet2D(src,j,i);  
            value=srcImage.at<uchar>(i,j);  
//value1=srcImage.at<uchar>(i,j);
            if(value==255)    
            {    
                rowwidth[i]++;
            }  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//if (rowwidth[i]%100==0)


        }    
}
Mat histogramImage=Mat::zeros(srcImage.rows,srcImage.cols,CV_8UC1);//相似替換
//Mat histogramImage(srcImage.rows,srcImage.cols,CV_8UC1);
// for(int i=0;i<srcImage.rows;i++)    
  //          for(int j=0;j<srcImage.cols;j++)    
  //          {    
  //              value=0;  //設定為黑色。  
  //              histogramImage.at<uchar>(i,j)=value;    
  //          }    


for(int i=0;i<srcImage.rows;i++)
for(int j=0;j<rowwidth[i];j++)
{
value=255;
histogramImage.at<uchar>(i,j)=value;
}


//******************************************************************************************************取峰值:
int max=0,i=0,one_number=0,one_max=0;
for(int i=0;i<srcImage.rows;i++)
{
if(srcImage.cols-rowwidth[i]>max){max=srcImage.cols-rowwidth[i];one_number=i;one_max=max;}
}
max=0;
//初步輸出二峰值
int two_number=0,two_max=0;
for(int i=0;i<srcImage.rows;i++)

         //////數字50控制車牌字元的上下端之差不至於小於2倍的50
if(   (srcImage.cols-rowwidth[i]>max)&&( (i>one_number+50)||(i<one_number-50) )  ){max=srcImage.cols-rowwidth[i];two_number=i;two_max=max;}
}
//輸出one峰值
printf("one行峰值數%3d\n",one_max);
printf("one行峰值位置%3d\n\n",one_number);
//輸出two峰值
printf("two行峰值數%3d\n",two_max);
printf("two行峰值位置%3d\n\n",two_number);


int qian=0,hou=0;//定義ROI區域的前行和後行
if(one_number<two_number){qian=one_number,hou=two_number;}
if(one_number>two_number){qian=two_number,hou=one_number;}


for(i=qian;i<qian+70;i++)//控制上邊界,使上邊界儘量下移,用以縮小即將裁剪的ROI區域,70控制下移距離不要超過70 
{         
if(  (srcImage.cols-rowwidth[i]== two_max)||(srcImage.cols-rowwidth[i]== one_max)  )qian=i;
}


jianqietu=srcImage(Rect(0,qian,srcImage.cols,hou-qian));//Rect(1,2,3,4),這裡1表ROI的起始橫座標,2表起始縱座標,3表ROI寬,4表ROI高
imshow("去上下邊界剪下圖",jianqietu);
imshow("白++黑 直方圖",histogramImage);
// imshow("l",histogramImage);


//srcImage=jianqietu.clone();


//*********************************************************下面我們來看看垂直投影********************************************************88


int *colheight =new int[srcImage.cols];    
   
memset(colheight,0,srcImage.cols*4);  //陣列必須賦初值為零,否則出錯。無法遍歷陣列。  


    for(int i=0;i<srcImage.rows;i++)   

        for(int j=0;j<srcImage.cols;j++)    
        {    
            //value=cvGet2D(src,j,i);  
            value=srcImage.at<uchar>(i,j);  
            if(value==255)    
            {    
                colheight[j]++; //統計每列的白色畫素點   *********************************************陣列用法 
            }    
  
}
}    
       
        for(int i=0;i<srcImage.rows;i++)    
     for(int j=0;j<srcImage.cols;j++)    
     {    
         value=0;  //設定為黑色。  
         histogramImage.at<uchar>(i,j)=value;    
     }    
     //imshow("d",histogramImage);  
     for(int i=0;i<srcImage.cols;i++)    
         for(int j=0;j<colheight[i];j++)    
         {    
             value=255;  //設定為白色  
             histogramImage.at<uchar>(j,i)=value;  
         }    
         //imshow("C",srcImage);
 int max2=0,kongzhi=30;//////kongzhi用來控制車牌字元的左右端之差不至於小於2倍的kongzhi
 int cols_one_number=0,cols_one_number2=0,cols_one_number3=0,cols_one_number4=0;
 int    cols_one_max=0,  cols_one_max2=0,    cols_one_max3=0,   cols_one_max4=0;
 //&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
 for(int i=srcImage.cols;i>0;i--)//用i--,就能從右往左選左邊界,這樣的左邊界儘量會靠右,更準確
 {
 if(srcImage.rows-colheight[i]>max2&&(i<70) )//控制即將分割的ROI的左邊界
{max2=srcImage.rows-colheight[i];cols_one_number=i;cols_one_max=max2;}
 }
 max2=0;
 //&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
 for(int i=0;i<srcImage.cols;i++)
 { 
 if(   (srcImage.rows-colheight[i]>max2)&&(i>srcImage.cols-100 ) )//控制即將分割的ROI的右邊界
 {max2=srcImage.rows-colheight[i];cols_one_number2=i,cols_one_max2=max2;}
 }
 
 //&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
 printf("第一大行峰值數%3d\n",cols_one_max);
 printf("第一大行峰值位置%3d\n\n",cols_one_number);
 
 printf("第二大行峰值數%3d\n",cols_one_max2);
 printf("第二大行峰值位置%3d\n\n",cols_one_number2);
 jieguo=srcImage(Range(qian-2,hou+2),Range(cols_one_number-2,cols_one_number2+2));//2用來控制ROI不要太靠邊哦
 //&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
     imshow("去掉上下左右邊界",jieguo);
 imshow("垂直投影儀",histogramImage); 
 imwrite("我最終的ROI區域.jpg",jieguo);
 //printf("OTSU自動閾值Value返回值%3f\n\n",Valueo);
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 這裡我要試圖做幾次縱向的分割  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int before=0,after=0,h=0;//控制縱向黑色峰值的左右邊界,h作為下面hang[6]的內容
int hang[9]={0,0,0,0,0,0,0,0,0};//準備存幾個縱向的分割線條
for(int i=0;i<srcImage.cols;i++)
 { 
 if(   (srcImage.rows-colheight[i]>srcImage.rows-20) )//如果縱向的黑色峰值大於行高減20,則,,,,,
 {   
  
before=i;
for(i;srcImage.rows-colheight[i]>srcImage.rows-20;i++)
{
//if((srcImage.rows-colheight[i]<srcImage.rows-20)||(srcImage.rows-colheight[i]==srcImage.rows-20)  ) //我這個if語句完全不起作用?。。。。
//{after=i;
//hang[h]=int((before+after)/2);
   //         //printf("hang[h]的值,有嗎?%d\n",hang[h]);
//break;}
}
after=i; 
hang[h]=int((before+after)/2);//我這樣是想從字元間空當的中間分割,不至於太挨著邊了
            printf("cccc字元間空當的中間座標,%d\n", hang[h]);//輸出九個間隔,後面沒輸出來這裡卻行,歪打正著了,,,哈哈哈哈
h++;
}
 }
printf("hang[h]的值,有嗎?%d\n",hang[2]);//,此處用作實驗輸出,無實際 意義。試了下,可以有效輸出陣列的值了,這裡的hang[]可能是貼邊的,所以數值跟上面有出入


// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  這裡我要來分割字元了  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Mat img1,img2,img3,img4,img5,img6,img7,img8;
img1=srcImage(Range(qian-2,hou+2),Range(hang[0],hang[1]));
 img2=srcImage(Range(qian-2,hou+2),Range(hang[1],hang[2]));
  img3=srcImage(Range(qian-2,hou+2),Range(hang[2],hang[3]));
   img4=srcImage(Range(qian-2,hou+2),Range(hang[3],hang[4]));
img5=srcImage(Range(qian-2,hou+2),Range(hang[4],hang[5]));
 img6=srcImage(Range(qian-2,hou+2),Range(hang[5],hang[6]));
  img7=srcImage(Range(qian-2,hou+2),Range(hang[6],hang[7]));
   img8=srcImage(Range(qian-2,hou+2),Range(hang[7],hang[8]));

imshow("第1個字元小塊",img1);
imshow("第2個字元小塊",img2);
imshow("第3個字元小塊",img3);
imshow("第4個字元小塊",img4);
imshow("第5個字元小塊",img5);
imshow("第6個字元小塊",img6);
imshow("第7個字元小塊",img7);
imshow("第8個字元小塊",img8);


 //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    下面是查詢外輪廓矩形  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
vector<vector<Point>>contours;
vector<Vec4i> hierarchy;


threshold( jieguo, jieguo, 100, 255, THRESH_BINARY );
findContours(jieguo, contours, hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
    printf("輪廓個數:%d",contours.size());
    //int idx = 0;
//char szName[56] = {0};
vector<vector<Point>> contours_poly(contours.size());
vector<Rect> boundRect(contours.size());


for( int i = 0; i < contours.size(); i++ )
     { approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );//逼近多邊形曲線
       boundRect[i] = boundingRect( Mat(contours_poly[i]) );//尋找外部矩形邊界
       
     }


Mat drawing = Mat::zeros( jieguo.size(), CV_8UC3 );
for(int unsigned i=0;i<contours.size();i++)
{
drawContours( drawing, contours_poly, i, Scalar(0,0,255), 1, 8, vector<Vec4i>(), 0, Point() );
 rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), Scalar(0,222,255), 2, 8, 0 );    //顯示矩形的外框,,這個tl和br我也是醉了,,
}


//imshow("攢公開",jieguo);
imshow("&&&&&&&&&&&&&",drawing);
waitKey(0);
return 0;