接着上面的工作,接下去就该是进行字符分割了。考虑到为了后面的字符识别,因此在这部分需要实现的目标是需要把车牌的边框全部切除,对重新定位的车牌进行垂直方向水平方向调整,保证字符是正的。最后才是字符的分割。
1.首先上下边框切割。对定位的车牌每行作一次的差分,计算每行的综合,小于某个阈值时候将其舍去。部分代码:
[length height]=size(p); % 水平方向定位 for i=1:length % 水平一阶差分 for j=1:height-1 revise_row(i,j)=abs(p(i,j)-p(i,j+1)); end revise_row(i,height)=revise_row(i,height-1); end for i=1:length T(i)=sum(revise_row(i,:)); end for i=2:length-1 T(i)=1/2*(T(i-1)+T(i)+T(i+1)); end
T(1)= T(2);
T(length)=T(length-1);
% 截取水平区间 left_row=0;right_row=0; threshold_row=max(T); for i=1:length if(T(i)>1/6*threshold_row) left_row=i; break; end end for i=length:-1:1 if(T(i)>1/6*threshold_row) right_row=i; break; end end temp=1; for i=left_row:right_row %截取区间 p_row(temp,:)=p1(i,:); temp=temp+1; end
左右切割结果图:
2 左右边框切割。对定位的车牌每列作一次的差分。考虑到左右边框的一阶总数是两边大,中间小,因此采用获取左右的峰值,当小于峰值的摸个阈值时进行截取。 部分代码:
% 竖直方向定位 for j=1:height % 一阶差分 for i=1:length-1 revise_column(i,j)=abs(p1(i,j)-p1(i+1,j)); end revise_column(length,j)=0; end for j=1:height T(j)=sum(revise_column(:,j)); end for j=2:height-1 T(j)=1/3*(T(j-1)+T(j)+T(j+1)); end T(1)= T(2); T(height)=T(height-1); [max_peak max_num]=max(T);%求出峰值 threshold=0.25*max_peak;%阈值 left_peak= T(1); right_peak= T(height); %找到左峰值 for j=2:height if(T(j)>=threshold&(T(j)>=T(j-1))&(T(j)>=T(j+1))&(j<=max_num)) left_peak=j; break; end end %找到右峰值 if(max_num<1/2*height) threshold=1/2*threshold; end for j=height-1:-1:2 if(T(j)>=threshold&(T(j)>=T(j-1))&(T(j)>=T(j+1))&(j>=max_num)) right_peak=j; break; end end left_valley=1;%谷底值 right_valley=right_peak; %找出左边最小值 for j=left_peak:-1:2 if((T(j)<=T(j-1))&(T(j)<=T(j+1))) left_valley=j; break; end end for j=right_peak:height-1 if((T(j)<=T(j-1))&(T(j)<=T(j+1))) right_valley=j; break; end end temp=1; for j=left_valley:right_valley %截取部分参数分开,否则仍然按照原图大小 p_array(:,temp)=p_row(:,j); temp=temp+1; end
截取图:
3 接下来进行车牌的水平校正。matlab里面有现成的函数imrotate,在-20~20角度范围对车牌进行操作,完成水平校正。代码
%水平校正 max_total=0; sum_abs_angle=zeros(1,41); count=1; for angle=-20:0.1:20%旋转角度选择 p_array_temp=edge(p_array,'sobel','horizontal');%水平边缘检测 picbw_temp = imrotate(p_array,angle, 'bicubic','loose');%p_array对灰度图像进行判断操作 sum_xz=sum(picbw_temp'); %计算垂直投影 [sum_hight sum_width]=size(sum_xz); % figure,bar(sum_xz);title('垂直投影(含边框)');%输出垂直投影 % title(['车牌旋转角: ',num2str(angle),'度后图像'] ,'Color','r');%显示车牌的旋转角度 total_xz=0; for i=1:sum_width-1 total_xz=total_xz+abs(sum_xz(i)-sum_xz(i+1)); end if total_xz>max_total %找出一阶导最大值 angle_xz=angle;%调整角度 picbw_xz=picbw_temp; max_total=total_xz; end sum_abs_angle(count)=total_xz; count=count+1; end % figure,bar(sum_abs_angle);title('每个角度绝对值总和');%输出垂直投影 image_correct=imrotate(p_array,angle_xz,'bicubic','loose');%车牌的水平旋转校正 figure,imshow(mat2gray(image_correct)); title(['车牌旋转角: ',num2str(angle_xz),'度后图像'] ,'Color','r');%显示车牌的旋转角度
水平矫正结果:
4 水平矫正后,我们还需要对车牌上下边框再次进行切割,去除车牌上下边框。与前面直接做一阶差分不同,这里考虑到上下边框水平成分较明显,所以我们在这里先对车牌进行竖直方向的边沿检测,然后计算水平方向的均值进行切割。 部分代码:
%去除上下边框 max_num=max(max(image_correct)); min_num=min(min(image_correct)); thresh=(max_num-min_num)/5;%15 image_correct_row=edge(image_correct,'sobel',thresh,'vertical');%根据所指定的敏感度阈值thresh,在所指定的方向direction上, % figure,imshow(image_correct_row); title('竖直边缘检测');%显示车牌的旋转角度 histrow=sum(image_correct_row'); %计算水平投影 histrow_mean=mean(histrow); histcol=sum(image_correct_row); %计算竖直投影 histcol_mean=mean(histcol); [width hight]=size(image_correct_row); rowtop=0; for i=1:width/2 if histrow(i)>=1/3*histrow_mean & histrow(i+1)>1/2*histrow_mean & histrow(i+2)>1/2*histrow_mean & histrow(i+3)>1/2*histrow_mean %连续有值判断为上边界 rowtop=i; %上边切割 break; end end rowbot=0; for i=width:-1:width/2 if histrow(i)>=1/4*histrow_mean & histrow(i-1)>histrow_mean & histrow(i-2)>histrow_mean & histrow(i-3)>histrow_mean & histrow(i-4)>histrow_mean rowbot=i; %下边切割 break; end end
结果图:
可以看到,上下边框基本清除,下面就需要对字符进行垂直校正。
5 关于字符垂直校正,这个部分自己也没太明白,只是参考别人的程序,可以交流。代码:
max_total=0; angle_yz=0; sum_abs_angle=zeros(1,41); count=1; for angle=-10:0.05:10%旋转角度选择 picbw_temp = imrotate(image_correct_x,angle, 'bicubic','crop');%p_array对灰度图像进行判断操作 sum_yz=sum(picbw_temp); %计算垂直投影 [sum_hight sum_width]=size(sum_yz); total_yz=0; for i=1:sum_width-1 total_yz=total_yz+abs(sum_yz(i)-sum_yz(i+1)); end if total_yz>max_total %找出一阶导最大值 angle_yz=angle; picbw_yz=picbw_temp; max_total=total_yz; end sum_abs_angle(count)=total_yz; count=count+1; end Td=maketform('affine',[1 tand(angle_yz) 0;0 1 0;0 0 1]');%切变的参数结构体 image_correct_x_y=imtransform(image_correct_x,Td,'FillValues',255);%实现图像切变
实验结果:
6 这样基本要做的都已经完成,剩下字符切割。在测试过程中,发现车牌的上下边框边角好去除,左右边框则无法达到理想的要求,经常是要么没有切割完全,要把把车牌字符也切割了。最后索性就不管左右边框了,直接进行字符分割。自己所用的方法是:设置字符宽为width=1/2*车牌的高度;考虑到车牌的第二位和第三位之间有小点(也可能没有),中间距离更大,所以认为车牌宽度的一半既是第四个字符所在位置或者是第三和第四字符之间,这里最主要是要确定某个参数,来标定车牌位置,便于分割。(实验证明还是有效的)当遇到字符宽度大于默认width时,取大的值。车牌左右第一个和第七个字符则是截取默认宽度,去除边框影响。部分代码:
获取第四个字符:
%切割字符 [image_heigth image_length]=size(image_correct_x_y); character_left=0;%切割字符左右位置 character_right=0; middle_histcol=uint8(image_length/2); char_width=image_heigth/2;%默认字符宽度 if binary_histcol(middle_histcol)>0 %垂直投影不为零 %找出车牌的第四个字符 for i=middle_histcol:-1:1 %字符左侧 if binary_histcol(i)==0 character_left=i+1; break; end end for i=middle_histcol:image_length%字符右侧 if binary_histcol(i)==0 character_right=i-1; break; end end result_char_forth(:,:)=binary_image(:,character_left:character_right); else %认为中间值在 第三、四个字符之间 for i=middle_histcol:image_length%获取第四个字符 if binary_histcol(i)>0 & binary_histcol(i-1)==0 %字符左起点 character_left=i; end if binary_histcol(i)>0 & binary_histcol(i+1)==0 %字符右边界 character_right=i; break; end end result_char_forth(:,:)=binary_image(:,character_left:character_right); %获取字符 if character_right-character_left+1>char_width %比默认字符大 char_width=character_right-character_left+1;%默认字符用于确认车牌左右两侧的宽度 end end
判断中间有无小点:
%判断有无中间小点!!!!! width_dot=4 width_dot=4;%只能是字母,认为宽度小于4即为点,否则认为没有点 for i=current_left_position-1:-1:2 if binary_histcol(i)>0 & binary_histcol(i+1)==0 %字符右起点 character_right_temp=i; end if binary_histcol(i)>0 & binary_histcol(i-1)==0 %字符左边界 character_left_temp=i; break; end end if((character_right_temp-character_left_temp)>width_dot)%不是点 current_left_position=character_left;%记录当前位置放到下面读取 else %是点 current_left_position=character_left_temp;%记录当前位置 end
切割结果:
可以看到,基本上消除了左右边框的影响。七个工作基本完成!欢迎交流!