车牌识别源代码部份流程供参考一.docx

上传人:b****2 文档编号:2117421 上传时间:2023-05-02 格式:DOCX 页数:44 大小:150.24KB
下载 相关 举报
车牌识别源代码部份流程供参考一.docx_第1页
第1页 / 共44页
车牌识别源代码部份流程供参考一.docx_第2页
第2页 / 共44页
车牌识别源代码部份流程供参考一.docx_第3页
第3页 / 共44页
车牌识别源代码部份流程供参考一.docx_第4页
第4页 / 共44页
车牌识别源代码部份流程供参考一.docx_第5页
第5页 / 共44页
车牌识别源代码部份流程供参考一.docx_第6页
第6页 / 共44页
车牌识别源代码部份流程供参考一.docx_第7页
第7页 / 共44页
车牌识别源代码部份流程供参考一.docx_第8页
第8页 / 共44页
车牌识别源代码部份流程供参考一.docx_第9页
第9页 / 共44页
车牌识别源代码部份流程供参考一.docx_第10页
第10页 / 共44页
车牌识别源代码部份流程供参考一.docx_第11页
第11页 / 共44页
车牌识别源代码部份流程供参考一.docx_第12页
第12页 / 共44页
车牌识别源代码部份流程供参考一.docx_第13页
第13页 / 共44页
车牌识别源代码部份流程供参考一.docx_第14页
第14页 / 共44页
车牌识别源代码部份流程供参考一.docx_第15页
第15页 / 共44页
车牌识别源代码部份流程供参考一.docx_第16页
第16页 / 共44页
车牌识别源代码部份流程供参考一.docx_第17页
第17页 / 共44页
车牌识别源代码部份流程供参考一.docx_第18页
第18页 / 共44页
车牌识别源代码部份流程供参考一.docx_第19页
第19页 / 共44页
车牌识别源代码部份流程供参考一.docx_第20页
第20页 / 共44页
亲,该文档总共44页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

车牌识别源代码部份流程供参考一.docx

《车牌识别源代码部份流程供参考一.docx》由会员分享,可在线阅读,更多相关《车牌识别源代码部份流程供参考一.docx(44页珍藏版)》请在冰点文库上搜索。

车牌识别源代码部份流程供参考一.docx

车牌识别源代码部份流程供参考一

车牌识别源代码部份流程(供参考一)

实际上车牌识别代码量并不是很大,如果不停的手工输入;用不到一天的时间,即可大功告成。

但是程序需要反复调试,才可以走向成熟。

所以工作量是很大的。

另外车牌识别还有其本身的特点——理论并不成熟。

这就给车牌识别带来了更多的工作量。

一般来说,开发车牌识别程序,先按照最原始最朴素的思想编码,然后是不断地优化。

这将贯彻到整个车牌识别的开发过程中去。

   

      车牌识别最基本的流程是:

将采集后的图像二值化,然后依次经过车牌定位、字符分割、去除干扰,最后是字符识别。

有时还会加入本节前面部分所叙述的思想(比如回溯)。

下面将分五章具体介绍每一个模块。

一、二值化

二值化是车牌识别的第一步。

二值化前后的对比如下图:

二值化的算法很简单,首先有一个亮度的阈值(threshold),对每一个像素的亮度和这个阈值做比较,根据比较结果得出车牌的前景和背景。

用c/c++描述如下:

voidCLPR:

:

Binary(intthreshold)

