图像处理基本算法 车牌识别与定位

 

图像处理基本算法 车牌识别与定位

标签: 图像处理
  4416人阅读  评论(1)  收藏  举报
  分类:
opencv(27) 

       进行车牌识别和定位首先要了解车牌的特征以及分类。

车牌主要包括以下几种:

蓝牌白字:普通小型车(其中包括政府机关专用号段、政法部门警车以外的行政用车)的牌照
黄牌黑字:大型车辆、摩托车、驾校教练车牌照
黑牌白字:涉外车辆牌照,式样和蓝牌基本相同
白牌:政法部门(公安、法院、检察院、国安、司法)警车、武警部队车辆、解放军军车的牌照都是白牌
警车:公安警车的牌照样式为[某·A1234警],除“警”为红字外其他的都是黑字,一共4位数字,含义与普通牌照相同

车牌定位的算法分为三类,一类是基于边缘的,一类是基于颜色的,一类是基于机器学习的,这三种方法我都做过实验,基于边缘的最简单也最有效,如果对于收费站和小区的应用,做到99%以上的检测率不是件难事,但如果场景复杂一点,误检会比较多,但并不会漏掉真正的车牌,只是虚警率高点,可以通过先验知识、颜色和后面的分割加以去除,误检不是重点。基于颜色的定位算法,从根本上讲也可以算是基于边缘的一种,无非是利用彩色边缘或者灰度图像边缘和颜色一起来定位,基于颜色的车牌定位算法用于高清图片效果不错,对于一般的场景我认为没必要用颜色进行定位,但初期用颜色先去除一些明显不是车牌的区域还是比较有效的。基于机器学习的算法进行车牌定位或者说检测,关键是找到好的特征和好的训练方法,不少人利用adaboost+haar特征进行车牌检测,从我的实验结果来看,检测率也能达到99%以上,但同时虚警率也非常高,会出现很多误检,而且很难把车牌的区域完整的检测出来,所以如果单独要用机器学习的算法还是不太可行,不过可以先利用边缘信息找到候选区域,然后用adaboost去去除非车牌区域,这个效果还是蛮不错的。

       对于边缘的检测,如果车牌在图像中占的比例不是很小,普通的差分和全局二值化就可以达到很好的效果,如果对于高清图像(譬如要检测几个车道)或者场景很复杂,导致车牌所占图像的比例很小,还有就是车牌处于比较暗的地方,而整个场景很亮,这个时候差分得到的边缘就不会很丰富,如果利用全局二值化就可能导致车牌区域检测不到边缘,解决办法一就是对图像进行灰度拉伸或增强,解决办法二就是换边缘检测的方法(譬如sobel),解决办法三就是改进二值化的方法。对于图像增强的方法我要特别提一下直方图均衡化,很多论文上都会说对输入图片先进行直方图均衡化,但我的实验发现,晚上的图片如果进行直方图均衡化操作后会导致噪点特别多,而且可能会导致车牌区域检测不到边缘,总之图像增强是一把双刃剑,需要慎重考虑。

       如果利用边缘进行定位,关键是要想办法一定要检测出车牌区域的边缘。

       车牌定位,利用边缘是王道,可以先粗检再精检,颜色可以用于精定位和去除误检,机器学习如果想要好的结果得需要好的特征,但目前好像还没有。我个人认为车牌定位的难点不在于找到车牌区域,而在于怎么对车牌区域进行更精确的定位,而精定位的难点在于左右精定位,以便于后面的分割算法。

这里给出一份基于颜色和形状的车牌识别的源代码,基本思路是提取车牌的特征然后根据特征进行筛选。

识别车牌用到的特征:颜色特征、形状特征、大小特征
a 颜色特征

