接着上面的工作,接下去就該是進行字符分割了。考慮到為了后面的字符識別,因此在這部分需要實現的目標是需要把車牌的邊框全部切除,對重新定位的車牌進行垂直方向水平方向調整,保證字符是正的。最后才是字符的分割。
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
切割結果:
可以看到,基本上消除了左右邊框的影響。七個工作基本完成!歡迎交流!