车牌识别Matlab算法详解.docx
《车牌识别Matlab算法详解.docx》由会员分享,可在线阅读,更多相关《车牌识别Matlab算法详解.docx(23页珍藏版)》请在冰点文库上搜索。
车牌识别Matlab算法详解
生成界面时注意事项
1、生成文件主菜单和打开、关闭子菜单不一样的地方,在于文件主菜单不对应实际执行代码,所以在Callback回调函数,这一项,可将其删除为空。
2、在关闭MenuEditor之前,先保存正在编辑的.fig文件,让其自动生成或者更新对应的.m文件
3、在.fig文件编辑器的空白处,双击,弹出PropertyInspector,在其中更改
Resize属性为on,表示窗体大小可以更改;
更改Units的单位为pixels;
Tag的名称代表窗体的名称,默认为figure1,另取一个名称为mainFrm.
注意在关闭PropertyInspector之前总是先保存.fig文件
双击之
4、假如程序运行出了问题,可先在Commandwindow中查看错误说明,根据提示修改。
或者打开.m源代码文件,重新编译运行。
注意在运行之前使用命令窗口中的clc命令清空内存。
5、现在一行一行来解释源代码。
第一行:
functionvarargout=LicenseRecognition(varargin)
分别表示主函数的输出、函数名称、函数输入
在%Begininitializationcode-DONOTEDIT
%Endinitializationcode-DONOTEDIT
这两行注释符之前的代码是编辑菜单时自动生成的,不需要修改
functionLicenseRecognition_OpeningFcn(hObject,eventdata,handles,varargin)
%系统自动生成
handles.output=hObject;%系统自动生成
——————————————————————————————————
%以下代码为手工添加,表示使用handles结构体来保存图形界面中各种对象的句柄,或者中间结果,这些句柄或者中间结果在创建图形对象或运算中产生,需要在以后的回调函数中多次用到,所以这里的作用类似于全局变量的作用,用来进行数据的传递。
handles.imgIn=[];
handles.imgOut=[];
handles.flag=0;%判断标志
handles.pos=[];%图形对象所在位置
handles.lef=1;
handles.top=1;
handles.wid=0;
handles.hig=0;
——————————————————————————————————
%Updatehandlesstructure自动生成,用来将上面定义的数据
guidata(hObject,handles);
%自动生成,定义输出
%---Outputsfromthisfunctionarereturnedtothecommandline.
functionvarargout=LicenseRecognition_OutputFcn(hObject,eventdata,handles)
varargout{1}=handles.output;
第一步:
打开读取文件
%--------------------------------------------------------------------
functionMenu_File_Open_Callback(hObject,eventdata,handles)%该行为自动生成
[fname,pname]=uigetfile({'*.bmp;*.jpg;*.tif','AllIMAGEFiles(*.bmp,*.jpg,*.tif)'},'Openaninputimage');%弹出打开文件的对话框
strPath=[pnamefname];%设置文件名和路径名
if~isempty(strPath)
[img,cmap]=imread(strPath);%读取文件数据
[hig,wid,page]=size(img);%获得图像大小
ifpage==3%假设图像是RGB彩色
imgIn=rgb2gray(img);%转换为灰度图象
else
imgIn=img;
end
handles.imgIn=imgIn;%将结果赋给全局变量,以便交给下一个函数处理
handles.flag=1%标志为1表示数据已经读取
%pos=get(handles.mainFrm,'Position');
figure(handles.mainFrm);%创建图形界面,用来显示图像
set(handles.mainFrm,'Position',[400,300,wid,hig]);%设置该图形界面的显示位置
image(imgIn);%显示图像,其和imshow功能类似,但带有默认的调色板
colormap(gray(256));%将其默认调色板更改为256级灰度
handles.pos=[400,300,wid,hig];%将图形界面的位置保存到hdl结构体中
guidata(hObject,handles);%保存图形界面数据
set(handles.Menu_EdgeDetect,'Enable','On');%设置下一个菜单为可点击的亮色
end
%完成菜单退出功能--------------------------------------------------------------------
functionMenu_File_Exit_Callback(hObject,eventdata,handles)
ifhandles.flag==1
str={'Areyousureaboutexitingtheprogram?
','Ifso,thedatawillbelost!
'};
ret=questdlg(str,'Warning');
switchret
case'Yes'
delete(gcf);
case'No','Cancel'
return;
end
end
%第二步:
边缘检测功能实现
--------------------------------------------------------------------
functionMenu_EdgeDetect_Callback(hObject,eventdata,handles)
if~isempty(handles.imgIn)%检查图像数据是否非空
imgIn=double(handles.imgIn);%在处理之前将图像数据类型转换为double型
%BW=uint8(255*edge(handles.imgIn,'roberts'));
%handles.imgOut=BW;
%guidata(hObject,handles);
%set(handles.mainFrm,'Position',handles.pos);
%image(BW);
%colormap(gray(256));
filt=fspecial('sobel');%构建soble算子的模板
horz=conv2(imgIn,filt,'same');%进行卷积运算
%Cs=conv2(A,B,'same')CsisthesamesizeasA:
3-by-3
vert=conv2(imgIn,filt','same');
imgOut=uint8(max(horz,vert));%取水平和垂直方向运算结果的最大值
%imgOut=uint8(sqrt(horz.^2+vert.^2));
handles.imgOut=imgOut;
handles.flag=1;
guidata(hObject,handles);
figure(handles.mainFrm);
set(handles.mainFrm,'Position',handles.pos);
image(imgOut);
colormap(gray(256));
set(handles.Menu_RoughLocate,'Enable','On');
set(handles.Menu_FineLocate,'Enable','Off');
set(handles.Menu_CharacterCutout,'Enable','Off');
set(handles.Menu_LicenseRecognition,'Enable','Off');
end
第三步:
车牌粗定位
%车牌粗定位:
根据车牌区域在水平方向灰度值具有明显频繁的跳变,可求边缘提取后水平方向的差分,然后进行水平方向上的投影,也即沿水平方向进行相邻像素差分值的累加,绘制的投影图横轴为图像高度,原点为左上角,纵轴即为沿水平方向的差分值累加和。
--------------------------------------------------------------------
functionMenu_RoughLocate_Callback(hObject,eventdata,handles)
if~isempty(handles.imgOut)
imgIn=double(handles.imgOut);
[hig,wid]=size(imgIn);
diff_horz=zeros(hig,wid);%构建一个与图像大小一样的全为0的矩阵,存储水平方向相邻像素的差值
diff_horz=abs(imgIn(:
1:
wid-1)-imgIn(:
2:
wid));%类似第1列像素减第2列像素,第2列减第3列,依次减下去
cum_horz=sum(diff_horz');%对图像矩阵先转置,再投影
figure;%绘制投影图形,横轴为1到图像高,纵轴为水平方向差分值累计和
bar(1:
hig,cum_horz,'r');
title('horizontalprojection');
%diff_vert=zeros(hig,wid);
%diff_vert=abs(imgIn(1:
hig-1,:
)-imgIn(2:
hig,:
));
%cum_vert=sum(diff_vert);
%figure;
%bar(1:
wid,cum_vert,'b');
%title('verticalprojection');
%通过观察投影图,给出车牌的大概位置是,左上角坐标为(85,225),宽度为60,高度为20。
lef=85;top=225;wid=60;hig=20;
handles.lef=lef;%将检测结果传递给hdl结构体,以传递到下一个微定位的函数
handles.top=top;
handles.wid=wid;
handles.hig=hig;
imgOut=uint8(imgIn(top:
top+hig-1,lef:
lef+wid-1));%按刚才给的位置大小提取出车牌,转换为无符号整型,保存到imgOut中
handles.imgOut=imgOut;
handles.flag=1;
guidata(hObject,handles);
figure(handles.mainFrm);
set(handles.mainFrm,'Position',handles.pos);
image(imgOut);
colormap(gray(256));
axisoff;
set(handles.Menu_FineLocate,'Enable','On');
set(handles.Menu_RoughLocate,'Enable','Off');
set(handles.Menu_CharacterCutout,'Enable','Off');
set(handles.Menu_LicenseRecognition,'Enable','Off');
end
第四步:
车牌微定位
如图所示,在粗定位结果的基础上,还需要把红色边框外的图像去掉,以进一步确定字符范围,缩减车牌的左右上下边界,以便后续字符处理。
--------------------------------------------------------------------
functionMenu_FineLocate_Callback(hObject,eventdata,handles)
if~isempty(handles.imgOut)
imgIn=double(handles.imgOut);%传入粗定位的结果图像
[hig,wid]=size(imgIn);%取图像大小
lef_tem=wid*ones(hig,1);%构建一个hig行,1列的值全为1的矩阵,与wid相乘,矩阵值全部为wid
rig_tem=zeros(hig,1);%构建一个hig*1的0矩阵
fori=1:
hig
forj=1:
wid-1%从左到右扫描,遇到相邻像素的灰度值差值大于60时,停止扫描,记下列号,说明此列是车牌左边界
tem=imgIn(i,j+1)-imgIn(i,j);
iftem>=60
lef_tem(i)=j+1;
break;
end
end
%从右向左扫描,遇到相邻像素的灰度值差值大于60时,停止扫描,记下列号,说明此列是车牌右边界
forj=wid:
-1:
2
tem=imgIn(i,j-1)-imgIn(i,j);
iftem>=60
rig_tem(i)=j-1;
break;
end
end
end4
lef=min(lef_tem);%每一行都可以扫描得到一个左边界,取其中最小的
rig=max(rig_tem);%每一行都可以扫描得到一个右边界,取其中最大的
%这也是lef_tem、rig_tem在初始化定义的时候分别为ones全为1的矩阵和全为zeros0的矩阵的原因
top_tem=hig*ones(wid,1);
bot_tem=zeros(wid,1);
forj=lef:
rig
fori=1:
hig-1
tem=imgIn(i+1,j)-imgIn(i,j);
iftem>=60
top_tem(j)=i+1;
break;
end
end
fori=hig:
-1:
2
tem=imgIn(i-1,j)-imgIn(i,j);
iftem>=60
bot_tem(j)=i-1;
break;
end
end
end
top=min(top_tem);%按同样的方法找到上下边界,注意图像左上角为原点
bot=max(bot_tem);
handles.lef=handles.lef+lef-1;%在原始图像上定位微定位后车牌的位置
handles.top=handles.top+top-1;
handles.wid=rig-lef+1;
handles.hig=bot-top+1;
%将微定位后的图像数据取出来
imgOut=uint8(imgIn(top:
bot,lef:
rig));
handles.imgOut=imgOut;
handles.flag=1;
guidata(hObject,handles);
figure(handles.mainFrm);
set(handles.mainFrm,'Position',handles.pos);
image(imgOut);
colormap(gray(256));
axisoff;
set(handles.Menu_CharacterCutout,'Enable','On');
set(handles.Menu_RoughLocate,'Enable','Off');
set(handles.Menu_FineLocate,'Enable','Off');
set(handles.Menu_LicenseRecognition,'Enable','Off');
end
第五步:
字符分割
%--------------------------------------------------------------------
functionMenu_CharacterCutout_Callback(hObject,eventdata,handles)
ifhandles.wid~=0
lef=handles.lef;%注意:
这里是微定位后车牌在原始图像上的位置
top=handles.top;
wid=handles.wid;
hig=handles.hig;
%此处imgIn是在原始图像上取微定位后车牌位置区域的数据产生的
imgIn=double(handles.imgIn(top:
top+hig-1,lef:
lef+wid-1));
%maxvalue=max(imgIn(:
));
%minvalue=min(imgIn(:
));
%imgOut=~(imgIn>=(maxvalue+minvalue+30)/2);
imgOut=~JudgeAnalysis(imgIn);%对图像进行二值化,然后反转
[label,num]=bwlabel(imgOut);%对二值图像进行连通成分的标记
%对于以上两个中间变量的结果,大家可以在Matlab的workspace中查看其结果变化,理解
k=0;
fori=1:
num%以下代码为找到像素总数小于10的连通成分,剔除,这里6个字符,但有7个连通成分,剔除其中较少像素的连通成分
tem=(label==i);%把所有label为i的像素统计出来
ifsum(tem(:
))<=10
imgOut(find(label==i))=0;
k=k+1;
end
end
num=num-k;%减去剔除的连通成分的个数
proj_vert=sum(imgOut);%对图像每一列求和,也即垂直投影
ifproj_vert
(1)~=0%如果第一列的投影不为0,说明第一列的像素中有字符
coordx
(1)=1;
k=1;
else
k=0;
end
%以下代码说明:
如果第后一列像素为0,而前一列的像素不为0,则说明此处是字符的右边界
%如果前一列像素为0,后一列不为0,说明此处是字符的左边界
forj=2:
wid-1
tem1=proj_vert(j-1)-proj_vert(j);
tem2=proj_vert(j+1)-proj_vert(j);
if(proj_vert(j)==0)&(tem1>=1)
k=k+1;
coordx(k)=j-1;
elseif(proj_vert(j)==0)&(tem2>=1)
k=k+1;
coordx(k)=j+1;
end
end
%如果倒数第二列不为0,倒数第1列为0,则说明倒数第2列为边界
if(proj_vert(wid-1)~=0)&(proj_vert(wid)==0)
k=k+1;
coordx(k)=wid-1;
end
%k代表总共有k条边界
k=length(coordx);
set(handles.mainFrm,'Position',handles.pos);
image(uint8(255*(~imgOut)));%将其反转回来在0-255之间显示
colormap(gray(256));
axisoff;
holdon;
forj=1:
k
x=coordx(j)*ones(1,hig);%将x的数目变成和y一样多,组成(x,y)坐标点对
y=1:
hig;
plot(x,y,'b');
holdon;
end
holdoff;
%根据上面所找边界线,对字符进行实际的分割
forj=1:
num
tem_wid(j)=coordx(2*j)-coordx(2*j-1)+1;%每个字符由一对边界线分割而成,由右边边界线减去左边边界线的位置,得到的是此字符的宽度
tem=imgOut(:
coordx(2*j-1):
coordx(2*j));%取出每一字符的实际数据
proj_horz=sum(tem');%对该字符的行求累加值
fork=1:
hig%从上往下扫描,找出第j个字符的上边界
ifproj_horz(k)~=0
tem1=k;
coordy(j,1)=tem1;
break;
end
end
fork=hig:
-1:
1%从下往上扫描,找出第j个字符的下边界
ifproj_horz(k)~=0
tem2=k;
coordy(j,2)=tem2;
break;
end
end
tem_hig(j)=tem2-tem1+1;%找出每一字符的高度
end
%以最大的宽和高来统一每个字符的大小
maxwid=max(tem_wid);%总共有6个字符,找出其中最宽的
maxhig=max(tem_hig);%总共有6个字符,找出其中最高的
fork=1:
num
lef=coordx(2*k-1);rig=coordx(2*k);
top=coordy(k,1);bot=coordy(k,2);
wid=rig-lef+1;hig=bot-top+1;
Norm_Char(1:
maxhig,1:
maxwid,k)=ones(maxhig,maxwid);%初始化为大小统一
Norm_Char(1:
hig,1:
wid,k)