一、前言
本篇主要針對牟新剛編著《基於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直接變換的效果好;