%% 清空环境变量和命令窗口
clc;
clear;
close all;
%% 定义全局变量A
global A
%% 读入车牌图片并显示
I=imread('9.jpeg');
figure(1);
imshow(I);
title('原图');
%%
if length(size(I))==3
I1 = rgb2gray(I); %转换为灰度图
else
I1 =I;
end
figure(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: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);
%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,:,:);
%IY为原始图像I中截取的纵坐标在PY1:PY2之间的部分
%end横向扫描
%% 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;
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: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');
figure(8),subplot(3,1,1),imshow(bw),title('1.定位后的这牌灰度图像');
figure(8),subplot(3,1,2),imshow(pic),title('2.利用radon算子做水平方向矫正');
%% 垂直方向校正
if y>m
bw2=edge(pic,'sobel','vertical');
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]);
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.365
d=imerode(d,se);
elseif bwarea(d)/m/n<=0.235
d=imdilate(d,se);
end
figure(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:y1
for j=1:x1
if(I3(i,j,1)==1)
Y1(i,1)= Y1(i,1)+1 ;
end
end
end
Py1=1;
Py0=1;
while ((Y1(Py0,1)<30)&&(Py0<y1))
Py0=Py0+1;
end
Py1=Py0;
while((Y1(Py1,1)>=30)&&(Py1<y1))
Py1=Py1+1;
end
d=d(Py0:Py1,:,:);
figure(9),subplot(2,3,6);imshow(d),title('6.目标车牌区域');
%% 在列方向上进行灰度值累加
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)
kkk=k;
flag1=1;
end
if (X1(1,k)~=0)&&(X1(1,k-1)==0)&&(flag2 ==0)
kkkk=k;
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
%% 分割字符
for i=1:7
while ((X1(1,Px0)<8)&&(Px0<x1))
Px0=Px0+1;
end
Px1=Px0;
while (((X1(1,Px1)>=7)&&(Px1<x1))||((Px1-Px0)<15))
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
%% 载入训练好的神经网络
load('-mat','ym');
%% 将分割后的车牌字符归一化处理
for ii=1:7
switch ii
case 1
PIN0=pretreatment(PIN0);
Z2=A;
imwrite(Z2,'A.jpg');
case 2
PIN1=pretreatment(PIN1);
Z2=A;
case 3
PIN2=pretreatment(PIN2);
Z2=A;
case 4
PIN3=pretreatment(PIN3);
Z2=A;
case 5
PIN4=pretreatment(PIN4);
Z2=A;
case 6
PIN5=pretreatment(PIN5);
Z2=A;
otherwise
PIN6=pretreatment(PIN6);
Z2=A;
end
figure(12);
subplot(1,7,ii);
imshow(Z2);
end
P0=[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()就是把切割的字符固定到相同大小。
然后主函数显示归一后的字符图片。
目前还没有做识别部分。