一、前言
本篇主要针对牟新刚编著《基于FPGA的数字图像处理原理及应用》中关于直方图
均衡化算法的功能仿真验证。2020-03-15 10:43:27
二、FPGA直方图均衡化算法原理
直方图均衡化又称为灰度均衡化,是指通过某种灰度映射使输入图像转换为在每一
灰度级上都有近似相同的输出图像(即输出的直方图是均匀的)。在经过均衡化处理后
的图像中,像素将占有尽可能多的灰度级并且分布均匀。因此,这样的图像将具有较高
的对比度和较大的动态范围,直方图均衡可以很好地解决相机过曝光或曝光不足的问题。
直方图均衡化计算步骤如下:
(1)首先计算出当前图像的直方图;
(2)其次计算像素直方图累计和;
(3)将上式乘以灰度值的最大值;
(4)将上式除以图像像素总数,我们也将后两步运算统称为归一化运算。
1.直方图累计和
直方图累加和的定义是小于指定像素的所有像素的统计值之和。
2.归一化计算
归一化计算的步骤是先乘以灰度最大值,然后再除以像素总数:
图像宽度x图像高度。
三、代码实现
代码包括直方图归一化计算文件hist_equalized、直方图累加和统计文件histogram_2d文件
及顶层文件hist_equal。
(1)histogram_2d.v文件中加入了累加和统计模块,主要加入了一个用于累加和统计的帧
双端口RAM;
1 `timescale 1ps/1ps 2 3 `define Equalize 1 4 //==============================================================================// 5 //FileName: histogram_2d.v 6 //Date: 2020-02-29 7 //==============================================================================// 8 9 module histogram_2d( 10 rst_n, 11 clk, 12 din_valid, //输入有效 13 din, //输入待统计的数据 14 dout, //统计输出 15 vsync, //输入场同步 16 dout_valid, //输出有效 17 rdyOutput, //数据读出请求 18 //dout_clk //数据输出时钟 19 `ifdef Equalize 20 hist_cnt_addr, 21 hist_cnt_out, 22 `endif 23 int_flag //中断输出 24 25 ); 26 27 //模块入口参数 28 parameter DW = 14; //数据位宽 29 parameter IH = 512; //图像高度 30 parameter IW = 640; //图像宽度 31 parameter TW = 32; //直方图统计数据位宽 32 33 localparam TOTAL_CNT = IW * IH; //像素总数 34 localparam HALF_WIDTH = (TW >> 1); //将32位的数据位宽拆分为高低16位 35 36 37 //输入输出声明 38 input rst_n; 39 input clk; 40 input din_valid; 41 input [DW-1:0] din; 42 input rdyOutput; 43 44 output reg [HALF_WIDTH:0] dout; 45 46 //output wire [TW-1:0] dout; 47 48 input vsync; 49 output reg dout_valid; 50 output reg int_flag; 51 //output dout_clk; 52 53 `ifdef Equalize 54 input [DW-1:0] hist_cnt_addr; 55 output reg [TW-1:0] hist_cnt_out; 56 `endif 57 58 //变量声明 59 reg vsync_r; 60 reg dvalid_r; 61 reg dvalid_r2; 62 reg [DW-1:0] din_r; 63 reg [DW-1:0] din_r2; 64 65 wire hsync_fall; 66 wire hsync_rise; 67 68 reg [9:0] hsync_count; 69 reg count_en; 70 wire [DW-1:0] mux_addr_b; 71 wire [DW-1:0] mux_addr_b2; 72 73 wire [TW-1:0] q_a; 74 wire [TW-1:0] q_b; 75 reg [TW-1:0] counter; 76 77 wire [TW-1:0] count_value; 78 wire rst_cnt; //统计计数器复位信号 79 wire inc_en; //递增使能信号 80 81 //DPRAM 信号 82 wire we_a; 83 wire we_b; 84 wire we_b_l; 85 reg we_b_h; 86 87 wire [DW-1:0] addr_a; 88 //中断寄存器 89 reg int_r; 90 wire [DW-1:0] clr_addr; //清零地址 91 reg [DW-1:0] clr_addr_r; 92 reg [DW:0] out_pixel; //输出计数 93 94 reg count_all; //统计完成信号 95 //reg count_en_r; 96 reg count_en_r; 97 98 reg [TW-1:0] hist_cnt; //直方图统计累加寄存器 99 wire rstOutput; //读出电路复位信号 100 101 wire [TW-1:0] dataTmp2; 102 wire clr_flag; //全局清零 103 104 //将输入数据打两拍 105 always@(posedge clk or negedge rst_n)begin 106 if(((~(rst_n))) == 1'b1) 107 begin 108 vsync_r <= #1 1'b0; 109 dvalid_r <= #1 1'b0; 110 dvalid_r2 <= #1 1'b0; 111 din_r <= #1 {DW{1'b0}}; 112 din_r2 <= #1 {DW{1'b0}}; 113 end 114 else 115 begin 116 vsync_r <= #1 vsync; 117 dvalid_r <= #1 din_valid; 118 dvalid_r2 <= #1 dvalid_r; 119 din_r <= #1 din; 120 din_r2 <= #1 din_r; 121 end 122 end 123 124 //输入行同步计数,确定统计的开始和结束时刻 125 assign #1 hsync_fall = dvalid_r & (~(din_valid)); 126 assign #1 hsync_rise = (~(dvalid_r)) & din_valid; 127 128 always@(posedge clk or negedge rst_n)begin 129 if(((~(rst_n))) == 1'b1) 130 hsync_count <= #1 {10{1'b0}}; 131 else 132 begin 133 if(vsync_r == 1'b1) 134 hsync_count <= #1 {10{1'b0}}; 135 else if(hsync_fall == 1'b1) 136 hsync_count <= hsync_count + 10'b1; 137 else 138 hsync_count <= hsync_count; 139 end 140 end 141 142 //一帧图像结束后停止统计 下一帧图像到来时开始计数 143 always@(posedge clk or negedge rst_n)begin 144 if(((~(rst_n))) == 1'b1) 145 count_en <= #1 1'b0; 146 else 147 begin 148 if(hsync_count >= IH) 149 count_en <= #1 1'b0; 150 else if(hsync_rise == 1'b1) 151 count_en <= #1 1'b1; 152 else 153 count_en <= #1 count_en; 154 end 155 end 156 157 assign mux_addr_b = ((count_en == 1'b1)) ? din_r : clr_addr; 158 assign mux_addr_b2 = ((count_en == 1'b1)) ? din_r : clr_addr_r; 159 160 //统计递增计数器 161 always@(posedge clk)begin 162 if(rst_cnt == 1'b1) 163 counter <= #1 {{TW-1{1'b0}},1'b1}; //复位值为1 164 else if(inc_en == 1'b1) 165 counter <= #1 counter + {{TW-1{1'b0}},1'b1}; 166 else 167 counter <= #1 counter; 168 end 169 170 assign #1 rst_cnt = ((din_r != din_r2) | ((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0))) ? 1'b1 : 1'b0; 171 assign #1 inc_en = (((din_r == din_r2) & (dvalid_r2 == 1'b1))) ? 1'b1 : 1'b0; 172 assign #1 we_a = ((((din_r != din_r2) & (dvalid_r2 == 1'b1)) |((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0)))) ? 1'b1 : 1'b0; 173 assign #1 count_value = ((count_en == 1'b1)) ? counter+q_b : {TW{1'b0}}; 174 assign #1 addr_a = din_r2; 175 176 177 //直方图存储器 分高16位和低16位分别存储 178 hist_buffer dpram_bin_l( 179 .address_a(addr_a), //输入地址为像素灰度值 180 .address_b(mux_addr_b), //读出和清零地址 181 .clock(clk), //同步时钟 182 .data_a(count_value[HALF_WIDTH-1:0]), //当前计数值 183 .data_b({HALF_WIDTH{1'b0}}), //清零数据 184 .wren_a(we_a), 185 .wren_b(we_b_l), 186 .q_a(q_a[HALF_WIDTH-1:0]), 187 .q_b(q_b[HALF_WIDTH-1:0]) 188 ); 189 190 191 hist_buffer dpram_bin_h( 192 .address_a(addr_a), 193 .address_b(mux_addr_b2), 194 .clock(clk), 195 .data_a(count_value[TW-1:HALF_WIDTH]), 196 .data_b({HALF_WIDTH{1'b0}}), 197 .wren_a(we_a), 198 .wren_b(we_b_h), 199 .q_a(q_a[TW-1:HALF_WIDTH]), 200 .q_b(q_b[TW-1:HALF_WIDTH]) 201 ); 202 203 always@(posedge clk or negedge rst_n)begin 204 if(((~(rst_n))) == 1'b1) 205 count_en_r <= #1 1'b0; 206 else 207 count_en_r <= #1 count_en; 208 end 209 210 //读出电路逻辑,计数时不能输出,读出请求时才输出 211 assign rstOutput = count_en_r | (~(rdyOutput)); 212 213 //输出像素计数 214 always@(posedge clk)begin 215 if(rstOutput == 1'b1) 216 out_pixel <= {DW+1{1'b0}}; 217 else begin 218 if((~count_all) == 1'b1) 219 begin 220 if(out_pixel == (((2 ** (DW + 1)) - 1))) 221 out_pixel <= #1 {DW+1{1'b0}}; //输出完毕 222 else 223 out_pixel <= #1 out_pixel + 1'b1; 224 end 225 end 226 end 227 228 //统计结束信号 229 always@(posedge clk)begin 230 //count_all_r <= (~rstOutput); 231 we_b_h <= we_b_l; 232 clr_addr_r <= clr_addr; 233 if(out_pixel == (((2 ** (DW + 1)) - 1))) 234 count_all <= #1 1'b1; 235 else if(count_en == 1'b1) 236 count_all <= #1 1'b0; 237 end 238 239 //全局清零信号 240 assign clr_flag = vsync; 241 242 //中断输出 信号读出操作完成 243 always@(posedge clk or negedge rst_n)begin 244 if((~(rst_n)) == 1'b1) 245 begin 246 int_flag <= 1'b1; 247 int_r <= 1'b1; 248 end 249 else 250 begin 251 int_flag <= #1 int_r; 252 if(clr_flag == 1'b1) 253 int_r <= #1 1'b1; 254 else if(out_pixel >= (((2 ** (DW + 1)) - 1))) 255 int_r <= #1 1'b0; 256 end 257 end 258 259 assign we_b_l = (((out_pixel[0] == 1'b1) & (count_all == 1'b0))) ? 1'b1 : 1'b0; 260 261 //清零地址,与读出地址反相 262 assign clr_addr = out_pixel[DW:1]; 263 264 wire dout_valid_temp; 265 266 wire [HALF_WIDTH-1:0] dout_temp; 267 268 always@(posedge clk or negedge rst_n)begin 269 if((~(rst_n)) == 1'b1) 270 begin 271 dout <= {HALF_WIDTH{1'b0}}; 272 dout_valid <= 1'b0; 273 end 274 else 275 begin 276 dout <= #1 dout_temp; 277 dout_valid <= #1 dout_valid_temp; 278 end 279 end 280 281 assign dout_temp = (we_b_l == 1'b1) ? q_b[HALF_WIDTH-1:0] : q_b[TW-1:HALF_WIDTH]; 282 283 assign dout_valid_temp = we_b_h | we_b_l; //输出使能 284 285 //assign dout_clk = (dout_valid) ? we_b_h : 1'b0; 286 287 //assign dout = q_b; 288 289 always@(posedge clk or negedge rst_n)begin 290 if((~(rst_n)) == 1'b1) 291 hist_cnt <= {TW{1'b0}}; //复位清零 292 else begin 293 if(vsync_r == 1'b0 & vsync == 1'b1) //新的一帧到来时清零 294 hist_cnt <= {TW{1'b0}}; 295 else if(out_pixel[0] == 1'b1) //每个像素读出时刻 296 hist_cnt <= hist_cnt + q_b; //将结果累加 297 else 298 hist_cnt <= hist_cnt; 299 end 300 end 301 302 reg [DW:0] out_pixel_r; 303 reg [DW-1:0] out_pixel_r2; 304 305 wire [TW-1:0] hist_cnt_temp; 306 307 always@(posedge clk or negedge rst_n)begin 308 if((~(rst_n)) == 1'b1)begin 309 out_pixel_r <= {DW+1{1'b0}}; 310 out_pixel_r2 <= {DW{1'b0}}; 311 hist_cnt_out <= {TW{1'b0}}; 312 end 313 else begin 314 out_pixel_r <= #1 out_pixel; 315 out_pixel_r2 <= #1 out_pixel_r[DW:1]; 316 hist_cnt_out <= #1 hist_cnt_temp; //将数据打一拍后输出 317 end 318 end 319 320 hist_buffer_cnt hist_cnt_buf( 321 .address_a(out_pixel_r2), //写入地址,直方图当前地址 322 .address_b(hist_cnt_addr), //读出地址 323 .clock(clk), //同步时钟 324 .data_a(hist_cnt), //写入数据 325 .data_b(), 326 .wren_a(dout_valid), //写入时刻:直方图数据有效 327 .wren_b(1'b0), 328 .q_a(), 329 .q_b(hist_cnt_temp) //输出数据 330 ); 331 332 endmodule
(2) hist_equalized.v文件主要用于计算直方图均衡化后的像素值,其中计算开销6个时钟,咋来的?
1 `timescale 1ps/1ps 2 3 //==========================================================================================================// 4 //FileName: hist_equalized.v 5 //Date: 2020-03-12 6 //==========================================================================================================// 7 8 module hist_equalized( 9 rst_n, 10 clk, 11 din_valid, //输入数据有效 12 din, //输入数据 13 dout, //输出数据 14 vsync, //输入场同步 15 dout_valid, //输出有效 16 vsync_out //输出场同步 17 ); 18 19 parameter DW = 8; //数据位宽 20 parameter IH = 512; //图像高度 21 parameter IW = 640; //图像宽度 22 parameter TW = 32; //直方图数据位宽 23 24 localparam TOTAL_CNT = IW * IH; 25 localparam HALF_WIDTH = (TW >> 1); 26 27 //计算开销 28 localparam latency = 6; 29 30 input rst_n; 31 input clk; 32 input din_valid; 33 input [DW-1:0] din; 34 output [DW-1:0] dout; 35 input vsync; 36 output vsync_out; 37 output dout_valid; 38 39 reg [DW-1:0] hist_cnt_addr; 40 wire [TW-1:0] hist_cnt_out; 41 42 //首先需例化一个直方图统计模块对输入图像进行直方图统计 43 //注意我们只需要得到直方图统计累加和信息 44 45 histogram_2d hist( 46 .rst_n(rst_n), 47 .clk(clk), 48 .din_valid(din_valid), 49 .din(din), 50 .vsync(vsync), 51 .dout(), //直方图统计输出 52 .dout_valid(), //输出有效 53 .rdyOutput(), //数据读出请求 54 .hist_cnt_addr(hist_cnt_addr), //累加和输入地址 55 .hist_cnt_out(hist_cnt_out), //累加和输出 56 .int_flag() 57 ); 58 59 defparam hist.DW = DW; 60 defparam hist.IH = IH; 61 defparam hist.IW = IW; 62 63 wire vsync_fall; 64 wire valid; 65 reg [1:0] frame_cnt; 66 reg hist_valid_temp; 67 reg vsync_r; 68 69 //由于至少需要等到第一帧输出完毕之后才能完成第一帧数据的直方图统计信息, 70 //因此有必要对图像帧进行计数 71 always@(posedge clk or negedge rst_n)begin 72 if((~(rst_n)) == 1'b1)begin 73 vsync_r <= #1 1'b0; 74 hist_valid_temp <= 1'b0; 75 frame_cnt <= 2'b0; 76 end 77 else begin 78 vsync_r <= #1 vsync; 79 80 if(vsync_fall) 81 frame_cnt <= frame_cnt + 2'b01; //每帧结束时帧计数加1 82 else 83 frame_cnt <= frame_cnt; 84 85 if(frame_cnt >= 2'b10) //第二帧开始输入均衡操作才开始有效 86 hist_valid_temp <= 1'b1; 87 end 88 end 89 90 //场同步下降沿信号 91 assign vsync_fall = (vsync & ~vsync_r); 92 93 //全局有效信号 94 assign valid = hist_valid_temp & din_valid; 95 96 //缓存全局有效信号,完成时序对齐 97 reg [latency:0] valid_r; 98 99 always@(posedge clk or negedge rst_n)begin 100 if((~(rst_n)) == 1'b1)begin 101 valid_r[latency:0] <= {latency+1{1'b0}}; 102 end 103 else begin 104 valid_r <= #1 {valid_r[latency-1:0],valid}; 105 end 106 end 107 108 reg [DW-1:0] din_r; 109 110 //缓存输入数据完成时序对齐 111 always@(posedge clk or negedge rst_n)begin 112 if((~(rst_n)) == 1'b1)begin 113 din_r <= {DW{1'b0}}; 114 end 115 else begin 116 din_r <= #1 din; 117 end 118 end 119 120 //查询当前像素的直方图统计累加和 121 always@(posedge clk or negedge rst_n)begin 122 if((~(rst_n)) == 1'b1)begin 123 hist_cnt_addr <= {DW{1'b0}}; 124 end 125 else begin 126 if(valid_r[0]) 127 hist_cnt_addr <= #1 din_r; 128 else 129 hist_cnt_addr <= hist_cnt_addr; 130 end 131 end 132 133 reg [2*TW-1:0] mul_temp[0:2]; 134 reg [DW-1:0] dout_temp; 135 136 //对于分辨率512*512的图像而言 137 generate 138 if((IW == 512) & (IH == 512))begin : IW_512 139 always@(posedge clk or negedge rst_n) 140 if((~(rst_n)) == 1'b1)begin 141 mul_temp[0] <= {2*TW{1'b0}}; 142 end 143 else begin 144 if(valid_r[1]) 145 //hist_cnt_out*255 - 1, 146 mul_temp[0] <= {{TW-DW{1'b0}},hist_cnt_out[TW-1:0],{DW{1'b0}}} - {{TW{1'b0}},hist_cnt_out}; 147 if(valid_r[1]) 148 //hist_cnt_out/(512*512) IW = IH =512 149 mul_temp[1] <= #1 {{18{1'b0}},mul_temp[0][2*TW-1:18]}; 150 if(valid_r[2]) 151 dout_temp <= #1 mul_temp[1][DW-1:0]; 152 end 153 end 154 endgenerate 155 156 //对于分辨率为640*512的图像而言 157 generate 158 if(IW == 640 & IH == 512)begin : IW_640 159 wire [2*TW-1:0] dout_temp_r/*synthesis keep*/; 160 161 assign dout_temp_r = {{16{1'b0}},mul_temp[2][2*TW-1:16]}; 162 163 always@(posedge clk or negedge rst_n) 164 if((~(rst_n)) == 1'b1)begin 165 mul_temp[0] <= {2*TW{1'b0}}; 166 end 167 else begin 168 if(valid_r[1]) 169 //hist_cnt_out*51,DW must be 8 170 //hist_cnt_out*32 + hist_cnt_out*16 171 mul_temp[0] <= #1 {{TW-5{1'b0}},hist_cnt_out[TW-1:0],{5{1'b0}}} + {{TW-4{1'b0}},hist_cnt_out[TW-1:0],{4{1'b0}}}; 172 //hist_cnt_out*1 + hist_cnt_out*2 173 mul_temp[1] <= #1 {{TW{1'b0}},hist_cnt_out[TW-1:0]} + {{TW-1{1'b0}},hist_cnt_out[TW-1:0],{1{1'b0}}}; 174 175 if(valid_r[1]) 176 //hist_cnt_out/(64*2*512) 177 mul_temp[2] <= #1 mul_temp[0] + mul_temp[1]; 178 179 if(valid_r[2]) 180 dout_temp <= #1 dout_temp_r[DW-1:0]; 181 end 182 end 183 endgenerate 184 185 //完成数据输出对齐 186 assign dout = dout_temp; 187 assign dout_valid = valid_r[latency]; 188 assign vsync_out = vsync; 189 190 endmodule
(6)用于功能仿真的顶层文件,包含图像数据的捕获、灰度图像转换及图像均衡化模块;
1 `timescale 1ps/1ps 2 3 //=========================================================================================// 4 //FileName: hist_equal.v TOP FILE 5 //Date: 2020-03-12 6 //=========================================================================================// 7 8 module hist_equal( 9 RSTn, //全局复位 10 CLOCK, //系统时钟 11 12 IMG_CLK, //像素时钟 13 IMG_DVD, //像素值 14 IMG_DVSYN, //输入场信号 15 IMG_DHSYN, //输入数据有效信号 16 HISTEQUAL_DAT, //输出直方均衡化数据 17 HISTEQUAL_VALID, //输出直方均衡化有效信号 18 HISTEQUAL_VSYNC //输出直方图均衡化场有效信号 19 ); 20 21 /*image parameter*/ 22 parameter iw = 640; //image width 23 parameter ih = 512; //image height 24 parameter trig_value = 400; //250 25 parameter tw = 32; //直方图统计数据位宽 26 27 localparam half_width = (tw >> 1); //将32位的数据位宽拆分为高低16位 28 29 /*data width*/ 30 parameter dvd_dw = 8; //image source data width 31 parameter dvd_chn = 3; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr 32 parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width 33 parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width 34 35 //Port Declared 36 input RSTn; 37 input CLOCK; 38 input IMG_CLK; 39 input [dvd_dw-1:0] IMG_DVD; 40 input IMG_DVSYN; 41 input IMG_DHSYN; 42 output [dvd_dw-1:0] HISTEQUAL_DAT; 43 output HISTEQUAL_VALID; 44 output HISTEQUAL_VSYNC; 45 46 47 //Variable Declared 48 wire GRAY_CLK; 49 wire GRAY_VSYNC; 50 wire GRAY_DVALID; 51 wire [dvd_dw-1:0] Y_DAT; 52 wire [dvd_dw-1:0] Cb_DAT; 53 wire [dvd_dw-1:0] Cr_DAT; 54 55 wire [local_dw-1:0] RGB_DAT; 56 wire RGB_DVALID; 57 wire RGB_VSYNC; 58 59 video_cap video_cap_inst( 60 .reset_l(RSTn), //异步复位信号 61 .DVD(IMG_DVD), //输入视频流 62 .DVSYN(IMG_DVSYN), //输入场同步信号 63 .DHSYN(IMG_DHSYN), //输入行同步 64 .DVCLK(IMG_CLK), //输入DV时钟 65 .cap_dat(RGB_DAT), //输出RGB通道像素流,24位 66 .cap_dvalid(RGB_DVALID), //输出数据有效 67 .cap_vsync(RGB_VSYNC), //输出场同步 68 .cap_clk(CLOCK), //本地逻辑时钟 69 .img_en(), 70 .cmd_rdy(), //命令行准备好,代表可以读取 71 .cmd_rdat(), //命令行数据输出 72 .cmd_rdreq() //命令行读取请求 73 ); 74 75 defparam video_cap_inst.DW_DVD = dvd_dw; 76 defparam video_cap_inst.DW_LOCAL = local_dw; 77 defparam video_cap_inst.DW_CMD = cmd_dw; 78 defparam video_cap_inst.DVD_CHN = dvd_chn; 79 defparam video_cap_inst.TRIG_VALUE = trig_value; 80 defparam video_cap_inst.IW = iw; 81 defparam video_cap_inst.IH = ih; 82 83 RGB2YCrCb RGB2YCrCb_Inst( 84 .RESET(RSTn), //异步复位信号 85 86 .RGB_CLK(CLOCK), //输入像素时钟 87 .RGB_VSYNC(RGB_VSYNC), //输入场同步信号 88 .RGB_DVALID(RGB_DVALID), //输入数据有信号 89 .RGB_DAT(RGB_DAT), //输入RGB通道像素流,24位 90 91 .YCbCr_CLK(GRAY_CLK), //输出像素时钟 92 .YCbCr_VSYNC(GRAY_VSYNC), //输出场同步信号 93 .YCbCr_DVALID(GRAY_DVALID), //输出数据有效信号 94 .Y_DAT(Y_DAT), //输出Y分量 95 .Cb_DAT(Cb_DAT), //输出Cb分量 96 .Cr_DAT(Cr_DAT) //输出Cr分量 97 ); 98 99 defparam RGB2YCrCb_Inst.RGB_DW = local_dw; 100 defparam RGB2YCrCb_Inst.YCbCr_DW = dvd_dw; 101 102 103 hist_equalized hist_equalized_inst( 104 .rst_n(RSTn), 105 .clk(GRAY_CLK), 106 .din_valid(GRAY_DVALID), //输入数据有效 107 .din(Y_DAT), //输入数据 108 .dout(HISTEQUAL_DAT), //输出数据 109 .vsync(GRAY_VSYNC), //输入场同步 110 .dout_valid(HISTEQUAL_VALID), //输出有效 111 .vsync_out(HISTEQUAL_VSYNC) //输出场同步 112 ); 113 114 defparam hist_equalized_inst.DW = dvd_dw; 115 defparam hist_equalized_inst.IH = ih; 116 defparam hist_equalized_inst.IW = iw; 117 defparam hist_equalized_inst.TW = tw; 118 119 endmodule
(4)用于Modelsim仿真的testbench文件;
1 `timescale 1ps/1ps 2 3 module histequalized_tb; 4 5 6 /*image para*/ 7 parameter iw = 640; //image width 8 parameter ih = 512; //image height 9 parameter trig_value = 400; //250 10 11 /*video parameter*/ 12 parameter h_total = 2000; 13 parameter v_total = 600; 14 parameter sync_b = 5; 15 parameter sync_e = 55; 16 parameter vld_b = 65; 17 18 parameter clk_freq = 72; 19 20 /*data width*/ 21 parameter dvd_dw = 8; //image source data width 22 parameter dvd_chn = 3; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr 23 parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width 24 parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width 25 26 27 /*test module enable*/ 28 parameter hist_equalized_en = 1; 29 30 /*signal group*/ 31 reg pixel_clk = 1'b0; 32 reg reset_l; 33 reg [3:0] src_sel; 34 35 36 /*input dv group*/ 37 wire dv_clk; 38 wire dvsyn; 39 wire dhsyn; 40 wire [dvd_dw-1:0] dvd; 41 42 /*dvd source data generated for simulation*/ 43 image_src image_src_inst//#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b) 44 ( 45 .clk(pixel_clk), 46 .reset_l(reset_l), 47 .src_sel(src_sel), 48 .test_data(dvd), 49 .test_dvalid(dhsyn), 50 .test_vsync(dvsyn), 51 .clk_out(dv_clk) 52 ); 53 54 defparam image_src_inst.iw = iw*dvd_chn; 55 defparam image_src_inst.ih = ih + 1; 56 defparam image_src_inst.dw = dvd_dw; 57 defparam image_src_inst.h_total = h_total; 58 defparam image_src_inst.v_total = v_total; 59 defparam image_src_inst.sync_b = sync_b; 60 defparam image_src_inst.sync_e = sync_e; 61 defparam image_src_inst.vld_b = vld_b; 62 63 /*local clk: also clk of all local modules*/ 64 reg cap_clk = 1'b0; 65 66 /*hist equalized operation module*/ 67 generate 68 if(hist_equalized_en != 0)begin : equalized_operation 69 wire equalized_dvalid; 70 wire [dvd_dw-1:0] equalized_data; 71 wire equalized_vsync; 72 73 wire equalized_dvalid_in; 74 wire [dvd_dw-1:0] equalized_data_in; 75 wire equalized_vsync_in; 76 77 integer fp_equalized,cnt_equalized = 0; 78 79 /*video capture: capture image src and transfer it into local timing*/ 80 hist_equal hist_equal_inst( 81 .RSTn(reset_l), //全局复位 82 .CLOCK(cap_clk), //系统时钟 83 84 .IMG_CLK(pixel_clk), //像素时钟 85 .IMG_DVD(equalized_data_in), //像素值 86 .IMG_DVSYN(equalized_vsync_in), //输入场信号 87 .IMG_DHSYN(equalized_dvalid_in), //输入数据有效信号 88 .HISTEQUAL_DAT(equalized_data), //输出直方图统计数据 89 .HISTEQUAL_VALID(equalized_dvalid), //输出直方图统计有效 90 .HISTEQUAL_VSYNC(equalized_vsync) //数据读出请求 91 ); 92 93 assign equalized_data_in = dvd; 94 assign equalized_dvalid_in = dhsyn; 95 assign equalized_vsync_in = dvsyn; 96 97 always@(posedge cap_clk or posedge equalized_vsync)begin 98 if((~(equalized_vsync)) == 1'b0) 99 cnt_equalized = 0; 100 else begin 101 if(equalized_dvalid == 1'b1)begin 102 fp_equalized = $fopen("E:/Modelsim/hist_equalized/sim/equalized.txt","r+"); 103 $fseek(fp_equalized,cnt_equalized,0); 104 $fdisplay(fp_equalized,"%02X",equalized_data); 105 $fclose(fp_equalized); 106 cnt_equalized <= cnt_equalized + 4; 107 end 108 end 109 end 110 end 111 endgenerate 112 113 initial 114 begin: init 115 reset_l <= 1'b1; 116 src_sel <= 4'b0000; 117 #(100); //reset the system 118 reset_l <= 1'b0; 119 #(100); 120 reset_l <= 1'b1; 121 end 122 123 //dv_clk generate 124 always@(reset_l or pixel_clk)begin 125 if((~(reset_l)) == 1'b1) 126 pixel_clk <= 1'b0; 127 else 128 begin 129 if(clk_freq == 48) //48MHz 130 pixel_clk <= #10417 (~(pixel_clk)); 131 132 else if(clk_freq == 51.84) //51.84MHz 133 pixel_clk <= #9645 (~(pixel_clk)); 134 135 else if(clk_freq == 72) //72MHz 136 pixel_clk <= #6944 (~(pixel_clk)); 137 end 138 end 139 140 //cap_clk generate: 25MHz 141 always@(reset_l or cap_clk)begin 142 if((~(reset_l)) == 1'b1) 143 cap_clk <= 1'b0; 144 else 145 cap_clk <= #20000 (~(cap_clk)); 146 end 147 148 wire clk; 149 assign clk = ~cap_clk; 150 151 endmodule 152
(5)用于Modelsim仿真的.do文件;
1 #切换至工程目录 2 cd E:/Modelsim/hist_equalized/sim 3 4 #打开工程 5 project open E:/Modelsim/hist_equalized/sim/hist_equalized 6 7 #添加指定设计文件 8 project addfile E:/Modelsim/hist_equalized/sim/histequalized_tb.v 9 project addfile E:/Modelsim/hist_equalized/src/cross_clock_fifo.v 10 project addfile E:/Modelsim/hist_equalized/src/hist_buffer.v 11 project addfile E:/Modelsim/hist_equalized/src/hist_equal.v 12 project addfile E:/Modelsim/hist_equalized/src/hist_equalized.v 13 project addfile E:/Modelsim/hist_equalized/src/histogram_2d.v 14 project addfile E:/Modelsim/hist_equalized/src/image_src.v 15 project addfile E:/Modelsim/hist_equalized/src/line_buffer_new.v 16 project addfile E:/Modelsim/hist_equalized/src/RGB2YCbCr.v 17 project addfile E:/Modelsim/hist_equalized/src/video_cap.v 18 project addfile E:/Modelsim/hist_equalized/prj/hist_buffer_cnt.v 19 20 #编译工程内的所有文件 21 project compileall 22 23 #仿真work库下面的histequalized_tb实例,同时调用altera_lib库,不进行任何优化 24 vsim -t 1ps -novopt -L altera_lib work.histequalized_tb 25 26 #添加输入灰度图像信号 27 add wave -divider GRAYImg 28 29 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/clk 30 31 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync 32 33 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/din_valid 34 35 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/din 36 37 #输入信号缓存及处理 38 add wave -divider DataCache 39 40 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync_r 41 42 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/frame_cnt 43 44 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/hist_valid_temp 45 46 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync_fall 47 48 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/valid 49 50 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/valid_r 51 52 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/din_r 53 54 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/hist_cnt_addr 55 56 #计算归一化的值 57 add wave -divider Calculator 58 59 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/mul_temp 60 61 #add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout_temp_r 62 63 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout_temp 64 65 #添加数据输出 66 add wave -divider EqualDataOut 67 68 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout_valid 69 70 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync_out 71 72 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout 73 74 #复位 75 restart 76 77 #取消警告 78 set StdArithNoWarnings 1 79 80 #开始 81 run 35ms
四、仿真结果
(1)算法时序仿真:存在两个问题,第一,书中提供的代码仿真时,帧计数有问题,主要是图像数据采集模块生成的场信号有问题;因此实际是帧计数为3时,
开始进行直方图均衡化操作。第二个问题就是计算开销latenc= 6?计算开销为6,没整明白?
(2)FPGA处理结果与Matlab处理结果对比,可见算法对图像有一定增强对比度的作用,但是效果的没有Matlab直接变换的效果好;