%% 清空环境变量和命令窗口clc;clear;close all;%% 定义全局变量Aglobal A%% 读入车牌图片并显示I=imread('9.jpeg');figure(1);imshow(I);title('原图');%%if length(size(I))==3I1 = rgb2gray(I); %转换为灰度图elseI1 =I;endfigure(2),subplot(1,2,1),imshow(I1);title('灰度图');figure(2),subplot(1,2,2),imhist(I1);title('灰度图直方图');%% canny算子边缘检测I2=edge(I1,'canny',[0.2,0.55]);figure(3),imshow(I2);title('canny算子边缘检测')%% 腐蚀se=[1;1;1];I3=imerode(I2,se);figure(4),imshow(I3);title('腐蚀后图像');%% 平滑se=strel('rectangle',[40,40]);I4=imclose(I3,se);figure(5),imshow(I4);title('平滑图像的轮廓');%% 从对象中移除小对象I5=bwareaopen(I4,1000);figure(6),imshow(I5);title('从对象中移除小对象');[y,x,z]=size(I5);myI=double(I5);%% begin横向扫描Blue_y=zeros(y,1);for i=1:yfor j=1:xif(myI(i,j,1)==1)%如果myI(i,j,1)即myI图像中坐标为(i,j)的点为蓝色%则Blue_y的相应行的元素white_y(i,1)值加1Blue_y(i,1)= Blue_y(i,1)+1; %蓝色像素点统计endendend[temp ,MaxY]=max(Blue_y);%temp为向量white_y的元素中的最大值,MaxY为该值的索引( 在向量中的位置)PY1=MaxY;while ((Blue_y(PY1,1)>=50)&&(PY1>1))PY1=PY1-1;endPY2=MaxY;while ((Blue_y(PY2,1)>=10)&&(PY2<y))PY2=PY2+1;endIY=I(PY1:PY2,:,:);%IY为原始图像I中截取的纵坐标在PY1:PY2之间的部分%end横向扫描%% begin纵向扫描Blue_x=zeros(1,x); %进一步确定x方向的车牌区域for j=1:xfor i=PY1:PY2if(myI(i,j,1)==1)Blue_x(1,j)= Blue_x(1,j)+1;endendendPX1=1;while ((Blue_x(1,PX1)<3)&&(PX1<x))PX1=PX1+1;endPX2=x;while ((Blue_x(1,PX2)<3)&&(PX2>PX1))PX2=PX2-1;end%这里的参数需要调整,裁掉车牌左边一条竖线。%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%PX1=PX1+10;PX2=PX2;PY1=PY1-3;PY2=PY2-8;%end纵向扫描dw=I(PY1:PY2,PX1:PX2,:);imwrite(dw,'dw.jpg'); %将图像数据写入图像中figure(7),subplot(1,2,1),imshow(IY),title('行方向合理区域');figure(7),subplot(1,2,2),imshow(dw),title('定位剪切后的彩色车牌图像')%% 利用radon变换做水平矫正bw=rgb2gray(dw);bw1=edge(bw,'sobel','horizontal');theta=0:179;r=radon(bw1,theta);[m,n]=size(r);c=1;for i=1:mfor j=1:nif r(1,1)<r(i,j)r(1,1)=r(i,j);c=j;endendendrot=90-c+2;pic=imrotate(bw,rot,'crop');figure(8),subplot(3,1,1),imshow(bw),title('1.定位后的这牌灰度图像');figure(8),subplot(3,1,2),imshow(pic),title('2.利用radon算子做水平方向矫正');%% 垂直方向校正if y>mbw2=edge(pic,'sobel','vertical');theta2=-45:45;r2=radon(bw2,theta2);[m2,n2]=size(r2);c2=1;for j2=1:n2for i2=1:m2if r2(1,1)<r2(i2,j2)r2(1,1)=r2(i2,j2);c2=i2endendendrot2=c2-45T=affine2d([1,0,0; tan(-rot2),1,0;0,0,1]);pic=imwarp(pic,T);figure(8),subplot(3,1,3), imshow(pic);title('3.利用radon算子做垂直方向矫正');end% pic2=imrotate(bw2,-rot2,'bilinear','crop');%% figure(8),subplot(3,1,3),imshow(pic2),title('2.利用radon算子做垂直方向矫正');%% 利用radon变换做垂直方向的矫正% binaryImage = edge(pic,'canny');% binaryImage = bwmorph(binaryImage,'thicken');% theta = -45:44;% [R,xp] = radon(binaryImage,theta);%% [R1,r_max] = max(R);% theta_max = 60;% while (theta_max > 50 || theta_max<-50)% [R2,theta_max] = max(R1);% R1(theta_max) = 0;% theta_max = theta_max -45;% end%角度计算完毕% H=[1,0,0; tan(-theta_max),1,0;0,0,1];% T=affine2d([1,0,0; tan(-theta_max),1,0;0,0,1]);% pic=imwarp(pic,T);% figure(8),subplot(3,1,3), imshow(pic);title('3.利用radon算子做垂直方向矫正');%% 字符分割前的预处理g_max=double(max(max(pic)));g_min=double(min(min(pic)));T=round(g_max-(g_max-g_min)/3); % T 为二值化的阈值[m,n]=size(pic);d=im2bw(pic,T/256); % d:二值图像figure(9);subplot(2,3,1),imshow(d),title('1.车牌二值图像')%% 滤波h=fspecial('average',3);d=im2bw(round(filter2(h,d)));figure(9),subplot(2,3,2),imshow(d),title('2.均值滤波后')%% 某些图像进行操作% 膨胀或腐蚀se=strel('square',3); % 使用一个3X3的正方形结果元素对象对创建的图像膨胀se=eye(2); % eye(n) returns the n-by-n identity matrix 单位矩阵[m,n]=size(d);if bwarea(d)/m/n>=0.365d=imerode(d,se);elseif bwarea(d)/m/n<=0.235d=imdilate(d,se);endfigure(9),subplot(2,3,3),imshow(d),title('3.膨胀或腐蚀处理后')%% 再次平滑+移除小对象se=strel('rectangle',[4,4]);d=imclose(d,se);figure(9),subplot(2,3,4),imshow(d);title('4.平滑图像的轮廓');d=bwareaopen(d,50);figure(9),subplot(2,3,5),imshow(d);title('5.再次从对象中移除小对象');%% 对图像的边框进行裁剪,只留下有效字符部分[y1,x1,z1]=size(d);I3=double(d);TT=1;Y1=zeros(y1,1);for i=1:y1for j=1:x1if(I3(i,j,1)==1)Y1(i,1)= Y1(i,1)+1 ;endendendPy1=1;Py0=1;while ((Y1(Py0,1)<30)&&(Py0<y1))Py0=Py0+1;endPy1=Py0;while((Y1(Py1,1)>=30)&&(Py1<y1))Py1=Py1+1;endd=d(Py0:Py1,:,:);figure(9),subplot(2,3,6);imshow(d),title('6.目标车牌区域');%% 在列方向上进行灰度值累加X1=zeros(1,x1);for j=1:x1for i=1:y1if(I3(i,j,1)==1)X1(1,j)= X1(1,j)+1;endendendflag1=0;flag2=0;kkk=0;kkkk=0;for k=2:x1if (X1(1,k-1)~=0)&&(X1(1,k)==0)&&(flag1 ==0)kkk=k;flag1=1;endif (X1(1,k)~=0)&&(X1(1,k-1)==0)&&(flag2 ==0)kkkk=k;flag2=1;endif kkkk-kkk>50break;endendfigure(10);plot(0:x1-1,X1),title('列方向像素点灰度值累计和'),xlabel('列值'),ylabel('累计像素量');% close all;%% 确定车牌起始位置Px0=1;Px1=1;if flag1==1 && flag2==1Px0=kkkk;Px1=kkkk;end%% 分割字符for i=1:7while ((X1(1,Px0)<8)&&(Px0<x1))Px0=Px0+1;endPx1=Px0;while (((X1(1,Px1)>=7)&&(Px1<x1))||((Px1-Px0)<15))Px1=Px1+1;endZ=d(:,Px0:Px1,:);switch strcat('Z',num2str(i)) %拼接Z1~Z7case 'Z1'PIN0=Z;case 'Z2'PIN1=Z;case 'Z3'PIN2=Z;case 'Z4'PIN3=Z;case 'Z5'PIN4=Z;case 'Z6'PIN5=Z;otherwisePIN6=Z;endfigure(11);subplot(1,7,i);imshow(Z);Px0=Px1;end%% 载入训练好的神经网络load('-mat','ym');%% 将分割后的车牌字符归一化处理for ii=1:7switch iicase 1PIN0=pretreatment(PIN0);Z2=A;imwrite(Z2,'A.jpg');case 2PIN1=pretreatment(PIN1);Z2=A;case 3PIN2=pretreatment(PIN2);Z2=A;case 4PIN3=pretreatment(PIN3);Z2=A;case 5PIN4=pretreatment(PIN4);Z2=A;case 6PIN5=pretreatment(PIN5);Z2=A;otherwisePIN6=pretreatment(PIN6);Z2=A;endfigure(12);subplot(1,7,ii);imshow(Z2);endP0=[PIN0',PIN1',PIN2',PIN3',PIN4',PIN5',PIN6'];
1.imread()
方案一、imread(文件名,文件格式)
x=imread(test,'bmp');

方案二、imread(文件名)
I=imread('9.jpeg');
2.figure(1)、imshow()
figure(1):创建一个窗口。
imshow():显示图片。
如果直接imshow(),这个图片和下个图片都不使用figure(),前一个图片被覆盖。
3.size()
彩色图片size()会返回三个数,长、宽和3(3表示彩色RGB)。
length的3是三个数的3,而不是3色RGB的3.
rgb2gray()函数,把彩色图片转换为灰度图。
4.subplot()
绘制子图。数字表示几行,几列,目前在绘制第几个图。
subplot(2,2,3)
5.edge()
边缘检测。edge(图片,‘canny算法’,灵敏度【灵敏度上下限】)
6.imerode()
se即卷积模板,腐蚀的目的是:
具体可见下面链接。
se=[1;1;1];
I3=imerode(I2,se); %I2图像用[1,1,1]进行腐蚀。
7.imclose()
8.bwareaopen()
作用:移除小对象(以后续的一个步骤为例,可以见到牌的‘·’和一些干扰亮点被移除了)
I5=bwareaopen(I4,1000); %小于1000的亮点被视为干扰,被去掉
9.横向与纵向扫描函数
%% begin横向扫描
Blue_y=zeros(y,1);
%%%%%%%%%%%% zeros(y,1)即生成y行1列 %%%%%%%%%%%
for i=1:y
for j=1:x
if(myI(i,j,1)==1)
%如果myI(i,j,1)即myI图像中坐标为(i,j)的点为蓝色
%则Blue_y的相应行的元素white_y(i,1)值加1
Blue_y(i,1)= Blue_y(i,1)+1; %蓝色像素点统计
end
end
end
[temp ,MaxY]=max(Blue_y);
%%%%%%%% max()函数的作用 %%%%%%%%%%%%%%%%%%%
%temp为向量white_y的元素中的最大值,MaxY为该值的索引( 在向量中的位置)
PY1=MaxY;
while ((Blue_y(PY1,1)>=50)&&(PY1>1))
PY1=PY1-1;
end
PY2=MaxY;
while ((Blue_y(PY2,1)>=10)&&(PY2<y))
PY2=PY2+1;
end
IY=I(PY1:PY2,:,:);
%得到车牌蓝色区域上下边缘
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% begin纵向扫描
Blue_x=zeros(1,x); %进一步确定x方向的车牌区域
for j=1:x
for i=PY1:PY2
if(myI(i,j,1)==1)
Blue_x(1,j)= Blue_x(1,j)+1;
end
end
end
PX1=1;
while ((Blue_x(1,PX1)<3)&&(PX1<x))
PX1=PX1+1;
end
PX2=x;
while ((Blue_x(1,PX2)<3)&&(PX2>PX1))
PX2=PX2-1;
end
%得到车牌蓝色区域左右边缘
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%这里的参数需要调整,裁掉车牌左边一条竖线。
PX1=PX1+10;
PX2=PX2;
%PX1,PX2分别是车牌左右两侧
PY1=PY1-3;
PY2=PY2-8;
%PY1,PY2分别是车牌上下两侧
dw=I(PY1:PY2,PX1:PX2,:);
imwrite(dw,'dw.jpg'); %将图像数据写入图像中
figure(7),subplot(1,2,1),imshow(IY),title('行方向合理区域');
figure(7),subplot(1,2,2),imshow(dw),title('行+列定位剪切后的彩色车牌图像')
10.水平校正
3篇论文名称:
[1]吴丽丽,余春艳.基于Sobel算子和Radon变换的车牌倾斜校正方法[J].计算机应用,2013,33(S1):220-222.
[2]杜晓刚. 车牌识别系统中牌照定位、倾斜校正及字符分割技术的研究[D].中北大学,2013.
[3]贡丽霞,白艳萍.基于Radon变换和坎尼边缘检测的倾斜车牌校正方法研究[J].太原师范学院学报(自然科学版),2010,9(01):61-63.
%% 利用radon变换做水平矫正
bw=rgb2gray(dw); %彩色图片变灰度图
bw1=edge(bw,'sobel','horizontal'); %水平方向用sobel方法边缘检测
%效果见图1
theta=0:179; %水平方向一般角度0~180
r=radon(bw1,theta); %randon校正。//后续原理我也不清楚,硕士论文也没有交代很清楚,比如说这个r是什么意义,然后后面为什么要找最大值做角度之类的。
[m,n]=size(r); %m为行,n为列
c=1; %循环作用:找到最大值所在的列
for i=1:m
for j=1:n
if r(1,1)<r(i,j)
r(1,1)=r(i,j);
c=j;
end
end
end
rot=90-c+2; %确定水平旋转角
pic=imrotate(bw,rot,'crop'); %对图像进行旋转,crop是保持旋转前后图像大小不变
figure(8),subplot(3,1,1),imshow(bw),title('1.定位后的这牌灰度图像');
figure(8),subplot(3,1,2),imshow(pic),title('2.利用radon算子做水平方向矫正');

图1
图2
(论文3里面说水平校正原理是这样图3,不过我没看懂代码和这个的关系)
图3
11.垂直方向校正
if y>m %先判断是否需要再垂直方向校正,因为有些很正的车牌,不判断反而垂直校正会变歪,水平校正不会。原因不知。
%y是原始图片的高,m是水平变换后的图片的高。//其实这里应该有点限制的,不过我用的几张图片效果挺好就没再改动。
bw2=edge(pic,'sobel','vertical'); %同水平校正原理,只不过垂直校正角度-45~45(论文2里面说的)
theta2=-45:45;
r2=radon(bw2,theta2);
[m2,n2]=size(r2);
c2=1;
for j2=1:n2
for i2=1:m2
if r2(1,1)<r2(i2,j2)
r2(1,1)=r2(i2,j2);
c2=i2
end
end
end
rot2=c2-45 %获得垂直校正角度
T=affine2d([1,0,0; tan(-rot2),1,0;0,0,1]); %affine2d括号里面奇怪的数据理由见图1.学名叫做放射变换矩阵。看不懂啊~直接用就行。
pic=imwarp(pic,T); %imwarp把图像以上述放射变换矩阵进行角度变换。
figure(8),subplot(3,1,3), imshow(pic);title('3.利用radon算子做垂直方向矫正');
end

图1
(源代码中是用edge的canny算法,不过是在是看不懂编的程序+效果也不好,就自己模仿水平校正写的一个。)
重点:如果运行到切割部分,出错说数组超界了,其实是水平校正的问题。没有校正为垂直向,反而校正的更歪了,这样它切割第一个字符的时候就切到最后,后面切割自然就出界了。
12.再次平滑+移除小对象(自增)
se=strel('rectangle',[4,4]);
d=imclose(d,se);
figure(9),subplot(2,3,4),imshow(d);title('4.平滑图像的轮廓');
d=bwareaopen(d,50);
figure(9),subplot(2,3,5),imshow(d);title('5.再次从对象中移除小对象');
这里平滑用的矩阵变小了,平滑的目的:汉字的偏旁部首连到一起去。要不然比如说“沪”,移除小对象后就变成“户”了。
移除小对象目的:车牌中间有个“·”,不移除在后面切割就会被看成字符而出错。
13.灰度值累加(自增判断函数)
%% 在列方向上进行灰度值累加
X1=zeros(1,x1);
for j=1:x1
for i=1:y1
if(I3(i,j,1)==1)
X1(1,j)= X1(1,j)+1;
end
end
end
flag1=0;
flag2=0;
kkk=0;
kkkk=0;
for k=2:x1
if (X1(1,k-1)~=0)&&(X1(1,k)==0)&&(flag1 ==0) %前一个不为0,这一个为0
kkk=k;
flag1=1; %flag1=1表示找到反光部分开始处
end
if (X1(1,k)~=0)&&(X1(1,k-1)==0)&&(flag2 ==0) %后一个不为0,这一个为0
kkkk=k;
flag2=1; %flag2=1表示找到反光部分结束处
end
if kkkk-kkk>50
break;
end
end
figure(10);
plot(0:x1-1,X1),title('列方向像素点灰度值累计和'),xlabel('列值'),ylabel('累计像素量');
% close all;
%% 确定车牌起始位置
Px0=1;
Px1=1;
if flag1==1 && flag2==1
Px0=kkkk;
Px1=kkkk;
end
最后plot的图像就是各个字符的特征波形。非常重要。因为可以根据这个加一些判断,避免出现切割定位的错误。比如说上面的循环kkk和kkkk就是说,如果车牌两边有反光部分,前面并没有裁掉。根据plot的波形可以看出,车牌字符之间的距离肯定很小,车的反光部分和车牌之间距离肯定比较大。那么,就可以不从原点开始,而从反光部分结束处开始。
(可以看到,左右两边的尖峰就是车的反光部分)
14.分割字符
%% 分割字符
for i=1:7
while ((X1(1,Px0)<8)&&(Px0<x1)) %这里的X1(1,Px0)<8)的8,是为了防止有车牌的小点“·”没去干净影响切割
Px0=Px0+1; %条件是不超过列x1,下面第二个while<15是字符至少15长度。
end
Px1=Px0; %Px0和Px1分别是一个字符开始和结束的位置
while (((X1(1,Px1)>=7)&&(Px1<x1))||((Px1-Px0)<15)) %这里的X1(1,Px1)>=7的7,是字符有个波形最低为7.
Px1=Px1+1;
end
Z=d(:,Px0:Px1,:); %裁图片,并且给图片依次命名
switch strcat('Z',num2str(i)) %拼接Z1~Z7
case 'Z1'
PIN0=Z;
case 'Z2'
PIN1=Z;
case 'Z3'
PIN2=Z;
case 'Z4'
PIN3=Z;
case 'Z5'
PIN4=Z;
case 'Z6'
PIN5=Z;
otherwise
PIN6=Z;
end
figure(11);
subplot(1,7,i); %依次展示裁剪出来的图片
imshow(Z);
Px0=Px1;
end
15.将分割后的车牌字符归一化处理
pretreatment()就是把切割的字符固定到相同大小。
然后主函数显示归一后的字符图片。
目前还没有做识别部分。



