一、前言
本篇主要针对牟新刚编著《基于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直接变换的效果好;
来源:oschina
链接:https://my.oschina.net/u/4392886/blog/4297822