FPGA圖像處理——數字識別


一、基於FPGA的數字識別方法

  常見算法有:基於模版匹配的識別方法、基於BP 神經網絡的識別方法、基於數字特征的識別方法等。

    1.模版匹配法

  模版匹配法是一種被較早應用的數字識別算法,該算法的關鍵是對所要識別的所有數字進行模版構建,之后將圖像中的數字與所有的數字模版一一進行比較,計算出圖像中數字與每個模版的相似度,根據所計算出的相似度結果進行識別。其中相似度最高的模版即為我們所要識別的結果。模版匹配法的對數字的大小、結構形狀的規范化程度要求很高,數字的規范化程度對識別的准確率有着直接的影響。該算法原理較為簡單,但計算復雜度過大,同時不利於 FPGA 的實現。

    2.神經網絡識別法

  神經網絡識別的方法是模仿動物神經網絡的特征,對信息進行分布式並行處理的一種算法。神經網絡識別算法具有一定的抗干擾能力,但為了保證識別的准確率,該算法需要負責並且大量的計算,來對神經網絡進行訓練,而過於復雜的計算不利於 FPGA 對該算法的實現。

    3.數字特征識別法

       基於數字特征的識別算法其核心是通過對數字的形狀以及結構等幾何特征進行分析與統計,通過對數字特征的識別從而達到對圖像中數字的識別。

二、基於數字特征的數字識別

  對於數字0~9,可通過兩條水平特征線一條垂直特征線來區分,如下圖所示:

  上圖中紅框是數字的上下左右邊界。X1在豎直方向的 2/5 處的水平線,x2在豎直方向的 2/3 處的水平線,y在水平方的 1/2 處的水直線。我們以此特征來統計x1,x2,y與數字的交叉點。

以交叉統計法來區分0~9數字的特征如下表1:

 

 

  由於2,3,5的數字特征統計表一樣,無法區分所以我們繼續增加數字特征以區分2,3,5。如表2:

【注】:不同字體的數字特征線位置x1和x2根據具體情況而定,而車牌中的數字是由黑體改變而來,這點需要測試找到合適畫線位置。

三、Matlab實現數字識別

clear all;close all;clc; I = imread('D:\FPGA\Test photo\num\5.jpg'); Ib =im2bw(I); % Ib =~Ib;  %黑字白底和黑底白字模板相反 %---------------步驟1:找到字符上下左右邊界--------------------- [ROW,COL] =size(Ib); min_x=ROW; max_x=0; min_y=COL; max_y=0; for i=1:ROW for j=1:COL if Ib(i,j)==0 
            if(min_x>i) min_x = i; end if(max_x<i) max_x = i; end if(min_y>j) min_y = j; end if(max_y<j) max_y = j; end end end end %--------------- 步驟2:畫特征線--------------- height = max_x - min_x; x1 = round(min_x+height*2/6); x2 = round(min_x+height*4/5); width = max_y - min_y; y = round(min_y + width*1/2); for i=1:ROW for j=1:COL if(i >= min_x && i <= max_x)&&(j==min_y ||j==max_y || j==y) R(i,j,1)= 255; R(i,j,2)= 0; R(i,j,3)= 0; elseif(i == min_x || i == max_x || i ==x1 || i ==x2)&&(j>=min_y && j<=max_y) R(i,j,1)= 255; R(i,j,2)= 0; R(i,j,3)= 0; else R(i,j,1)= I(i,j,1);       %I(i,j,1) R(i,j,2)= I(i,j,2); R(i,j,3)= I(i,j,3); end end end figure; imshow(R); %---------------步驟3:統計特征信息--------------------- cross_y=0; cross_x1_L=0; cross_x1_R=0; cross_x2_L=0; cross_x2_R=0; for i=1:ROW-1
    for j=1:COL-1
      if j == y && Ib(i,j)==1&&Ib(i+1,j)==0 cross_y =cross_y+1; elseif i == x1 && Ib(i,j)==1&&Ib(i,j+1)==0
          if(j<y) cross_x1_L =cross_x1_L+1; elseif(j>y) cross_x1_R =cross_x1_R+1; end elseif i == x2 && Ib(i,j)==1&&Ib(i,j+1)==0
          if(j<y) cross_x2_L =cross_x2_L+1; elseif(j>y) cross_x2_R =cross_x2_R+1; end end end end %---------------步驟4:根據交點數匹配數字--------------- cross_x1=cross_x1_L+cross_x1_R; cross_x2=cross_x2_L+cross_x2_R; result=NaN; if(cross_y==1) if(cross_x1==1 && cross_x2==1) result=1; end elseif(cross_y==2) if(cross_x1==1 && cross_x2==1) result=7; elseif(cross_x1==2 && cross_x2==1) result=4; elseif(cross_x1==2 && cross_x2==2) result=0; end elseif(cross_y==3) if(cross_x1==1 && cross_x2==2) result=6; elseif(cross_x1==2 && cross_x2==1) result=9; elseif(cross_x1==2 && cross_x2==2) result=8; elseif(cross_x1==1 && cross_x2==1) if(cross_x1_R==1 && cross_x2_L==1) result=2; elseif(cross_x1_R==1 && cross_x2_R==1) result=3; elseif(cross_x1_L==1 && cross_x2_R==1) result=5; end end end display(result);

 Matlab結果:

 

 四、FPGA實現數字識別

 數字識別分四步:

  1.字符邊界選框。通過檢測字符黑色像素來膨脹式包絡出邊界。注:此處需消耗一幀時間來檢測,相鄰兩幀圖像差別很小。

  2.畫特征線。根據前一幀的邊界值得到兩條水平特征線x1、x2和一條垂直特征線y。注:進行判斷會消耗1clk,顯示的行場同步信號需打一拍。

  3.統計特征信息。對於交點數進行統計,由於背景(白)到字符(黑)存在01跳變,利用狀態轉移的方法來檢測交點數。注:圖像輸入是經過處理后的黑白二值圖,不存在噪聲干擾。

  4.根據特征信息匹配數字。對於數字2、3、5的檢測進行了兩層匹配。注:在一幀特征信息統計完后再利用寄存器寄存,對於想輸出特征信息也方便顯示。

