一、基于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开源工作室(公众号)