1. %% 清空环境变量和命令窗口
  2. clc;
  3. clear;
  4. close all;
  5. %% 定义全局变量A
  6. global A
  7. %% 读入车牌图片并显示
  8. I=imread('9.jpeg');
  9. figure(1);
  10. imshow(I);
  11. title('原图');
  12. %%
  13. if length(size(I))==3
  14. I1 = rgb2gray(I); %转换为灰度图
  15. else
  16. I1 =I;
  17. end
  18. figure(2),subplot(1,2,1),imshow(I1);title('灰度图');
  19. figure(2),subplot(1,2,2),imhist(I1);title('灰度图直方图');
  20. %% canny算子边缘检测
  21. I2=edge(I1,'canny',[0.2,0.55]);
  22. figure(3),imshow(I2);title('canny算子边缘检测')
  23. %% 腐蚀
  24. se=[1;1;1];
  25. I3=imerode(I2,se);
  26. figure(4),imshow(I3);title('腐蚀后图像');
  27. %% 平滑
  28. se=strel('rectangle',[40,40]);
  29. I4=imclose(I3,se);
  30. figure(5),imshow(I4);title('平滑图像的轮廓');
  31. %% 从对象中移除小对象
  32. I5=bwareaopen(I4,1000);
  33. figure(6),imshow(I5);
  34. title('从对象中移除小对象');
  35. [y,x,z]=size(I5);
  36. myI=double(I5);
  37. %% begin横向扫描
  38. Blue_y=zeros(y,1);
  39. for i=1:y
  40. for j=1:x
  41. if(myI(i,j,1)==1)
  42. %如果myI(i,j,1)即myI图像中坐标为(i,j)的点为蓝色
  43. %则Blue_y的相应行的元素white_y(i,1)值加1
  44. Blue_y(i,1)= Blue_y(i,1)+1; %蓝色像素点统计
  45. end
  46. end
  47. end
  48. [temp ,MaxY]=max(Blue_y);
  49. %temp为向量white_y的元素中的最大值,MaxY为该值的索引( 在向量中的位置)
  50. PY1=MaxY;
  51. while ((Blue_y(PY1,1)>=50)&&(PY1>1))
  52. PY1=PY1-1;
  53. end
  54. PY2=MaxY;
  55. while ((Blue_y(PY2,1)>=10)&&(PY2<y))
  56. PY2=PY2+1;
  57. end
  58. IY=I(PY1:PY2,:,:);
  59. %IY为原始图像I中截取的纵坐标在PY1PY2之间的部分
  60. %end横向扫描
  61. %% begin纵向扫描
  62. Blue_x=zeros(1,x); %进一步确定x方向的车牌区域
  63. for j=1:x
  64. for i=PY1:PY2
  65. if(myI(i,j,1)==1)
  66. Blue_x(1,j)= Blue_x(1,j)+1;
  67. end
  68. end
  69. end
  70. PX1=1;
  71. while ((Blue_x(1,PX1)<3)&&(PX1<x))
  72. PX1=PX1+1;
  73. end
  74. PX2=x;
  75. while ((Blue_x(1,PX2)<3)&&(PX2>PX1))
  76. PX2=PX2-1;
  77. end
  78. %这里的参数需要调整,裁掉车牌左边一条竖线。
  79. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  80. PX1=PX1+10;
  81. PX2=PX2;
  82. PY1=PY1-3;
  83. PY2=PY2-8;
  84. %end纵向扫描
  85. dw=I(PY1:PY2,PX1:PX2,:);
  86. imwrite(dw,'dw.jpg'); %将图像数据写入图像中
  87. figure(7),subplot(1,2,1),imshow(IY),title('行方向合理区域');
  88. figure(7),subplot(1,2,2),imshow(dw),title('定位剪切后的彩色车牌图像')
  89. %% 利用radon变换做水平矫正
  90. bw=rgb2gray(dw);
  91. bw1=edge(bw,'sobel','horizontal');
  92. theta=0:179;
  93. r=radon(bw1,theta);
  94. [m,n]=size(r);
  95. c=1;
  96. for i=1:m
  97. for j=1:n
  98. if r(1,1)<r(i,j)
  99. r(1,1)=r(i,j);
  100. c=j;
  101. end
  102. end
  103. end
  104. rot=90-c+2;
  105. pic=imrotate(bw,rot,'crop');
  106. figure(8),subplot(3,1,1),imshow(bw),title('1.定位后的这牌灰度图像');
  107. figure(8),subplot(3,1,2),imshow(pic),title('2.利用radon算子做水平方向矫正');
  108. %% 垂直方向校正
  109. if y>m
  110. bw2=edge(pic,'sobel','vertical');
  111. theta2=-45:45;
  112. r2=radon(bw2,theta2);
  113. [m2,n2]=size(r2);
  114. c2=1;
  115. for j2=1:n2
  116. for i2=1:m2
  117. if r2(1,1)<r2(i2,j2)
  118. r2(1,1)=r2(i2,j2);
  119. c2=i2
  120. end
  121. end
  122. end
  123. rot2=c2-45
  124. T=affine2d([1,0,0; tan(-rot2),1,0;0,0,1]);
  125. pic=imwarp(pic,T);
  126. figure(8),subplot(3,1,3), imshow(pic);title('3.利用radon算子做垂直方向矫正');
  127. end
  128. % pic2=imrotate(bw2,-rot2,'bilinear','crop');
  129. %
  130. % figure(8),subplot(3,1,3),imshow(pic2),title('2.利用radon算子做垂直方向矫正');
  131. %% 利用radon变换做垂直方向的矫正
  132. % binaryImage = edge(pic,'canny');
  133. % binaryImage = bwmorph(binaryImage,'thicken');
  134. % theta = -45:44;
  135. % [R,xp] = radon(binaryImage,theta);
  136. %
  137. % [R1,r_max] = max(R);
  138. % theta_max = 60;
  139. % while (theta_max > 50 || theta_max<-50)
  140. % [R2,theta_max] = max(R1);
  141. % R1(theta_max) = 0;
  142. % theta_max = theta_max -45;
  143. % end
  144. %角度计算完毕
  145. % H=[1,0,0; tan(-theta_max),1,0;0,0,1];
  146. % T=affine2d([1,0,0; tan(-theta_max),1,0;0,0,1]);
  147. % pic=imwarp(pic,T);
  148. % figure(8),subplot(3,1,3), imshow(pic);title('3.利用radon算子做垂直方向矫正');
  149. %% 字符分割前的预处理
  150. g_max=double(max(max(pic)));
  151. g_min=double(min(min(pic)));
  152. T=round(g_max-(g_max-g_min)/3); % T 为二值化的阈值
  153. [m,n]=size(pic);
  154. d=im2bw(pic,T/256); % d:二值图像
  155. figure(9);subplot(2,3,1),imshow(d),title('1.车牌二值图像')
  156. %% 滤波
  157. h=fspecial('average',3);
  158. d=im2bw(round(filter2(h,d)));
  159. figure(9),subplot(2,3,2),imshow(d),title('2.均值滤波后')
  160. %% 某些图像进行操作
  161. % 膨胀或腐蚀
  162. se=strel('square',3); % 使用一个3X3的正方形结果元素对象对创建的图像膨胀
  163. se=eye(2); % eye(n) returns the n-by-n identity matrix 单位矩阵
  164. [m,n]=size(d);
  165. if bwarea(d)/m/n>=0.365
  166. d=imerode(d,se);
  167. elseif bwarea(d)/m/n<=0.235
  168. d=imdilate(d,se);
  169. end
  170. figure(9),subplot(2,3,3),imshow(d),title('3.膨胀或腐蚀处理后')
  171. %% 再次平滑+移除小对象
  172. se=strel('rectangle',[4,4]);
  173. d=imclose(d,se);
  174. figure(9),subplot(2,3,4),imshow(d);title('4.平滑图像的轮廓');
  175. d=bwareaopen(d,50);
  176. figure(9),subplot(2,3,5),imshow(d);title('5.再次从对象中移除小对象');
  177. %% 对图像的边框进行裁剪,只留下有效字符部分
  178. [y1,x1,z1]=size(d);
  179. I3=double(d);
  180. TT=1;
  181. Y1=zeros(y1,1);
  182. for i=1:y1
  183. for j=1:x1
  184. if(I3(i,j,1)==1)
  185. Y1(i,1)= Y1(i,1)+1 ;
  186. end
  187. end
  188. end
  189. Py1=1;
  190. Py0=1;
  191. while ((Y1(Py0,1)<30)&&(Py0<y1))
  192. Py0=Py0+1;
  193. end
  194. Py1=Py0;
  195. while((Y1(Py1,1)>=30)&&(Py1<y1))
  196. Py1=Py1+1;
  197. end
  198. d=d(Py0:Py1,:,:);
  199. figure(9),subplot(2,3,6);imshow(d),title('6.目标车牌区域');
  200. %% 在列方向上进行灰度值累加
  201. X1=zeros(1,x1);
  202. for j=1:x1
  203. for i=1:y1
  204. if(I3(i,j,1)==1)
  205. X1(1,j)= X1(1,j)+1;
  206. end
  207. end
  208. end
  209. flag1=0;
  210. flag2=0;
  211. kkk=0;
  212. kkkk=0;
  213. for k=2:x1
  214. if (X1(1,k-1)~=0)&&(X1(1,k)==0)&&(flag1 ==0)
  215. kkk=k;
  216. flag1=1;
  217. end
  218. if (X1(1,k)~=0)&&(X1(1,k-1)==0)&&(flag2 ==0)
  219. kkkk=k;
  220. flag2=1;
  221. end
  222. if kkkk-kkk>50
  223. break;
  224. end
  225. end
  226. figure(10);
  227. plot(0:x1-1,X1),title('列方向像素点灰度值累计和'),xlabel('列值'),ylabel('累计像素量');
  228. % close all;
  229. %% 确定车牌起始位置
  230. Px0=1;
  231. Px1=1;
  232. if flag1==1 && flag2==1
  233. Px0=kkkk;
  234. Px1=kkkk;
  235. end
  236. %% 分割字符
  237. for i=1:7
  238. while ((X1(1,Px0)<8)&&(Px0<x1))
  239. Px0=Px0+1;
  240. end
  241. Px1=Px0;
  242. while (((X1(1,Px1)>=7)&&(Px1<x1))||((Px1-Px0)<15))
  243. Px1=Px1+1;
  244. end
  245. Z=d(:,Px0:Px1,:);
  246. switch strcat('Z',num2str(i)) %拼接Z1~Z7
  247. case 'Z1'
  248. PIN0=Z;
  249. case 'Z2'
  250. PIN1=Z;
  251. case 'Z3'
  252. PIN2=Z;
  253. case 'Z4'
  254. PIN3=Z;
  255. case 'Z5'
  256. PIN4=Z;
  257. case 'Z6'
  258. PIN5=Z;
  259. otherwise
  260. PIN6=Z;
  261. end
  262. figure(11);
  263. subplot(1,7,i);
  264. imshow(Z);
  265. Px0=Px1;
  266. end
  267. %% 载入训练好的神经网络
  268. load('-mat','ym');
  269. %% 将分割后的车牌字符归一化处理
  270. for ii=1:7
  271. switch ii
  272. case 1
  273. PIN0=pretreatment(PIN0);
  274. Z2=A;
  275. imwrite(Z2,'A.jpg');
  276. case 2
  277. PIN1=pretreatment(PIN1);
  278. Z2=A;
  279. case 3
  280. PIN2=pretreatment(PIN2);
  281. Z2=A;
  282. case 4
  283. PIN3=pretreatment(PIN3);
  284. Z2=A;
  285. case 5
  286. PIN4=pretreatment(PIN4);
  287. Z2=A;
  288. case 6
  289. PIN5=pretreatment(PIN5);
  290. Z2=A;
  291. otherwise
  292. PIN6=pretreatment(PIN6);
  293. Z2=A;
  294. end
  295. figure(12);
  296. subplot(1,7,ii);
  297. imshow(Z2);
  298. end
  299. P0=[PIN0',PIN1',PIN2',PIN3',PIN4',PIN5',PIN6'];

//**全部代码**//
//*部分函数讲解**//

1.imread()

方案一、imread(文件名,文件格式)

x=imread(test,'bmp');

image.png
方案二、imread(文件名)

I=imread('9.jpeg');

image.png

2.figure(1)、imshow()

figure(1):创建一个窗口。
imshow():显示图片。
如果直接imshow(),这个图片和下个图片都不使用figure(),前一个图片被覆盖。
image.png
image.png

3.size()

彩色图片size()会返回三个数,长、宽和3(3表示彩色RGB)。
length的3是三个数的3,而不是3色RGB的3.
rgb2gray()函数,把彩色图片转换为灰度图。
image.png

4.subplot()

绘制子图。数字表示几行,几列,目前在绘制第几个图。

subplot(2,2,3)

image.png

5.edge()

边缘检测。edge(图片,‘canny算法’,灵敏度【灵敏度上下限】)
image.png
image.png

6.imerode()

se即卷积模板,腐蚀的目的是:
image.png
具体可见下面链接。

se=[1;1;1];
I3=imerode(I2,se);   %I2图像用[1,1,1]进行腐蚀。

image.pngimage.png
image.png

7.imclose()

获得图像轮廓。
效果见上图。有的小干扰亮点用下面函数消除。

8.bwareaopen()

作用:移除小对象(以后续的一个步骤为例,可以见到牌的‘·’和一些干扰亮点被移除了)

I5=bwareaopen(I4,1000);    %小于1000的亮点被视为干扰,被去掉

image.png
image.png

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算子做水平方向矫正');

image.png
图1
image.png
图2
(论文3里面说水平校正原理是这样图3,不过我没看懂代码和这个的关系)
image.png
图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

image.png
图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的波形可以看出,车牌字符之间的距离肯定很小,车的反光部分和车牌之间距离肯定比较大。那么,就可以不从原点开始,而从反光部分结束处开始。
image.png
(可以看到,左右两边的尖峰就是车的反光部分)

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()就是把切割的字符固定到相同大小。
然后主函数显示归一后的字符图片。
image.png
目前还没有做识别部分。