{

inty;

for(y=0;y

{

intx;

for(x=0;x

{

unsignedcharred,green,blue;

GetPixel(red,green,blue,x,y);

intbright;

bright=red+green;

if(m_search_blue_plate)

{

if(bright<=threshold)

SetBinary(x,y,BACKGROUND);

else

SetBinary(x,y,FOREGOUND);

}

else//wearesearchingyellowplate

{

if(bright>=threshold)

SetBinary(x,y,FOREGOUND);

else

SetBinary(x,y,BACKGROUND);

}

}

}

}

二值化算法虽然简单,但是阈值却不容易寻找。

本章后面的部分,将重点介绍各种求解阈值的算法。

1、OTSU算法

OTSU算法的思想是:

把输入图像首先转换成灰度图象,然后对图像进行直方图分析。

如果直方图呈双峰分布。

那么双峰之间的“谷”就是阈值。

从统计学角度讲,阈值两边的距离最大。

由于车牌识别的特殊性,图象象素点的亮度为该象素点的红色分量和绿色分量的和,并且忽略蓝色分量。

这一点对蓝色车牌和黄色车牌都是适用的。

OTSU算法仅对直方图呈双峰分布的图像有效。

全部代码如下:

voidLPR:

:

OTSU()

{

//直方图统计

{

intindex;

for(index=0;index

m_pixel_number[index]=0;

}

{

inty;

for(y=0;y<=m_height;y++)

{

intx;

for(x=0;x<=m_width;x++)

{

intbright;

bright=Bright(x,y);

m_pixel_number[bright]++;

}

}

}

//真正求阈值

doublesum;

sum=0;

intn;

n=0;

intk;

for(k=0;k<=(m_bright_level_count-1);k++)

{

sum+=k*m_pixel_number[k];

n+=m_pixel_number[k];

}

doublec_sum;

c_sum=0.0;

doublef_max;

f_max=-1.0;

intn1;

n1=0;

for(k=0;k<(m_bright_level_count-1);k++)

{

n1+=m_pixel_number[k];

if(n1==0)

continue;

intn2;

n2=n-n1;

if(n2==0)

break;

c_sum+=(double)k*m_pixel_number[k];

doublem_1,m_2;

m_1=c_sum/n1;

m_2=(sum-c_sum)/n2;

doublesb;

sb=(m_1-m_2)*(m_1-m_2)*(double)n1*(double)n2;

if(f_max

{

f_max=sb;

m_prepare_threhold=(int)(k+0.5);

}

}

}

2、Matlab算法

使用Matlab进行车牌识别,也是一个比较好的选择。

在Matlab的环境中首先把输入的彩色图像使用命令rgb2gray转换成灰度图像。

有了灰度图像就可以使用命令graythresh获得阈值了。

最后使用命令im2bw对图像进行二值化。

十分方便!

代码如下:

I=imread('blood1.tif');

imhist(I);

%人工观察灰度直方图,发现灰度120处有谷,确定阈值T=120

I1=im2bw(I,120/255);

%im2bw函数需要将灰度值转换到[0,1]范围内

figure,imshow(I1);

改进为

I=imread('blood1.tif');

imhist(I);

I1=graythresh(I);

%im2bw函数需要将灰度值转换到[0,1]范围内

figure,imshow(I1);

二、车牌定位

图像二值化,占用车牌识别中的大部分时间,也是最难的。

在二值化之后,车牌识别将变得比较难。

车牌定位为二值化后的第一步。

下面分小节分别介绍各种车牌定位算法。

车牌识别过程中,角点定位的基本思想是。

在所有的边界点中,如果某些点的曲率半径比较小,那么这些点叫做“角点”:

如下图所示(角点用红点表示):

图中字符上和车牌的四角都有角点。

但是这并不影响车牌的定位。

根据距离最大的四个角点,得到了车牌的四个角,从而定了车牌。

从角点定位的原理看出,如果经过旋转后车牌并不会影响角点定位的成功率和速度。

该算法的实现可以采取遍历匹配的算法,实现如下:

voidLPR:

:

GetConere()

{

inty;

for(y=0;y

{

intx;

for(x=0;x

{

if(Line(x,y,x+4,y)>=3)

{

if(Line(x,y,x,y+4)>=3)

{

if(Line(x+1,y+1,x+4,y+4)<=1)

Add(x,y,LEFT_TOP_CONNER);

}

if(Line(x,y,x,y-4)>=3)

{

if(Line(x+1,y-1,x+4,y-4)<=1)

Add(x,y,LEFT_DOWN_CONNER);

}

}

if(Line(x,y,x-4,y)>=3)

{

if(Line(x,y,x,y+4)>=3)

{

if(Line(x-1,y+1,x-4,y+4)<=1)

Add(x,y,RIGHT_TOP_CONNER);

}

if(Line(x,y,x,y-4)>=3)

{

if(Line(x-1,y-1,x-4,y-4)<=1)

Add(x,y,RIGHT_DOWN_CONNER);

}

}

}

}

}

函数Line(x1,y1,x2,y2)返回过两点(x1,y1),(x2,y2)的直线,前景的象素个数。

注意这里的4,是检验角点的区域范围,如果区域过大,图像旋转时就会影响车牌定位的成功率。

1、上下定位方法

仔细观察二值化后的图像,在车牌的上边和下边各有一条较长的背景线(上图用红线表示)。

根据这两条背景线可以准确的定位车牌。

定位算法如下:

intLPR:

:

HorizontalLine(intx,inty,intcount)

{

intret;

ret=0;

intx_loop;

for(x_loop=0;x_loop

if(!

IsForegournd(x+x_loop,y))

ret++;

returnret;

}

IsForegournd(x,y)为询问点(x,y)是不是前景点的函数。

该算法比角点定位算法要快,但是不适合经过旋转后的车牌。

虽然经过改进后也可以识别出旋转后的车牌,但是速度很慢,不能出现在成熟的产品中。

2、变化率定位法

根据尺寸分割

      从理论上讲,图片和实物相比,尺寸上有了很大的变化。

并不一定图片和实物几何意义上的相似。

也就是说未必图像和实物成比例。

但是实验证实,在一到两个像素范围内,在水平方向上,实物和图像基本上成比例。

请观察下图:

这个是来自于《GA36-2007中华人民共和国机动车号牌》的车牌尺寸说明。

根据这幅图片,我们可以在一定位的车牌上,找到各个字符的坐标。

请看下面的代码:

voidGetCharacterPosition(intcharacter_position[7],intplate_left,intplate_right)

{

staticconstintmm[]=

{

3+45/2,

character_position_mm[0]+12+45,

character_position_mm[1]+12+10+12+45,

character_position_mm[2]+12+45,

character_position_mm[3]+12+45,

character_position_mm[4]+12+45,

character_position_mm[5]+12+45,

character_position_mm[6]+45/2+1,

};

intindex;

for(index=0;index

character_position[index]=plate_left+(-plate_left+plate_right)*mm[index]/mm[sizeof(mm)/sizeof(mm[0])-1];

}

上面的代码技巧性很大,还需大家认真揣摩。

去除干扰

干扰车牌识别的因素很多。

比如车牌旋转、污染、固定螺丝和车牌边框等等。

下面分小节分别介绍去除各种干扰的方法。

去除噪音

去除噪音的原理是:

每一个字符,都是很大的一个连续块,但是噪音确是比较小的多个连续块。

通过递归算法得到每个字符中各个块的大小,保留最大的块,其余块当作噪音删掉。

算法很简单,这里不再列出代码。

去除螺丝干扰

去除螺丝的工作要和字符本身的特征联系在一起。

对不同的字符、相同字符不同部位的螺丝,都要分别编码。

工作量很大。

下面分小节举几个例子:

10986等左上方螺丝

这种情况如下图所示:

                        

在比较圆的9的上部出现了一个螺丝,比较明显。

可以删掉,代码如下:

voidCLPR:

:

DeleteLeftUpSmallScrewCharacterRound()

{

boolpossible_screw;

intpossible_screw_start;

intpossible_screw_end;

if(m_character_index==5)

{

possible_screw=false;

{

intseek;

for(seek=0;seek<=1;seek++)

{

possible_screw_start=m_min_y+seek;

intscrew_right;

if(ExpandHorizontal(screw_right,possible_screw_start,possible_screw_start,m_max_x,SUB))

{

if(screw_right>=m_delta_x/2)

{

possible_screw=true;

break;

}

}

}

}

intdelta_x;

if(possible_screw)

{

possible_screw=false;

for(possible_screw_end=possible_screw_start+1;possible_screw_end

{

delta_x=RecognizeCharacterLocateVeritcalSmallSideScrewGetDeltaX(true,possible_screw_end,true);

intleft;

if(!

ExpandHorizontal(left,possible_screw_end,possible_screw_end,m_min_x,ADD))

continue;

if(left>m_delta_x/2)

continue;

intright;

if(!

ExpandHorizontal(right,possible_screw_end,possible_screw_end,m_max_x,SUB))

continue;

if(right>delta_x/2)

continue;

if(possible_screw_end+4>m_center_y)

continue;

intdistance1;

distance1=HorizontalDistance(possible_screw_end,possible_screw_end);

intdistance2;

distance2=HorizontalDistance(possible_screw_end+2,possible_screw_end+2);

intdistance3;

distance3=HorizontalDistance(possible_screw_end+4,possible_screw_end+4);

if(!

(distance1<=distance2&&distance2<=distance3&&distance10))

continue;

if(distance3

continue;

intseek;

for(seek=2;seek<=4;seek++)

{

if(ChangeTimeHorizontal(possible_screw_end+seek,possible_screw_end+seek)==4)

{

possible_screw=true;

break;

}

}

if(possible_screw)

break;

}

}

if(possible_screw)

{

intleft;

if(!

ExpandHorizontal(left,possible_screw_end-1,possible_screw_end-1,m_min_x,ADD))

possible_screw=false;

else

{

if(left>=m_delta_x/2)

possible_screw=false;

}

}

if(possible_screw)

{

boolknown_letter;

known_letter=false;

if(!

known_letter)

{

m_min_y=possible_screw_end;

RecognizeCharacterAdjustVertical();

}

}

}

}

以上代码拷贝自深职院张教授车牌识别工程(索威尔),实际上是2003年的版本。

读者可以进行修改,并放入自己工程中。

2EFT5等右上方螺丝

这种情况如下图所示:

                     

图中有三条直线,如果沿着这三条直线扫描的话,会发现:

黑色的扫描线前景占很大的比例,绿色的扫描线背景占很大的比例;红色的扫描线前景背景比例都不突出,但是前景背景交替较为频繁。

这说明根据前景背景变化率可以定位车牌。

这种算法可以有效防止车牌旋转的干扰。

获得变化率的代码如下:

doubleCLPR:

:

ScanLine(intx,inty,intcount)

{

boolcurrent_foreground;

current_foreground=IsForeground(x,y);

intchange_times;

change_times=0;

intloop;

for(loop=1;loop

{

if(current_foreground)

{

if(!

IsForeground(x+loop,y))

{

change_times++;

current_foreground=false;

}

}

else

{

if(IsForeground(x+loop,y))

{

change_times++;

current_foreground=true;

}

}

}

doubleret;

ret=change_times;

ret/=(count+1);//donotdiv0

returnret;

}

三、字符分割

在车牌识别过程中,车牌定位后的工作便是分割字符。

本章将分若干节介绍字符分割的算法。

连续点分割法

对于一个数字或者字母,前景的点是连续的。

用填充算法对种子点填充即可得到整个字符。

当然汉字就不是了,所以这种分割算法仅仅适合数字或者字母。

但是当整个车牌的数字和字母都得到之后,剩下的那个必定是汉字。

这个思想用c/c++描述如下:

voidLPR:

:

Scan()

{

inty;

for(y=m_plate_top;y<=m_plate_bottom;y++)

{

intx;

for(x=m_plate_left;x<=m_plate_right;x++)

{

if(IsForeGround(x,y)&&!

IsVisited(x,y))

{

Fill(x,y);

}

}

}

}

Fill是种子填充,算法有多种。

下面分别介绍。

1、递归填充算法

递归填充算法的基本思想是,首先访问当前点,然后访问当前点的四个邻居。

每次访问时,要做一个记号,否则递归过程无法结束。

具体到车牌识别,每访问一个点,还要记录该点的坐标,从而得到当前字符点的集合。

voidLPR:

:

Fill(intx,inty)

{

if(!

IsForground(x,y))

return;

if(IsVisited(x,y))

return;

AddPixelToCharacter(x,y);

MarkPixelVisited(x,y);

Fill(x+1,y);

Fill(x,y+1);

Fill(x-1,y);

Fill(x,y-1);

}

可以看出用c/c++描述的填充算法十分简单。

2、递归扫描线算法

基本的递归填充算法,函数递归调用较为频繁,引起系统资源消耗巨大。

人们后来又提出了改进了的“扫描线种子填充算法”。

其基本思想是:

在种子的左右两边水平扫描暂时不需要递归,以减少不必要的函数调用。

水平扫描结束后,仅仅考虑扫描线两个端点即可。

算法如下:

voidLPR:

:

Fill(intx,inty)

{

int(*stack)[2];

stack=(int(*)[2])newint[m_width*2];

intstack_length;

stack_length=0;

stack[stack_length][0]=x;

stack[stack_length][1]=y;

stack_length++;

while(true)

{

if(stack_length==0)

break;

x=stack[stack_length-1][0];

y=stack[stack_length-1][1];

stack_length--;

intleft_x;

for(left_x=x-1;left_x>=0;left_x--)

if(!

IsForeGround(left_x,y)||IsVisited(left_x,y))

break;

left_x++;

for(x=left_x;x

{

if(!

IsForeGround(x,y)||IsVisited(left_x,y))

break;

Visit(x,y);

}

intright_x;

right_x=x;

if(right_x>=m_width)

right_x=m_width-1;

left_x--;

if(left_x<0)

left_x=0;

intdown_y;

down_y=y+1;

if(down_y

{

for(x=left_x;x<=right_x;x++)

{

if(IsForeGround(x,down_y)&&!

IsVisited(left_x,y))

{

stack[stack_length][0]=x;

stack[stack_length][1]=down_y;

stack_length++;

}

}

}

intup_y;

up_y=y-1;

if(up_y>=0)

{

for(x=left_x;x<=right_x;x++)

{

if(IsForeGround(x,up_y)&&!

IsVisited(left_x,y))

{

stack[stack_length][0]=x;

stack[stack_length][1]=up_y;

stack_length++;

}

}

}

}

delete[](int*)stack;

}

虽然扫描线种子填充算法,比基本的种子填充算法复杂一些,但是在车牌识别测试后发现,扫描线种子填充算法比种子填充算法要快三分之一左右。

边界法

边界法和连续点分割法很相似,只不过连续的边界点,而不是所有的前景点。

边界法需要事先得到前景像素中的边界点,

这可能会花费一些时间。

但是这样会加速得到连续点的递归操作。

边界点如下图:

得到边界点的算法如下:

voidCLPR:

:

GetEdge()

{

inty;

for(y=0;y

{

intx;

for(x=0;x

{

if(IsForeg

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2