一、前言
最近学习牟新刚编著《基于FPGA的数字图像处理原理及应用》的第六章直方图操作,由于需要将捕获的图像转换为灰度图像,因此在之前代码的基础上加入了RGB图像转灰度图像的算法实现。
2020-02-29 10:38:40
二、RGB图像转灰度图像算法原理
将彩色图像转换为灰度图像的方法有两种,一个是令RGB三个分量的数值相等。输出后便可以得到灰度图像,另一种是转换为YCbCr格式,将Y分量提取出来,YCbCr格式中的Y分量表示的是图
像的亮度和浓度,所以只输出Y分量,得到图像就是灰度图像。
YCbCr是通过有序的三元组来表示的,三元由Y(Luminance)、Cb(Chrominace-Blue)和Cr(Chrominace-Red)组成,其中Y表示颜色的明亮度和浓度,而Cb和Cr则分别表示颜色的蓝色浓度
偏移量和红色浓度偏移量。人的肉眼对由YCbCr色彩空间编码的视频中Y分量更敏感,而Cb和Cr的微小变换不会引起视觉上的不同。根据该原理,通过对Cb和Cr进行子采样来减小图像的数据量。使得
图像对存储需求和传输带宽的要求大大降低,从而达到完成图像压缩的同时,也保证了视觉上几乎没有损失的效果,进而使得图像的传输速度更快、存储更加方便。
官方给的RGB888转YCrCb的算法公式:
Y = 0.299R + 0.587G + 0.114B
Cb = 0.568(B-Y) + 128 = -0.172R -0.339G + 0.511B + 128
Cr = 0.713(R -Y) + 128 = 0.511R - 0.428G - 0.083B + 128
扩大256倍 →
Y = ((77*R + 150*G + 29*B)>>8)
Cb = ((-43*B - 85*G + 128*B)>>8) + 128
Cr = ((128*R - 107*G - 21*B)>>8) + 128
三、代码实现
代码分为三部分,包括视频码流生成image_src.v、视频捕获video_cap.v、彩色图像转灰度图像RGB2YCbCr.v及顶层文件rgb2gray.v;同时为了仿真及测试结果分析提供相应的matlab文件及用于
Modelsim仿真的rgb2gray.do文件。
(1)频码流生成image_src.v,生成640*512的24Bit RGB图像数据流;
1 /*
2 ***********************************************************************************************************
3 ** Input file: None
4 ** Component name: image_src.v
5 ** Author: zhengXiaoliang
6 ** Company: WHUT
7 ** Description: to simulate dvd stream
8 ***********************************************************************************************************
9 */
10
11 `timescale 1ps/1ps
12
13 `define SEEK_SET 0
14 `define SEEK_CUR 1
15 `define SEEK_END 2
16
17 module image_src(
18 reset_l, //全局复位
19 clk, //同步时钟
20 src_sel, //数据源通道选择
21 test_vsync, //场同步输出
22 test_dvalid, //像素有效输出
23 test_data, //像素数据输出
24 clk_out //像素时钟输出
25 );
26
27 parameter iw = 640; //默认视频宽度
28 parameter ih = 512; //默认视频高度
29 parameter dw = 8; //默认像素数据位宽
30
31 parameter h_total = 1440; //行总数
32 parameter v_total = 600; //垂直总数
33
34 parameter sync_b = 5; //场前肩
35 parameter sync_e = 55; //场同步脉冲
36 parameter vld_b = 65; //场后肩
37
38 //port decleared
39 input reset_l,clk;
40 input [3:0] src_sel; //to select the input file
41 output test_vsync, test_dvalid,clk_out;
42 output [dw-1:0] test_data;
43
44
45 //variable decleared
46 reg [dw-1:0] test_data_reg;
47 reg test_vsync_temp;
48 reg test_dvalid_tmp;
49 reg [1:0] test_dvalid_r;
50
51 reg [10:0] h_cnt;
52 reg [10:0] v_cnt;
53
54 integer fp_r;
55 integer cnt = 0;
56
57 //输出像素时钟
58 assign clk_out = clk; //output the dv clk
59
60 //输出像素数据
61 assign test_data = test_data_reg; //test data output
62
63 //当行同步有效时,从文件读取像素数据输出到数据线上
64 always@(posedge clk or posedge test_vsync_temp)begin
65 if(((~(test_vsync_temp))) == 1'b0) //场同步清零文件指针
66 cnt <= 0; //clear file pointer when a new frame comes
67 else begin
68 if(test_dvalid_tmp == 1'b1)begin //行同步有效,说明当前时钟数据有效
69 case(src_sel) //选择不同的数据源
70 4'b0000: fp_r = $fopen("E:/Modelsim/rgb2gray/sim/rgb_image.txt","r");
71 4'b0001: fp_r = $fopen("txt_source/test_scr1.txt","r");
72 4'b0010: fp_r = $fopen("txt_source/test_scr2.txt","r");
73 4'b0011: fp_r = $fopen("txt_source/test_scr3.txt","r");
74 4'b0100: fp_r = $fopen("txt_source/test_scr4.txt","r");
75 4'b0101: fp_r = $fopen("txt_source/test_scr5.txt","r");
76 4'b0110: fp_r = $fopen("txt_source/test_scr6.txt","r");
77 4'b0111: fp_r = $fopen("txt_source/test_scr7.txt","r");
78 4'b1000: fp_r = $fopen("txt_source/test_scr8.txt","r");
79 4'b1001: fp_r = $fopen("txt_source/test_scr9.txt","r");
80 4'b1010: fp_r = $fopen("txt_source/test_scr10.txt","r");
81 4'b1011: fp_r = $fopen("txt_source/test_scr11.txt","r");
82 4'b1100: fp_r = $fopen("txt_source/test_scr12.txt","r");
83 4'b1101: fp_r = $fopen("txt_source/test_scr13.txt","r");
84 4'b1110: fp_r = $fopen("txt_source/test_scr14.txt","r");
85 4'b1111: fp_r = $fopen("txt_source/test_scr15.txt","r");
86 default: fp_r = $fopen("txt_source/test_src3.txt","r");
87 endcase
88
89 $fseek(fp_r,cnt,0); //查找当前需要读取的文件位置
90 $fscanf(fp_r,"%02x\n",test_data_reg); //将数据按指定格式读入test_data_reg寄存器
91
92 cnt <= cnt + 4; //移动文件指针到下一个数据
93 $fclose(fp_r); //关闭文件
94 $display("h_cnt = %d,v_cnt = %d, pixdata = %d",h_cnt,v_cnt,test_data_reg); //for debug use
95 end
96 end
97 end
98
99 //水平计数器,每来一个时钟就+1,加到h_total置零重新计数
100 always@(posedge clk or negedge reset_l)begin
101 if(((~(reset_l))) == 1'b1)
102 h_cnt <= #1 {11{1'b0}};
103 else begin
104 if(h_cnt == ((h_total -1)))
105 h_cnt <= #1 {11{1'b0}};
106 else
107 h_cnt <= #1 h_cnt + 11'b00000000001;
108 end
109 end
110
111 //垂直计数器:水平计数器计满后+1,计满后清零
112 always@(posedge clk or negedge reset_l)begin
113 if(((~(reset_l))) == 1'b1)
114 v_cnt <= #1 {11{1'b0}};
115 else begin
116 if(h_cnt == ((h_total - 1)))begin
117 if(v_cnt == ((v_total - 1)))
118 v_cnt <= #1 {11{1'b0}};
119 else
120 v_cnt <= #1 v_cnt + 11'b00000000001;
121 end
122 end
123 end
124
125 //场同步信号生成
126 always@(posedge clk or negedge reset_l)begin
127 if(((~(reset_l))) == 1'b1)
128 test_vsync_temp <= #1 1'b1;
129 else begin
130 if(v_cnt >= sync_b & v_cnt <= sync_e)
131 test_vsync_temp <= #1 1'b1;
132 else
133 test_vsync_temp <= #1 1'b0;
134 end
135 end
136
137 assign test_vsync = (~test_vsync_temp);
138
139 //水平同步信号生成
140 always@(posedge clk or negedge reset_l)begin
141 if(((~(reset_l))) == 1'b1)
142 test_dvalid_tmp <= #1 1'b0;
143 else begin
144 if(v_cnt >= vld_b & v_cnt < ((vld_b + ih)))begin
145 if(h_cnt == 10'b0000000000)
146 test_dvalid_tmp <= #1 1'b1;
147 else if(h_cnt == iw)
148 test_dvalid_tmp <= #1 1'b0;
149 end
150 else
151 test_dvalid_tmp <= #1 1'b0;
152 end
153 end
154
155 //水平同步信号输出
156 assign test_dvalid = test_dvalid_tmp;
157
158 always@(posedge clk or negedge reset_l)begin
159 if(((~(reset_l))) == 1'b1)
160 test_dvalid_r <= #1 2'b00;
161 else
162 test_dvalid_r <= #1 ({test_dvalid_r[0],test_dvalid_tmp});
163 end
164
165 endmodule
(2)视频捕获video_cap.v,捕获RGB图像数据,并输出RGB888格式的24bit数据码流;
1 //2020-02-17
2 //Huang.Wei
3 `timescale 1ps/1ps
4
5 module video_cap(
6 reset_l, //异步复位信号
7 DVD, //输入视频流
8 DVSYN, //输入场同步信号
9 DHSYN, //输入行同步
10 DVCLK, //输入DV时钟
11 cap_dat, //输出RGB通道像素流,24位
12 cap_dvalid, //输出数据有效
13 cap_vsync, //输出场同步
14 cap_clk, //本地逻辑时钟
15 img_en,
16 cmd_rdy, //命令行准备好,代表可以读取
17 cmd_rdat, //命令行数据输出
18 cmd_rdreq //命令行读取请求
19 );
20
21 parameter TRIG_VALUE = 250; //读触发值,也即行消隐时间
22 parameter IW = 640; //图像宽度
23 parameter IH = 512; //图像高度
24
25 parameter DW_DVD = 8; //输入像素宽度
26 parameter DVD_CHN = 3; //输入像素通道: RGB 3通道
27 parameter DW_LOCAL = 24; //本地捕获的数据宽度24位
28 parameter DW_CMD = 24; //命令行数据宽度
29 parameter VSYNC_WIDTH = 100; //9 //场同步宽度,9个时钟
30
31 parameter CMD_FIFO_DEPTH = 1024; //行缓存位宽
32 parameter CMD_FIFO_DW_DEPTH = 10;
33 parameter IMG_FIFO_DEPTH = 512; //异步fifo深度,选512
34 parameter IMG_FIFO_DW_DEPTH = 9;
35
36 //Port Declared
37 input reset_l;
38 input [DW_DVD-1:0] DVD;
39 input DVSYN;
40 input DHSYN;
41 input DVCLK;
42
43 output reg [DW_LOCAL-1:0] cap_dat;
44 output reg cap_dvalid;
45 output cap_vsync;
46 input cap_clk;
47 output img_en;
48
49 output reg cmd_rdy;
50 output [DW_CMD-1:0] cmd_rdat;
51 input cmd_rdreq;
52
53 //首先完成数据位宽转换
54 wire pixel_clk;
55 reg [1:0] vc_reset;
56 reg dv_enable;
57 reg [9:0] count_lines;
58 reg cmd_en;
59 reg cmd_wrreq;
60 reg cmd_wrreq_r;
61 reg rst_cmd_fifo;
62 wire [DW_CMD-1:0] cmd_din;
63 reg [DW_CMD-1:0] cmd_dat;
64
65 assign pixel_clk = DVCLK;
66
67 always@(posedge pixel_clk or negedge reset_l)begin
68 if(((~(reset_l))) == 1'b1)
69 begin
70 vc_reset <= 2'b00;
71 dv_enable <= 1'b0;
72 end
73 else
74 begin
75 dv_enable <= #1 1'b1;
76 if((~(DVSYN)) == 1'b1 & dv_enable == 1'b1)
77 vc_reset <= #1 ({vc_reset[0],1'b1});
78 end
79 end
80
81 reg [DW_DVD-1:0] vd_r[0:DVD_CHN-1];
82 reg [DVD_CHN*DW_DVD-1:0] data_merge;
83
84 reg vsync;
85 reg [DVD_CHN:0] hsync_r;
86 reg mux;
87 reg mux_r;
88
89 //缓存场同步和行同步信号
90 always@(posedge pixel_clk or negedge reset_l)begin
91 if(((~(reset_l))) == 1'b1)
92 begin
93 vsync <= 1'b0;
94 hsync_r <= {DVD_CHN+1{1'b0}};
95 end
96 else
97 begin
98 vsync <= #1 DVSYN;
99 hsync_r <= #1 {hsync_r[DVD_CHN-1:0],DHSYN};
100 end
101 end
102
103 //像素通道计算,指示当前像素属于RGB那个通道
104 reg [DVD_CHN:0] pixel_cnt;
105
106 always@(posedge pixel_clk or negedge reset_l)begin
107 if(((~(reset_l))) == 1'b1)
108 begin
109 pixel_cnt <= {DVD_CHN+1{1'b1}};
110 end
111 else
112 begin
113 if(hsync_r[1] == 1'b0)
114 pixel_cnt <= #1 {DVD_CHN+1{1'b1}};
115 else
116 if(pixel_cnt == DVD_CHN -1)
117 pixel_cnt <= #1 {DVD_CHN+1{1'b0}};
118 else
119 pixel_cnt <= #1 pixel_cnt + 1'b1;
120 end
121 end
122
123 integer i;
124 integer j;
125
126 //缓存输入DV,获得3个RGB通道值
127
128 always@(posedge pixel_clk or negedge reset_l)begin
129 if(((~(reset_l)))==1'b1)
130 for(i=0;i<DVD_CHN;i=i+1)
131 vd_r[i] <= {DW_DVD{1'b0}};
132 else
133 begin
134 vd_r[0] <= #1 DVD;
135 for(j=1;j<DVD_CHN;j=j+1)
136 vd_r[j] <= vd_r[j-1];
137 end
138 end
139
140
141 //RGB 合并有效信号
142 wire mux_valid;
143
144 always@(posedge pixel_clk or negedge reset_l)begin
145 if(((~(reset_l))) == 1'b1)
146 mux <= 1'b0;
147 else begin
148 if(hsync_r[DVD_CHN-2] == 1'b0)
149 mux <= #1 1'b1;
150 else
151 if(mux_valid == 1'b1)
152 mux <= #1 1'b1;
153 else
154 mux <= #1 1'b0;
155 end
156 end
157
158 always@(posedge pixel_clk)
159 mux_r <= mux;
160
161
162 wire [DVD_CHN*DW_DVD-1:0] dvd_temp;
163 wire mux_1st;
164
165 assign mux_1st = (~hsync_r[DVD_CHN]) & (hsync_r[DVD_CHN-1]);
166
167 //一个颜色通道
168 generate
169 if(DVD_CHN == 1)
170 begin: xhdl1
171 assign mux_valid = hsync_r[0];
172 assign dvd_temp = vd_r[0];
173 end
174 endgenerate
175
176
177 //两个颜色通道
178 generate
179 if(DVD_CHN == 2)
180 begin: xhdl2
181 assign mux_valid = mux_1st | (pixel_cnt == DVD_CHN - 1);
182 assign dvd_temp = {vd_r[0],vd_r[1]};
183 end
184 endgenerate
185
186 //三个颜色通道,将三路RBG数据合并到dvd_temp信号中
187 generate
188 if(DVD_CHN == 3)
189 begin: xhdl3
190 assign mux_valid = mux_1st | (pixel_cnt == 0);
191 assign dvd_temp = {vd_r[2],vd_r[1],vd_r[0]};
192 end
193 endgenerate
194
195 //四个颜色通道
196 generate
197 if(DVD_CHN == 4)
198 begin: xhdl4
199 assign mux_valid = mux_1st | (pixel_cnt == 1);
200 assign dvd_temp = {vd_r[0],vd_r[1],vd_r[2],vd_r[3]};
201 end
202 endgenerate
203
204 //将合并后的数据存入寄存器
205 always@(posedge pixel_clk or negedge reset_l)begin
206 if(((~(reset_l))) == 1'b1)
207 data_merge <= {DVD_CHN*DW_DVD{1'b0}};
208 else
209 begin
210 if(hsync_r[DVD_CHN] == 1'b1 & mux == 1'b1)
211 data_merge <= #1 dvd_temp;
212 end
213 end
214
215 //将合并后的数据打入异步fifo
216 wire [DW_DVD*DVD_CHN-1:0] fifo_din;
217 wire [DW_DVD*DVD_CHN-1:0] fifo_dout;
218
219 wire [IMG_FIFO_DW_DEPTH-1:0] rdusedw;
220 reg [9:0] trig_cnt;
221 wire fifo_empty;
222 reg fifo_wrreq;
223 reg fifo_wrreq_r;
224 //wire fifo_wrreq;
225
226 //assign fifo_wrreq = mux & hsync_r[DVD_CHN];
227
228 reg fifo_rdreq;
229 reg fifo_rdreq_r1;
230 reg rst_fifo;
231
232 //实例化异步fifo
233 cross_clock_fifo img_fifo(
234 .data(fifo_din),
235 .rdclk(cap_clk),
236 .rdreq(fifo_rdreq),
237 .wrclk(pixel_clk),
238 .wrreq(fifo_wrreq),
239 .q(fifo_dout),
240 .rdempty(fifo_empty),
241 .rdusedw(rdusedw),
242 .aclr(rst_fifo)
243 );
244
245 /*
246 defparam img_fifo.DW = DW_DVD*DVD_CHN;
247 defparam img_fifo.DEPTH = IMG_FIFO_DEPTH;
248 defparam img_fifo.DW_DEPTH = IMG_FIFO_DW_DEPTH;
249 */
250
251 assign fifo_din = data_merge;
252
253
254 //RGB合并时写入fifo
255 always@(posedge pixel_clk or negedge reset_l)begin
256 if(reset_l == 1'b0)begin
257 fifo_wrreq <= #1 1'b0;
258 fifo_wrreq_r <= #1 1'b0;
259 end
260 else begin
261 fifo_wrreq <= hsync_r[DVD_CHN] & mux_r;
262 fifo_wrreq_r <= fifo_wrreq;
263 end
264 end
265
266 //fifo中数据大于触发值时开始读,读完一行停止
267 always@(posedge cap_clk or negedge reset_l)begin
268 if(reset_l == 1'b0)
269 fifo_rdreq <= #1 1'b0;
270 else
271 begin
272 if((rdusedw >= TRIG_VALUE) & (fifo_empty == 1'b0))
273 fifo_rdreq <= #1 1'b1;
274 else if(trig_cnt == (IW - 1))
275 fifo_rdreq <= #1 1'b0;
276 end
277 end
278
279 //读计数
280 always@(posedge cap_clk or negedge reset_l)begin
281 if(reset_l == 1'b0)
282 trig_cnt <= #1 {10{1'b0}};
283 else
284 begin
285 if(fifo_rdreq == 1'b0)
286 trig_cnt <= #1 {10{1'b0}};
287 else
288 if(trig_cnt == (IW - 1))
289 trig_cnt <= #1 {10{1'b0}};
290 else
291 trig_cnt <= #1 trig_cnt + 10'b0000000001;
292 end
293 end
294
295 wire [DW_LOCAL-1:0] img_din;
296
297 assign img_din = ((cmd_en == 1'b0)) ? fifo_dout[DW_LOCAL-1:0] : {DW_LOCAL{1'b0}};
298
299 assign cmd_din = ((cmd_en == 1'b1)) ? fifo_dout[DW_CMD-1:0] : {DW_CMD{1'b0}};
300
301 //生成场同步信号、数据有效信号及像素数据输出
302 reg vsync_async;
303 reg vsync_async_r1;
304 reg [VSYNC_WIDTH:0] vsync_async_r;
305 reg cap_vsync_tmp;
306
307 always@(posedge cap_clk or negedge reset_l)begin
308 if(reset_l == 1'b0)
309 begin
310 vsync_async <= #1 1'b0;
311 vsync_async_r1 <= #1 1'b0;
312 vsync_async_r <= {VSYNC_WIDTH+1{1'b0}};
313 cap_vsync_tmp <= #1 1'b0;
314 end
315 else
316 begin
317 vsync_async <= #1 (~vsync);
318 vsync_async_r1 <= #1 vsync_async;
319 vsync_async_r <= {vsync_async_r[VSYNC_WIDTH-1:0], vsync_async_r1};
320 if(vsync_async_r[1] == 1'b1 & vsync_async_r[0] == 1'b0)
321 cap_vsync_tmp <= #1 1'b1;
322 else if(vsync_async_r[VSYNC_WIDTH] == 1'b0 & vsync_async_r[0] == 1'b0)
323 cap_vsync_tmp <= #1 1'b0;
324 end
325 end
326
327 assign cap_vsync = cap_vsync_tmp;
328
329 always@(posedge cap_clk or negedge reset_l)begin
330 if(reset_l==1'b0)
331 begin
332 cap_dat <= #1 {DW_LOCAL{1'b0}};
333 fifo_rdreq_r1 <= #1 1'b0;
334 cap_dvalid <= #1 1'b0;
335 cmd_dat <= #1 {DW_CMD{1'b0}};
336 cmd_wrreq <= #1 1'b0;
337 cmd_wrreq_r <= #1 1'b0;
338 end
339 else
340 begin
341 cap_dat <= #1 img_din;
342 fifo_rdreq_r1 <= #1 fifo_rdreq;
343 cap_dvalid <= #1 fifo_rdreq_r1 & (~(cmd_en));
344 cmd_dat <= #1 cmd_din;
345 cmd_wrreq <= #1 fifo_rdreq_r1 & cmd_en;
346 cmd_wrreq_r <= cmd_wrreq;
347 end
348 end
349
350 //frame count and img_en signal
351 reg [1:0] fr_cnt;
352 reg img_out_en;
353
354 always@(posedge cap_clk)begin
355 if(vc_reset[1] == 1'b0)
356 begin
357 img_out_en <= 1'b0;
358 fr_cnt <= {2{1'b0}};
359 end
360 else
361 begin
362 if(vsync_async_r1 == 1'b0 & vsync_async == 1'b1)
363 begin
364 fr_cnt <= fr_cnt + 2'b01;
365 if(fr_cnt == 2'b11)
366 img_out_en <= 1'b1;
367 end
368 end
369 end
370
371 assign img_en = img_out_en;
372
373
374 //行计数,确定cmd数据到来时刻
375 always@(posedge cap_clk)begin
376 if(cap_vsync_tmp == 1'b1)
377 begin
378 count_lines <= {10{1'b0}};
379 cmd_en <= 1'b0;
380 cmd_rdy <= 1'b0;
381 end
382 begin
383 if(fifo_rdreq_r1 == 1'b1 & fifo_rdreq == 1'b0)
384 count_lines <= #1 count_lines + 4'h1;
385 if(count_lines == (IH - 2))
386 rst_cmd_fifo <= 1'b1;
387 else
388 rst_cmd_fifo <= 1'b0;
389 if(count_lines >= IH)
390 cmd_en <= #1 1'b1;
391 if(cmd_wrreq_r == 1'b1 & cmd_wrreq == 1'b0)
392 cmd_rdy <= 1'b1;
393 if(cmd_wrreq_r == 1'b1 & cmd_wrreq == 1'b0)
394 rst_fifo <= 1'b1;
395 else
396 rst_fifo <= 1'b0;
397 end
398 end
399
400
401 //Instance a line buffer to store the cmd line
402 line_buffer_new
403 cmd_buf(
404 .aclr(rst_cmd_fifo),
405 .clock(cap_clk),
406 .data(cmd_dat),
407 .rdreq(cmd_rdreq),
408 .wrreq(cmd_wrreq),
409 .empty(),
410 .full(),
411 .q(cmd_rdat),
412 .usedw()
413 );
414
415 /*
416 defparam cmd_buf.DW = DW_CMD;
417 defparam cmd_buf.DEPTH = CMD_FIFO_DEPTH;
418 defparam cmd_buf.DW_DEPTH = CMD_FIFO_DW_DEPTH;
419 defparam cmd_buf.IW = IW;
420 */
421 endmodule
(3)彩色图像转灰度图像RGB2YCbCr.v,实现RGB888到YCbCr图像格式的转换,并输出三个通道的数据;
1 //==================================================================================================//
2 //FileName: RGB2YCrCb.v
3 /*
4 官方给的RGB888 to YCbCr的计算公式:
5 Y = 0.299R + 0.587G + 0.114B
6 Cb = 0.568(B - Y) + 128 = -0.172R - 0.339G + 0.511B + 128
7 Cr = 0.713(R -Y) + 128 = 0.511R - 0.428G - 0.083B + 128
8
9 =>
10
11 Y = ((77*R + 150*G + 29*B)>>8);
12 Cb = ((-43*R - 85*G + 128*B)>>8) + 128;
13 Cr = ((128*R - 107*G - 21*B)>>8) + 128;
14 */
15 //Date: 2020-02-28
16 //==================================================================================================//
17 `timescale 1ps/1ps
18
19 module RGB2YCrCb(
20 RESET, //异步复位信号
21
22 RGB_CLK, //输入像素时钟
23 RGB_VSYNC, //输入场同步信号
24 RGB_DVALID, //输入数据有信号
25 RGB_DAT, //输入RGB通道像素流,24位
26
27 YCbCr_CLK, //输出像素时钟
28 YCbCr_VSYNC, //输出场同步信号
29 YCbCr_DVALID, //输出数据有效信号
30 Y_DAT, //输出Y分量
31 Cb_DAT, //输出Cb分量
32 Cr_DAT //输出Cr分量
33 );
34
35 parameter RGB_DW = 24; //输入像素宽度
36 parameter YCbCr_DW = 8; //输出像素宽度
37
38 //Port Declared
39 input RESET;
40 input RGB_CLK;
41 input RGB_VSYNC;
42 input RGB_DVALID;
43 input [RGB_DW-1:0]RGB_DAT;
44
45 output YCbCr_CLK;
46 output YCbCr_VSYNC;
47 output YCbCr_DVALID;
48 output reg [YCbCr_DW-1:0] Y_DAT;
49 output reg [YCbCr_DW-1:0] Cb_DAT;
50 output reg [YCbCr_DW-1:0] Cr_DAT;
51
52 reg [2*YCbCr_DW-1:0] RGB_R1,RGB_R2,RGB_R3;
53 reg [2*YCbCr_DW-1:0] RGB_G1,RGB_G2,RGB_G3;
54 reg [2*YCbCr_DW-1:0] RGB_B1,RGB_B2,RGB_B3;
55
56 reg [2*YCbCr_DW-1:0] IMG_Y,IMG_Cb,IMG_Cr;
57
58 reg [2:0] VSYNC_R;
59 reg [2:0] DVALID_R;
60
61 //Step1: Consume 1Clk
62 always@(posedge RGB_CLK or negedge RESET)begin
63 if(!RESET)begin
64 RGB_R1 <= {2*YCbCr_DW{1'b0}};
65 RGB_R2 <= {2*YCbCr_DW{1'b0}};
66 RGB_R3 <= {2*YCbCr_DW{1'b0}};
67 RGB_G1 <= {2*YCbCr_DW{1'b0}};
68 RGB_G2 <= {2*YCbCr_DW{1'b0}};
69 RGB_G3 <= {2*YCbCr_DW{1'b0}};
70 RGB_B1 <= {2*YCbCr_DW{1'b0}};
71 RGB_B2 <= {2*YCbCr_DW{1'b0}};
72 RGB_B3 <= {2*YCbCr_DW{1'b0}};
73 end
74 else begin
75 RGB_R1 <= RGB_DAT[23:16] * 8'd77;
76 RGB_G1 <= RGB_DAT[15:8] * 8'd150;
77 RGB_B1 <= RGB_DAT[7:0] * 8'd29;
78 RGB_R2 <= RGB_DAT[23:16] * 8'd43;
79 RGB_G2 <= RGB_DAT[15:8] * 8'd85;
80 RGB_B2 <= RGB_DAT[7:0] * 8'd128;
81 RGB_R3 <= RGB_DAT[23:16] * 8'd128;
82 RGB_G3 <= RGB_DAT[15:8] * 8'd107;
83 RGB_B3 <= RGB_DAT[7:0] * 8'd21;
84 end
85 end
86
87 //Step2: Consume 1Clk
88 always@(posedge RGB_CLK or negedge RESET)begin
89 if(!RESET)begin
90 IMG_Y <= {2*YCbCr_DW{1'b0}};
91 IMG_Cr <= {2*YCbCr_DW{1'b0}};
92 IMG_Cb <= {2*YCbCr_DW{1'b0}};
93 end
94 else begin
95 IMG_Y <= RGB_R1 + RGB_G1 + RGB_B1;
96 IMG_Cb <= RGB_B2 - RGB_R2 - RGB_G2 + 16'd32768;
97 IMG_Cr <= RGB_R3 - RGB_G3 - RGB_B3 + 16'd32768;
98 end
99 end
100
101 //Step3: Consume 1Clk
102 always@(posedge RGB_CLK or negedge RESET)begin
103 if(!RESET)begin
104 Y_DAT <= {YCbCr_DW{1'b0}};
105 Cb_DAT <= {YCbCr_DW{1'b0}};
106 Cr_DAT <= {YCbCr_DW{1'b0}};
107 end
108 else begin
109 Y_DAT <= IMG_Y[15:8];
110 Cr_DAT <= IMG_Cr[15:8];
111 Cb_DAT <= IMG_Cb[15:8];
112 end
113 end
114
115 assign YCbCr_CLK = RGB_CLK;
116
117 always@(posedge RGB_CLK or negedge RESET)begin
118 if(!RESET)begin
119 VSYNC_R <= 4'd0;
120 DVALID_R <= 4'd0;
121 end
122 else begin
123 VSYNC_R <= {VSYNC_R[1:0],RGB_VSYNC};
124 DVALID_R <= {DVALID_R[1:0],RGB_DVALID};
125 end
126 end
127
128 assign YCbCr_DVALID = DVALID_R[2];
129 assign YCbCr_VSYNC = VSYNC_R[2];
130
131
132 endmodule
(4)顶层文件 rgb2gray.v;
1 //===============================================================================================//
2 //FileName: rgb2gray.v
3 //Date:2020-02-28
4 //===============================================================================================//
5
6 `timescale 1ps/1ps
7
8 module rgb2gray(
9 RSTn, //全局复位
10 CLOCK, //系统时钟
11
12 IMG_CLK, //像素时钟
13 IMG_DVD, //像素值
14 IMG_DVSYN, //输入场信号
15 IMG_DHSYN, //输入数据有效信号
16
17 GRAY_CLK, //输出灰度图像时钟
18 GRAY_VSYNC, //输出灰度图像场信号
19 GRAY_DVALID, //输出灰度图像数据有效信号
20 Y_DAT, //输出图像数据Y分量
21 Cb_DAT, //输出图像数据Cb分量
22 Cr_DAT //输出图像数据Cr分量
23
24 );
25 /*image parameter*/
26 parameter iw = 640; //image width
27 parameter ih = 512; //image height
28 parameter trig_value = 400; //250
29
30 /*data width*/
31 parameter dvd_dw = 8; //image source data width
32 parameter dvd_chn = 3; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
33 parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width
34 parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width
35
36 //Port Declared
37 input RSTn;
38 input CLOCK;
39 input IMG_CLK;
40 input [dvd_dw-1:0] IMG_DVD;
41 input IMG_DVSYN;
42 input IMG_DHSYN;
43
44 output GRAY_CLK;
45 output GRAY_VSYNC;
46 output GRAY_DVALID;
47 output [dvd_dw-1:0] Y_DAT;
48 output [dvd_dw-1:0] Cb_DAT;
49 output [dvd_dw-1:0] Cr_DAT;
50
51 //Variable Declared
52 wire [local_dw-1:0] RGB_DAT;
53 wire RGB_DVALID;
54 wire RGB_VSYNC;
55
56 video_cap u1(
57 .reset_l(RSTn), //异步复位信号
58 .DVD(IMG_DVD), //输入视频流
59 .DVSYN(IMG_DVSYN), //输入场同步信号
60 .DHSYN(IMG_DHSYN), //输入行同步
61 .DVCLK(IMG_CLK), //输入DV时钟
62 .cap_dat(RGB_DAT), //输出RGB通道像素流,24位
63 .cap_dvalid(RGB_DVALID), //输出数据有效
64 .cap_vsync(RGB_VSYNC), //输出场同步
65 .cap_clk(CLOCK), //本地逻辑时钟
66 .img_en(),
67 .cmd_rdy(), //命令行准备好,代表可以读取
68 .cmd_rdat(), //命令行数据输出
69 .cmd_rdreq() //命令行读取请求
70 );
71
72 defparam u1.DW_DVD = dvd_dw;
73 defparam u1.DW_LOCAL = local_dw;
74 defparam u1.DW_CMD = cmd_dw;
75 defparam u1.DVD_CHN = dvd_chn;
76 defparam u1.TRIG_VALUE = trig_value;
77 defparam u1.IW = iw;
78 defparam u1.IH = ih;
79
80 RGB2YCrCb u2(
81 .RESET(RSTn), //异步复位信号
82
83 .RGB_CLK(CLOCK), //输入像素时钟
84 .RGB_VSYNC(RGB_VSYNC), //输入场同步信号
85 .RGB_DVALID(RGB_DVALID), //输入数据有信号
86 .RGB_DAT(RGB_DAT), //输入RGB通道像素流,24位
87
88 .YCbCr_CLK(GRAY_CLK), //输出像素时钟
89 .YCbCr_VSYNC(GRAY_VSYNC), //输出场同步信号
90 .YCbCr_DVALID(GRAY_DVALID), //输出数据有效信号
91 .Y_DAT(Y_DAT), //输出Y分量
92 .Cb_DAT(Cb_DAT), //输出Cb分量
93 .Cr_DAT(Cr_DAT) //输出Cr分量
94 );
95
96 defparam u2.RGB_DW = local_dw;
97 defparam u2.YCbCr_DW = dvd_dw;
98
99 endmodule
(5)Maltab文件用显示仿真结果;
1 clc;
2 clear;
3
4 %% 数据获取
5 RGBImg = imread('lena_512x512.jpg'); %rgb原始图像
6 RGBImg = imresize(RGBImg,[512 640]);
7
8 GRAYImg = rgb2gray(RGBImg); %Matlab变换灰度图像
9
10 fid = fopen('gray_image_Y.txt','r'); %FPGA转换灰度图像
11 data = fscanf(fid,'%2x');
12 data = uint8(data);
13 gray_data = reshape(data,640,512);
14 gray_data = gray_data';
15
16 %% 画图显示
17 figure(1);
18 subplot(1,3,1);
19 imshow(RGBImg);
20 title('lena原始图像');
21
22 subplot(1,3,2);
23 imshow(GRAYImg);
24 title('Matlab变换灰度图像');
25
26 subplot(1,3,3);
27 imshow(gray_data);
28 title('FPGA变换灰度图像');
(6)用于Modelsim测试的Testbench文件rgb2gray_tb.v;
1 `timescale 1ps/1ps
2
3 module rgb2gray_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 /*test module enable*/
27 parameter cap_en = 1;
28
29 /*signal group*/
30 reg clk = 1'b0;
31 reg reset_l;
32 reg [3:0] src_sel;
33
34
35 /*input dv group*/
36 wire dv_clk;
37 wire dvsyn;
38 wire dhsyn;
39 wire [dvd_dw-1:0] dvd;
40
41 /*dvd source data generated for simulation*/
42 image_src //#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b)
43 u1(
44 .clk(clk),
45 .reset_l(reset_l),
46 .src_sel(src_sel),
47 .test_data(dvd),
48 .test_dvalid(dhsyn),
49 .test_vsync(dvsyn),
50 .clk_out(dv_clk)
51 );
52
53 defparam u1.iw = iw*dvd_chn;
54 defparam u1.ih = ih + 1;
55 defparam u1.dw = dvd_dw;
56 defparam u1.h_total = h_total;
57 defparam u1.v_total = v_total;
58 defparam u1.sync_b = sync_b;
59 defparam u1.sync_e = sync_e;
60 defparam u1.vld_b = vld_b;
61
62
63 /*local clk: also clk of all local modules*/
64 reg cap_clk = 1'b0;
65
66 /*output data*/
67 wire GRAY_CLK;
68 wire GRAY_VSYNC;
69 wire GRAY_DVALID;
70 wire [dvd_dw-1:0] Y_DAT;
71 wire [dvd_dw-1:0] Cb_DAT;
72 wire [dvd_dw-1:0] Cr_DAT;
73
74 /*video capture: capture image src and transfer it into local timing*/
75
76 rgb2gray u2(
77 .RSTn(reset_l),
78 .CLOCK(cap_clk),
79
80 .IMG_CLK(dv_clk),
81 .IMG_DVD(dvd),
82 .IMG_DVSYN(dvsyn),
83 .IMG_DHSYN(dhsyn),
84
85 .GRAY_CLK(GRAY_CLK),
86 .GRAY_VSYNC(GRAY_VSYNC),
87 .GRAY_DVALID(GRAY_DVALID),
88 .Y_DAT(Y_DAT),
89 .Cb_DAT(Cb_DAT),
90 .Cr_DAT(Cr_DAT)
91 );
92
93 initial
94 begin: init
95 reset_l <= 1'b1;
96 src_sel <= 4'b0000;
97 #(100); //reset the system
98 reset_l <= 1'b0;
99 #(100);
100 reset_l <= 1'b1;
101 end
102
103 //dv_clk generate
104 always@(reset_l or clk)begin
105 if((~(reset_l)) == 1'b1)
106 clk <= 1'b0;
107 else
108 begin
109 if(clk_freq == 48) //48MHz
110 clk <= #10417 (~(clk));
111
112 else if(clk_freq == 51.84) //51.84MHz
113 clk <= #9645 (~(clk));
114
115 else if(clk_freq == 72) //72MHz
116 clk <= #6944 (~(clk));
117 end
118 end
119
120 //cap_clk generate: 25MHz
121 always@(reset_l or cap_clk)begin
122 if((~(reset_l)) == 1'b1)
123 cap_clk <= 1'b0;
124 else
125 cap_clk <= #20000 (~(cap_clk));
126 end
127
128 generate
129 if(cap_en != 0) begin :capture_operation
130 integer fid1, fid2, fid3, cnt_cap=0;
131
132 always@(posedge GRAY_CLK or posedge GRAY_VSYNC)begin
133 if(((~(GRAY_VSYNC))) == 1'b0)
134 cnt_cap = 0;
135 else
136 begin
137 if(GRAY_DVALID == 1'b1)
138 begin
139 //Y
140 fid1 = $fopen("E:/Modelsim/rgb2gray/sim/gray_image_Y.txt","r+");
141 $fseek(fid1,cnt_cap,0);
142 $fdisplay(fid1,"%02x\n",Y_DAT);
143 $fclose(fid1);
144
145 //Cb
146 fid2 = $fopen("E:/Modelsim/rgb2gray/sim/gray_image_Cb.txt","r+");
147 $fseek(fid2,cnt_cap,0);
148 $fdisplay(fid2,"%02x\n",Cb_DAT);
149 $fclose(fid2);
150
151 //Cr
152 fid3 = $fopen("E:/Modelsim/rgb2gray/sim/gray_image_Cr.txt","r+");
153 $fseek(fid3,cnt_cap,0);
154 $fdisplay(fid3,"%02x\n",Cr_DAT);
155 $fclose(fid3);
156
157 cnt_cap<=cnt_cap+4;
158 end
159 end
160 end
161 end
162 endgenerate
163
164 endmodule
165
(7) 用于Modelsim仿真的.do文件rgb2gray.do。
1 #切换至工程目录
2 cd E:/Modelsim/rgb2gray/sim
3
4 #打开工程
5 project open E:/Modelsim/rgb2gray/sim/rgb2gray
6
7 #添加指定设计文件
8 project addfile E:/Modelsim/rgb2gray/src/cross_clock_fifo.v
9 project addfile E:/Modelsim/rgb2gray/src/image_src.v
10 project addfile E:/Modelsim/rgb2gray/src/line_buffer_new.v
11 project addfile E:/Modelsim/rgb2gray/src/rgb2gray.v
12 project addfile E:/Modelsim/rgb2gray/src/RGB2YCbCr.v
13 project addfile E:/Modelsim/rgb2gray/src/video_cap.v
14 project addfile E:/Modelsim/rgb2gray/sim/rgb2gray_tb.v
15
16 #编译工程内所有文件
17 project compileall
18
19 #仿真work库下面的rgb2gray_tb实例,同时调用altera_lib库,不进行任何优化
20 vsim -t 1ps -novopt -L altera_lib work.rgb2gray_tb
21
22 #添加输入信号
23 add wave -divider RGBImg
24 add wave -radix binary -position insertpoint sim:/rgb2gray_tb/dv_clk
25 add wave -radix binary -position insertpoint sim:/rgb2gray_tb/dvsyn
26 add wave -radix binary -position insertpoint sim:/rgb2gray_tb/dhsyn
27 add wave -radix hex -position insertpoint sim:/rgb2gray_tb/dvd
28
29 #添加输出信号
30 add wave -divider GRAYImg
31 add wave -radix binary -position insertpoint sim:/rgb2gray_tb/GRAY_CLK
32 add wave -radix binary -position insertpoint sim:/rgb2gray_tb/GRAY_VSYNC
33 add wave -radix binary -position insertpoint sim:/rgb2gray_tb/GRAY_DVALID
34 add wave -radix hex -position insertpoint sim:/rgb2gray_tb/Y_DAT
35 add wave -radix hex -position insertpoint sim:/rgb2gray_tb/Cb_DAT
36 add wave -radix hex -position insertpoint sim:/rgb2gray_tb/Cr_DAT
37
38 #复位
39 restart
40
41 #取消警告
42 set StdArithNoWarnings 1
43
44 #开始
45 run 17ms
四、仿真结果
如下图所示,整个转换消耗3个时钟,因此相应的行/场信号延迟3个时钟,保持时钟的同步性。
如下图所示,将FPGA运算处理结果与Matlab自带rgb2gray函数处理结果对比如下。
来源:oschina
链接:https://my.oschina.net/u/4362791/blog/4297835