每种车牌的底色具有各自特殊的RGB值,例如程序中使用的蓝底车牌的典型RGB值为 R = 28,G = 63, B = 138; 
当RGB值接近时则认为可能是车牌,将该点像素赋值为255,否则0;利用颜色特征可以获取二值图像,可以去除大部分的其他物体,但是会有很多的干扰。
b、大小特征
对二值图像进行膨胀处理和腐蚀处理之后,可以去掉很小的噪点。对于灰度图获取轮廓边缘,轮廓具有一定的面积。车牌应该具有一定的大小,如果面积过小则认为不是车牌。此法可以去除大部分的小面积的干扰物。
颜色特征和大小特征是初选。形状特征是精选。
c、形状特征
矩形度:提取轮廓之后,轮廓包围的面积和轮廓最小外接矩形的面积之比称为矩形度,值越接近1,则是矩形的概率越大。
长宽比:正常车牌的长宽比为3:1,最小外接矩形的长宽比越接近1则认为是车牌的概率最大。
如果三个筛选条件都符合,则是车牌的概率非常大。

  //主程序lpr.cpp     

[cpp]  view plain  copy
  1. #include   
  2. #include   
  3. #include   
  4. #include  
  5. #include  
  6. #include   
  7. #include "image.h"  
  8. using namespace std;  
  9. int main(){  
  10.     char imageName[12] = "1.jpg";  
  11.     char imageDstName[12] = "1_dst.jpg";  
  12.     char imageBwName[12]  = "1_bw.jpg";  
  13.     IplImage* srcImage = NULL,*image = NULL,*bwImage = NULL;  
  14.       
  15.     cvNamedWindow("srcImage",1);  
  16.     cvNamedWindow("bwImage",1);  
  17.     //cvShowImage("srcImage",srcImage);  
  18.     int imageWidth,imageHeight;    
  19.     int maxDif = 50;   
  20.     //找到蓝色区域  
  21.     int i= 0,j = 0;  
  22.     unsigned char * pPixel = NULL;  
  23.     unsigned char   pixelR = 0,pixelG = 0,pixelB = 0;  
  24.     unsigned char R = 28,G = 63, B = 138;   
  25.   
  26.     double length,area,rectArea;  
  27.     double rectDegree = 0.0; //矩形度  
  28.     double long2Short = 1.0; //体态比  
  29.     //计算边界序列的参数 长度 面积 矩形 最小矩形   
  30.     //并输出每个边界的参数  
  31.     CvRect rect;  
  32.     CvBox2D box;  
  33.     int imageCnt = 1;  
  34.     double axisLong = 0.0, axisShort = 0.0;  
  35.     double temp;  
  36.   
  37.     while ((srcImage = cvLoadImage(imageName,1)) != NULL)  
  38.     {  
  39.         cvShowImage("srcImage",srcImage);   
  40.         cout<": "<
  41.         imageWidth = srcImage->width;  
  42.         imageHeight = srcImage->height;  
  43.   
  44.           
  45.         image = cvCreateImage(cvSize(imageWidth,imageHeight),8,3);  
  46.         //image = cvCloneImage(srcImage);  
  47.         Image::cloneImage(srcImage,image);  
  48.   
  49.         bwImage = cvCreateImage(cvGetSize(srcImage),srcImage->depth,1);  
  50.         //cvZero(bwImage);  
  51.         Image::ZerosImage(bwImage);  
  52.   
  53.   
  54.         for (i = 0; i< imageHeight;i++)  
  55.         {  
  56.             for (j = 0;j
  57.             {  
  58.                 pPixel = (unsigned char*)srcImage->imageData + i*srcImage->widthStep + j*3;  
  59.                 pixelB = pPixel[0];  
  60.                 pixelG = pPixel[1];  
  61.                 pixelR = pPixel[2];  
  62.   
  63.                 if (abs(pixelB - B) < maxDif && abs(pixelG - G)< maxDif && abs(pixelR - R)< maxDif)  
  64.                 {  
  65.                     *((unsigned char*)bwImage->imageData + i*bwImage->widthStep + j) = 255;  
  66.                 }else {  
  67.                     *((unsigned char*)bwImage->imageData + i*bwImage->widthStep + j) = 0;  
  68.                 }  
  69.             }  
  70.         }  
  71.         cvShowImage("bwImage",bwImage);  
  72.         cvSaveImage(imageBwName,bwImage);  
  73.          //cvWaitKey(0);  
  74.         //膨胀  
  75.         //cvDilate(bwImage,bwImage,0,3);  
  76.         Image::dilateImage(bwImage,bwImage);  
  77.         Image::dilateImage(bwImage,bwImage);  
  78.         Image::dilateImage(bwImage,bwImage);  
  79.   
  80.         //cvErode (bwImage,bwImage,0,3);  
  81.         Image::erodeImage(bwImage,bwImage);  
  82.         Image::erodeImage(bwImage,bwImage);  
  83.         Image::erodeImage(bwImage,bwImage);  
  84.   
  85.         cvShowImage("bwImage",bwImage);  
  86.         //cvWaitKey(0);  
  87.   
  88.         //新图,将轮廓绘制到dst  
  89.         IplImage *dst = cvCreateImage(cvGetSize(srcImage),8,3);   
  90.         //dst = cvCloneImage(srcImage);//赋值为0  
  91.         Image::cloneImage(srcImage,dst);  
  92.         //寻找轮廓  
  93.         CvMemStorage *storage = cvCreateMemStorage(0);  
  94.         CvSeq * seq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);  
  95.         CvSeq * tempSeq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);  
  96.         int cnt = cvFindContours(bwImage,storage,&seq);//返回轮廓的数目  
  97.         cout<<"number of contours   "<
  98.         cvShowImage("bwImage",bwImage);    //难道使用cvFindContours会改变源图像?需要实现保存一下  
  99.   
  100.         for (tempSeq = seq;tempSeq != NULL; tempSeq = tempSeq->h_next)  
  101.         {  
  102.               
  103.             length = cvArcLength(tempSeq);  
  104.             area =  cvContourArea(tempSeq);  
  105.             //筛选面积比较大的区域  
  106.             if (area > 1000 && area < 50000)  
  107.             {  
  108.                   
  109.                 //cout<<"Points:  "<total<  
  110.                 //外接矩形  
  111.                 rect = cvBoundingRect(tempSeq,1);  
  112.                 //绘制轮廓和外接矩形  
  113.                 //cvDrawContours(dst,tempSeq,CV_RGB(255,0,0),CV_RGB(255,0,0),0);  
  114.                 //cvRectangleR(dst,rect,CV_RGB(0,255,0));  
  115.                 //cvShowImage("dst",dst);  
  116.   
  117.                 //绘制外接最小矩形  
  118.                 CvPoint2D32f pt[4];  
  119.                 box = cvMinAreaRect2(tempSeq,0);  
  120.                 cvBoxPoints(box,pt);  
  121.                 //下面开始分析图形的形状特征   
  122.                 //长轴 短轴  
  123.                 axisLong = sqrt(pow(pt[1].x -pt[0].x,2) + pow(pt[1].y -pt[0].y,2));  
  124.                 axisShort = sqrt(pow(pt[2].x -pt[1].x,2) + pow(pt[2].y -pt[1].y,2));  
  125.   
  126.                 if (axisShort > axisLong)  
  127.                 {    
  128.                     temp = axisLong;  
  129.                     axisLong = axisShort;  
  130.                     axisShort= temp;  
  131.                 }    
  132.                 rectArea = axisLong*axisShort;   
  133.                 rectDegree =  area/rectArea;    
  134.                 //体态比or长宽比 最下外接矩形的长轴和短轴的比值  
  135.                 long2Short = axisLong/axisShort;  
  136.                
  137.                 if (long2Short> 2.2 && long2Short < 3.8 && rectDegree > 0.63 && rectArea > 3000 && rectArea <50000)  
  138.                 {  
  139.                     cout<<"Length:  "<
  140.                     cout<<"Area  :  "<
  141.                     cout<<"long axis :"<
  142.                     cout<<"short axis: "<
  143.                     cout<<"long2Short: "<
  144.                     cout<<"rectArea:  "<
  145.                     cout<<"rectDegree:  "<
  146.   
  147.                     for(int i = 0;i<4;++i){  
  148.                         cvLine(dst,cvPointFrom32f(pt[i]),cvPointFrom32f(pt[((i+1)%4)?(i+1):0]),CV_RGB(255,0,0));  
  149.                     }  
  150.                 }  
  151.                 //cvShowImage("dst",dst);  
  152.                 //cvWaitKey();  
  153.             }  
  154.         }  
  155.         cvShowImage("dst",dst);  
  156.         cvSaveImage(imageDstName,dst);  
  157.         //cvWaitKey(0);  
  158.   
  159.         imageCnt++;  
  160.         sprintf(imageName,"%d.jpg",imageCnt);  
  161.         sprintf(imageBwName,"%d_bw.jpg",imageCnt);  
  162.         sprintf(imageDstName,"%d_dst.jpg",imageCnt);  
  163.         cout<<"\n\n";  
  164.     }  
  165.   
  166.     return 0;  
  167. }  
  168.   
  169.    