完整代碼如下:(代碼中有詳細注釋)

 1 //////////////////////////////////////////////////////////////////////////////////
 2 // Create Date: 16:49:38 06/20/2021  3 // Author Name: yiquwange  4 // Module Name: Digital_Rec  5 // Project Name: Image processing  6 // Target Devices: ALINX AX309  7 // Tool versions: ISE14.7  8 // Description: 0~9 numeral recognition based on digital features  9 // Revision: 1.0
 10 //////////////////////////////////////////////////////////////////////////////////
 11 
 12 module Digital_Rec(  13             input clk,  14             input rst_n,  15             input Y_de,  16             input Y_hs,  17             input Y_vs,  18             input [10:0] hcount,    //行計數坐標
 19             input [10:0] vcount,    //場計數坐標
 20             input [7:0] Y_data,    //經二值化后的數據,數字黑色(8'd0),背景白色(8'd255),也可用1bit:0、1表示
 21             output DR_de,        //輸出使能信號
 22             output DR_hs,        //行同步信號
 23             output DR_vs,        //場同步信號
 24             output reg [3:0] result,    //識別結果,用於數碼管顯示
 25             /*
 26  output reg [1:0] cross_y_r, //中間測試用於數碼管顯示  27  output reg [1:0] cross_x1_L_r,  28  output reg [1:0] cross_x1_R_r,  29  output reg [1:0] cross_x2_L_r,  30  output reg [1:0] cross_x2_R_r,*/
 31             output reg [23:0] DR_data  32  );  33         
 34 parameter ROW = 10'd272; //圖片高
 35 parameter COL = 10'd480; //圖片寬
 36 
 37 
 38 reg [3:0] result;  39 //特征線交點計數
 40 reg [1:0] cross_y;  41 reg [1:0] cross_x1;  42 reg [1:0] cross_x2;  43 reg [1:0] cross_x1_L;  44 reg [1:0] cross_x1_R;  45 reg [1:0] cross_x2_L;  46 reg [1:0] cross_x2_R;  47 
 48 wire pos_vs;  49 wire neg_vs;  50 reg [10:0] min_x;  51 reg [10:0] max_x;  52 reg [10:0] min_y;  53 reg [10:0] max_y;  54 wire dis_en;  55 reg dis_en_r;  56 wire dis_en_pos;  57 wire dis_en_neg;  58 wire     [10:0]    cnt_col;    //圖片顯示區域的行計數
 59 wire     [10:0]    cnt_row;    //圖片顯示區域的場計數
 60 reg     [10:0] cnt_col_r;  61 reg     [10:0] cnt_row_r;  62 assign cnt_col = hcount;    //這里如果圖片尺寸小於顯示屏尺寸可進行坐標轉換
 63 assign cnt_row = vcount;  64 assign dis_en = Y_de;  65 
 66 always @(posedge clk or negedge rst_n)  67     if(!rst_n)  68         dis_en_r <= 0;  69     else 
 70         dis_en_r <= dis_en;  71         
 72 assign    dis_en_pos = dis_en && !dis_en_r;  73 assign    dis_en_neg = !dis_en && dis_en_r;  74 
 75 always @(posedge clk or negedge rst_n)  76     if(!rst_n)  77         Y_vs_r <= 0;  78     else 
 79         Y_vs_r <= Y_vs;  80         
 81 assign    pos_vs = Y_vs && !Y_vs_r;  82 assign  neg_vs = !Y_vs && Y_vs_r;  83 
 84 
 85 //第1幀,找到字符上下左右邊界
 86 always @(posedge clk or negedge rst_n) begin
 87     if(!rst_n) begin
 88         min_x <= ROW-1;        //初始時最小值賦值最大
 89         min_y <= COL-1;  90         max_x <= 0;            //初始時最大值賦值最小
 91         max_y <= 0;  92     end
 93     else if(neg_vs) begin        
 94         min_x <= ROW-1;  95         min_y <= COL-1;  96         max_x <= 0;  97         max_y <= 0;  98     end
 99     else if(dis_en && Y_data==0) begin    //檢測到黑色字符開始更新邊界