//image类

//image.h

[cpp]  view plain  copy
  1. #pragma once  
  2. #include "cv.h"  
  3. #include  
  4. class Image  
  5. {  
  6. public:  
  7.     Image(void);  
  8.     ~Image(void);  
  9.     static void cloneImage(IplImage *src,IplImage*dst);  
  10.     static void ZerosImage(IplImage * src);  
  11.     static void dilateImage(IplImage* src, IplImage *dst);  
  12.     static void erodeImage(IplImage* src,IplImage*dst);  
  13.     static void rgb2gray(IplImage* src, IplImage* dst);  
  14.     static void gray2bw(IplImage* src, IplImage* dst,unsigned char value);  
  15. };  

//image.cpp

[cpp]  view plain  copy
  1. #include "Image.h"  
  2.   
  3.   
  4. Image::Image(void)  
  5. {  
  6. }  
  7.   
  8.   
  9. Image::~Image(void)  
  10. {  
  11. }  
  12.   
  13. void Image::cloneImage(IplImage *src,IplImage*dst){  
  14.     int nChannels = src->nChannels;  
  15.     int imageHeight = src->height;  
  16.     int imageWidth  = src->width;  
  17.     unsigned char *pPixel= NULL;  
  18.     unsigned char *pPixel2= NULL;  
  19.     if (nChannels == 1)  
  20.     {  
  21.         for (int i = 0; i< imageHeight;i++)  
  22.         {  
  23.             for (int j = 0; j< imageWidth ;j++)  
  24.             {  
  25.                 pPixel  = (unsigned char *)src->imageData + i*src->widthStep+j;  
  26.                 pPixel2 = (unsigned char *)dst->imageData + i*dst->widthStep+j;   
  27.                 pPixel2[0] = pPixel[0];                            
  28.             }  
  29.         }  
  30.     }else if (nChannels == 3)  
  31.     {  
  32.         for (int i = 0; i< imageHeight;i++)  
  33.         {  
  34.             for (int j = 0; j< imageWidth ;j++)  
  35.             {  
  36.                 pPixel  = (unsigned char *)src->imageData + i*src->widthStep+3*j;  
  37.                 pPixel2 = (unsigned char *)dst->imageData + i*dst->widthStep+3*j;   
  38.                 pPixel2[0] = pPixel[0];  
  39.                 pPixel2[1] = pPixel[1];  
  40.                 pPixel2[2] = pPixel[2];                              
  41.             }  
  42.         }  
  43.     }  
  44. }  
  45. void Image::ZerosImage(IplImage * src){  
  46.     int nChannels = src->nChannels;  
  47.     int imageHeight = src->height;  
  48.     int imageWidth  = src->width;  
  49.     unsigned char *pPixel= NULL;  
  50.     unsigned char *pPixel2= NULL;  
  51.     if (nChannels == 1)  
  52.     {  
  53.         for (int i = 0; i< imageHeight;i++)  
  54.         {  
  55.             for (int j = 0; j< imageWidth ;j++)  
  56.             {  
  57.                 pPixel  = (unsigned char *)src->imageData + i*src->widthStep+j;  
  58.                 pPixel[0] = 0;                            
  59.             }  
  60.         }  
  61.     }else if (nChannels == 3)  
  62.     {  
  63.         for (int i = 0; i< imageHeight;i++)  
  64.         {  
  65.             for (int j = 0; j< imageWidth ;j++)  
  66.             {  
  67.                 pPixel  = (unsigned char *)src->imageData + i*src->widthStep + 3*j;   
  68.                 pPixel[0] = 0;  
  69.                 pPixel[1] = 0;  
  70.                 pPixel[2] = 0;                              
  71.             }  
  72.         }  
  73.     }  
  74.   
  75. }  
  76. //膨胀  
  77. void Image::dilateImage(IplImage* src, IplImage *dst){  
  78.     int nChannels = src->nChannels;  
  79.     int imageHeight = src->height;  
  80.     int imageWidth  = src->width;  
  81.     IplImage * tmpDst = cvCreateImage(cvSize(imageWidth,imageHeight),src->depth,src->nChannels);  
  82.     ZerosImage(tmpDst);  
  83.     unsigned char *pPixel= NULL;  
  84.     unsigned char *pPixel2= NULL;  
  85.     CvSize windowSize = cvSize(3,3);  
  86.     int SW2 = windowSize.width/2;  
  87.     int SH2 = windowSize.height/2;  
  88.   
  89.     if (nChannels == 1)  
  90.     {  
  91.         for (int i = 1; i< imageHeight - 1;i++)  
  92.         {  
  93.             for (int j = 1; j< imageWidth - 1 ;j++)  
  94.             {  
  95.                 pPixel  = (unsigned char *)tmpDst->imageData + i*tmpDst->widthStep+j;  
  96.                 for (int m = i - SH2 ; m <= i+SH2; m++)  
  97.                 {  
  98.                     for (int n = j - SW2; n <= j+SW2;n++)  
  99.                     {  
  100.                         pPixel2  = (unsigned char *)src->imageData + m*src->widthStep + n;  
  101.                         if (pPixel2[0] == 255)  
  102.                         {  
  103.                             pPixel[0] = 255;  
  104.                         }  
  105.                     }  
  106.                 }  
  107.             }  
  108.         }  
  109.     }else if (nChannels == 3)  
  110.     {  
  111.        //不是灰度图像,报错  
  112.     }  
  113.   
  114.     //拷贝图像  
  115.     cloneImage(tmpDst,dst);  
  116.     //cvShowImage("dst",dst);  
  117.     //cvWaitKey(0);  
  118. }  
  119. void Image::erodeImage(IplImage* src,IplImage*dst){  
  120.     int nChannels = src->nChannels;  
  121.     int imageHeight = src->height;  
  122.     int imageWidth  = src->width;  
  123.     IplImage * tmpDst = cvCreateImage(cvSize(imageWidth,imageHeight),src->depth,src->nChannels);  
  124.     ZerosImage(tmpDst);  
  125.     unsigned char *pPixel= NULL;  
  126.     unsigned char *pPixel2= NULL;  
  127.     CvSize windowSize = cvSize(3,3);  
  128.     int SW2 = windowSize.width/2;  
  129.     int SH2 = windowSize.height/2;  
  130.     int flag = 0;  
  131.   
  132.     if (nChannels == 1)  
  133.     {  
  134.         for (int i = 1; i< imageHeight - 1;i++)  
  135.         {  
  136.             for (int j = 1; j< imageWidth - 1 ;j++)  
  137.             {  
  138.                 pPixel  = (unsigned char *)tmpDst->imageData + i*tmpDst->widthStep+j;  
  139.                 flag = 0;  
  140.                 for (int m = i - SH2 ; m <= i+SH2; m++)  
  141.                 {  
  142.                     for (int n = j - SW2; n <= j+SW2;n++)  
  143.                     {  
  144.                         pPixel2  = (unsigned char *)src->imageData + m*src->widthStep + n;  
  145.                         if (pPixel2[0] == 0)  
  146.                         {  
  147.                             flag = 1;  
  148.                         }  
  149.                     }  
  150.                     if (flag == 0)  
  151.                     {  
  152.                         //  
  153.                         pPixel[0] = 255;  
  154.                     }else {  
  155.                         pPixel[0] = 0;  
  156.                     }  
  157.   
  158.                 }  
  159.             }  
  160.         }  
  161.     }else if (nChannels == 3)  
  162.     {  
  163.         //不是灰度图像,报错  
  164.     }  
  165.   
  166.     //拷贝图像  
  167.     cloneImage(tmpDst,dst);  
  168.     //cvShowImage("dst",dst);  
  169.     //cvWaitKey(0);  
  170. }  
  171.   
  172. void Image::rgb2gray(IplImage* src, IplImage* dst){  
  173.     //Gray = 0.29900 * R + 0.58700 * G + 0.11400 * B  
  174.     int nChannels = src->nChannels;  
  175.     int imageHeight = src->height;  
  176.     int imageWidth  = src->width;  
  177.     unsigned char *pPixel= NULL;  
  178.     unsigned char *pPixel2= NULL;  
  179.     unsigned char R ,G, B;  
  180.     unsigned char grayPixle = 0;  
  181.   
  182.     if (nChannels == 3)  
  183.     {  
  184.         for (int i = 0; i< imageHeight;i++)  
  185.         {  
  186.             for (int j = 0; j< imageWidth ;j++)  
  187.             {  
  188.                 pPixel  = (unsigned char *)src->imageData + i*src->widthStep+j*3;  
  189.                 pPixel2 = (unsigned char *)dst->imageData + i*dst->widthStep+j;  
  190.                 B = pPixel[0];  
  191.                 G = pPixel[1];  
  192.                 R = pPixel[2];  
  193.                 grayPixle = (unsigned char)(0.29900 * R + 0.58700 * G + 0.11400 * B);  
  194.                 pPixel2[0] = grayPixle;                            
  195.             }  
  196.         }  
  197.     }else if (nChannels == 1)  
  198.     {  
  199.         //不是RGB图像,报错  
  200.     }  
  201. }  
  202. void Image::gray2bw(IplImage* src, IplImage* dst,unsigned char value){  
  203.     int nChannels = src->nChannels;  
  204.     int imageHeight = src->height;  
  205.     int imageWidth  = src->width;  
  206.     unsigned char *pPixel= NULL;  
  207.     unsigned char *pPixel2= NULL;  
  208.     if (nChannels == 1)  
  209.     {  
  210.         for (int i = 0; i< imageHeight;i++)  
  211.         {  
  212.             for (int j = 0; j< imageWidth ;j++)  
  213.             {  
  214.                 pPixel  = (unsigned char *)src->imageData + i*src->widthStep+j;  
  215.                 pPixel2  = (unsigned char *)dst->imageData + i*dst->widthStep+j;  
  216.                 if (pPixel[0] > value)  
  217.                 {  
  218.                     pPixel2[0] = 255;  
  219.                 }else {  
  220.                     pPixel2[0] = 0;  
  221.                 }  
  222.                                             
  223.             }  
  224.         }  
  225.     }else if (nChannels == 3)  
  226.     {  
  227.        //不是灰度图像,报错  
  228.     }  
  229. }  

效果图:(最终的效果用红色框框出)









源码以及测试图片下载地址:http://download.csdn.NET/detail/renshengrumenglibing/5073845

你可能感兴趣的:(050,算法)