100         if(min_x>cnt_row)                //檢測原理類似單形體膨脹算法
101             min_x <= cnt_row; 102         else
103             min_x <= min_x; 104         if(max_x<cnt_row) 105             max_x <= cnt_row; 106         else
107             max_x <= max_x; 108         if(min_y>cnt_col) 109             min_y <= cnt_col; 110         else
111             min_y <= min_y; 112         if(max_y<cnt_col) 113             max_y <= cnt_col; 114         else
115             max_y <= max_y; 116     end
117 end
118 
119 
120 reg [10:0] min_x_r; 121 reg [10:0] max_x_r; 122 reg [10:0] min_y_r; 123 reg [10:0] max_y_r; 124 
125 //幀同步鎖存邊框角點檢測結果
126 always @(posedge clk or negedge rst_n) 127     if(!rst_n)begin
128         min_x_r <= 0; 129         max_x_r <= 0; 130         min_y_r <= 0; 131         max_y_r <= 0; 132     end
133     else if(neg_vs)begin
134         min_x_r <= min_x; 135         max_x_r <= max_x; 136         min_y_r <= min_y; 137         max_y_r <= max_y; 138     end
139 
140 
141 //得到特征線坐標 142 //畫兩橫一豎三條線,第一條橫線位於高度的2/5,第二條橫線位於高度2/3處, 143 //豎線位於寬度的1/2,對這三條線與數字的交點個數及交點位置進行統計和分析 144 //reg [10:0] height; 145 //reg [10:0] width;
146 reg [10:0] x1; 147 reg [10:0] x2; 148 reg [10:0] y; 149 
150 always@(posedge clk or negedge rst_n)begin
151     if(!rst_n) begin
152         x1 <= 0; 153         x2 <= 0; 154         y <= 0; 155     end
156     else if(pos_vs) begin
157         x1 <= min_x_r+(max_x_r - min_x_r)*1/3;    //不建議這樣寫,多步操作應分時鍾進行
158         x2 <= min_x_r+(max_x_r - min_x_r)*4/5;    //復雜的乘除操作應借助IP核
159         y <= min_y_r +(max_y_r - min_y_r)/2; 160     end
161 end
162 
163 
164 //第2幀,畫特征線定位 【1clk】
165 always @(posedge clk or negedge rst_n) begin
166     if(!rst_n) begin
167         DR_data <= {Y_data,Y_data,Y_data}; 168     end
169     else if(dis_en) begin    //有效顯示區域
170         if((cnt_row >= min_x_r && cnt_row <= max_x_r)&&(cnt_col == min_y_r || cnt_col == max_y_r || cnt_col == y)) 171             DR_data <= {8'hff,8'h0,8'h0}; //豎向紅線
172         else if((cnt_col >= min_y_r && cnt_col <= max_y_r)&&(cnt_row == min_x_r || cnt_row == max_x_r || cnt_row == x1 || cnt_row == x2)) 173             DR_data <= {8'hff,8'h0,8'h0}; //橫向紅線
174         else
175             DR_data <= {Y_data,Y_data,Y_data};    //其它區域不變
176     end
177     else 
178         DR_data <= {Y_data,Y_data,Y_data}; 179 end
180 
181 
182 //---------------------------------------------------------------------------- 183 //---------------------- 特征線交點檢測 ---------------------------------- 184 //設置一段式狀態機來檢測像素灰度值變化: 185 //背景(1)到背景(1)、背景(1)到數字(0)、數字(0)到數字(0)、數字(0)到背景(1)
186 parameter IDLE=4'd0,CHECK_LEFT=4'd1,LEFT=4'd2,CHECK_RIGHT=4'd3,RIGHT=4'd4;
187 parameter CHECK_UP=4'd5,UP=4'd6,CHECK_DOWN=4'd7,DOWN=4'd8; 188 reg [3:0] state_x1;        //x1特征線左右邊沿狀態
189 reg [3:0] state_x2; 190 reg [3:0] state_y;        //y特征線上下邊沿狀態
191 
192 always@(posedge clk or negedge rst_n)begin
193     if( !rst_n) 194         state_x1 <= IDLE; 195     else if(dis_en && cnt_row == x1)begin    //x1特征線檢測
196         case(state_x1) 197  IDLE: 198                 state_x1 <= Y_data ? CHECK_LEFT : CHECK_RIGHT;    //真(白色背景)假(黑色數字)
199  CHECK_LEFT: 200                 state_x1 <= Y_data ? CHECK_LEFT : LEFT; 201  LEFT: 202                 state_x1 <= CHECK_RIGHT; 203  CHECK_RIGHT: 204                 state_x1 <= Y_data ? RIGHT : CHECK_RIGHT; 205  RIGHT: 206                 state_x1 <= CHECK_LEFT; 207             default: 208                 state_x1 <= IDLE; 209         endcase
210     end
211 end
212 
213 always@(posedge clk or negedge rst_n)begin
214     if( !rst_n) 215         state_x2 <= IDLE; 216     else if(dis_en && cnt_row == x2)begin
217         case(state_x2) 218  IDLE: 219                 state_x2 <= Y_data ? CHECK_LEFT : CHECK_RIGHT; 220  CHECK_LEFT: 221                 state_x2 <= Y_data ? CHECK_LEFT : LEFT; 222  LEFT: 223                 state_x2 <= CHECK_RIGHT; 224  CHECK_RIGHT: 225                 state_x2 <= Y_data ? RIGHT : CHECK_RIGHT; 226  RIGHT: 227                 state_x2 <= CHECK_LEFT; 228             default: 229                 state_x2 <= IDLE; 230         endcase
231     end
232 end
233 
234 always@(posedge clk or negedge rst_n)begin
235     if( !rst_n) 236         state_y <= IDLE; 237     else if(dis_en && cnt_col == y)begin
238         case(state_y) 239  IDLE: 240                 state_y <= Y_data ? CHECK_UP : CHECK_DOWN; 241  CHECK_UP: 242                 state_y <= Y_data ? CHECK_UP : UP; 243  UP: 244                 state_y <= CHECK_DOWN; 245  CHECK_DOWN: 246                 state_y <= Y_data ? DOWN : CHECK_DOWN; 247  DOWN: 248                 state_y <= CHECK_UP; 249             default: 250                 state_y <= IDLE; 251         endcase
252     end
253 end
254 
255 
256 always @(posedge clk or negedge rst_n) begin
257     if(!rst_n) begin
258         cross_y <= 0; 259         cross_x1 <= 0; 260         cross_x2 <= 0; 261     end
262     else if(pos_vs) begin     //場同步上升沿特征交點計數清零
263         cross_y <= 0; 264         cross_x1 <= 0; 265         cross_x2 <= 0; 266     end
267     else if(state_y==UP)    //也可以檢測DOWN
268         cross_y <= cross_y+1; 269     else if(state_x1==LEFT) 270         cross_x1 <=cross_x1+1; 271     else if(state_x2==LEFT) 272         cross_x2 <=cross_x2+1; 273     else begin
274         cross_y <= cross_y; 275         cross_x1 <= cross_x1; 276         cross_x2 <= cross_x2; 277     end
278 end
279 
280 
281 //因為狀態轉移會消耗1clk,行場計數打拍
282 always @(posedge clk or negedge rst_n) 283     if(!rst_n)begin
284         cnt_col_r <= 0; 285         cnt_row_r <= 0; 286         end
287     else begin
288         cnt_col_r <= cnt_col; 289         cnt_row_r <= cnt_row; 290         end
291 
292 
293 //水平特征線的左右區域檢測,只用於檢測數字2、3、5
294 always @(posedge clk or negedge rst_n) begin
295     if(!rst_n) begin
296         cross_x1_L <= 0; 297         cross_x1_R <= 0; 298         cross_x2_L <= 0; 299         cross_x2_R <= 0; 300     end
301     else if(pos_vs) begin    //場同步上升沿特征交點計數清零
302         cross_x1_L <= 0; 303         cross_x1_R <= 0; 304         cross_x2_L <= 0; 305         cross_x2_R <= 0; 306     end
307     else if(cnt_row_r == x1) begin            //當掃描到水平特征線x1
308         if(cnt_col_r<=y && state_x1==RIGHT)    //進行左右區域判斷
309             cross_x1_L <=cross_x1_L+1; 310         else if(cnt_col_r>=y && state_x1==LEFT) 311             cross_x1_R <=cross_x1_R+1; 312     end
313     else if(cnt_row_r == x2) begin    
314         if(cnt_col_r<=y && state_x2==RIGHT) 315             cross_x2_L <=cross_x2_L+1; 316         else if(cnt_col_r>=y && state_x2==LEFT) 317             cross_x2_R <=cross_x2_R+1; 318     end    
319     else begin
320         cross_x1_L <= cross_x1_L; 321         cross_x1_R <= cross_x1_R; 322         cross_x2_L <= cross_x2_L; 323         cross_x2_R <= cross_x2_R; 324     end
325 end
326 
327 
328 //設置幀同步鎖存器,當一幀結束后再更新特征交點數,便於數碼管顯示
329 always @(posedge clk or negedge rst_n) begin
330     if(!rst_n) begin
331         cross_y_r <= 0; 332         cross_x1_L_r <= 0; 333         cross_x1_R_r <= 0; 334         cross_x2_L_r <= 0; 335         cross_x2_R_r <= 0; 336     end
337     else if(pos_vs) begin
338         cross_y_r <= cross_y; 339         cross_x1_L_r <= cross_x1_L; 340         cross_x1_R_r <= cross_x1_R; 341         cross_x2_L_r <= cross_x2_L; 342         cross_x2_R_r <= cross_x2_R; 343     end
344 end
345 
346 
347 //根據特征交點數匹配相應數字
348 always@(posedge neg_vs)begin
349     if(cross_y==1)begin
350         if(cross_x1==1 && cross_x2==1) 351             result=4'd1;
352     end
353     else if(cross_y==2)begin
354         if(cross_x1==1 && cross_x2==1) 355             result=4'd7;
356         else if(cross_x1==2 && cross_x2==1) 357             result=4'd4;
358         else if(cross_x1==2 && cross_x2==2) 359             result=4'd0;
360     end
361     else if(cross_y==3)begin
362         if(cross_x1==1 && cross_x2==2) 363             result=4'd6;
364         else if(cross_x1==2 && cross_x2==1) 365             result=4'd9; 
366         else if(cross_x1==2 && cross_x2==2) 367             result=4'd8; 
368         else if(cross_x1==1 && cross_x2==1)begin
369             if(cross_x1_R==1 && cross_x2_L==1) 370                 result=4'd2;
371             else if(cross_x1_R==1 && cross_x2_R==1) 372                 result=4'd3;
373             else if(cross_x1_L==1 && cross_x2_R==1) 374                 result=4'd5; 
375         end
376     end
377     else
378         result=4'd10;
379 end
380 
381 
382 //========================================================================== 383 //== 信號同步 384 //==========================================================================
385 reg Y_de_r; 386 reg Y_hs_r; 387 reg Y_vs_r; 388 always @(posedge clk or negedge rst_n) begin
389     if(!rst_n) begin
390         Y_de_r <= 'b0;
391         Y_hs_r <= 'b0;
392         Y_vs_r <= 'b0;
393     end
394     else begin  
395         Y_de_r <= Y_de; 396         Y_hs_r <= Y_hs; 397         Y_vs_r <= Y_vs; 398     end
399 end
400 //畫特征線消耗1clk
401 assign DR_de = Y_de_r; 402 assign DR_hs = Y_hs_r; 403 assign DR_vs = Y_vs_r; 404 
405 endmodule

實驗效果:

FPGA圖像處理——數字識別

https://www.bilibili.com/video/BV1H44y1z7zf

  算法原理網上到處都是,但是對於初學者更想有一份源碼,經過獨自摸索終於效果差強人意,現將代碼分享給廣大萌新以供參考,對於不足處請指正,謝謝!

 

參考資料:[1] Opens Lee:FPGA開源工作室(公眾號)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM