基于FPGA的RGB图像转灰度图像算法实现
TheSkyIsMine 人气:0一、前言
最近学习牟新刚编著《基于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 /https://img.qb5200.com/download-x/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_tbhttps://img.qb5200.com/download-x/dv_clk 25 add wave -radix binary -position insertpoint sim:/rgb2gray_tbhttps://img.qb5200.com/download-x/dvsyn 26 add wave -radix binary -position insertpoint sim:/rgb2gray_tbhttps://img.qb5200.com/download-x/dhsyn 27 add wave -radix hex -position insertpoint sim:/rgb2gray_tbhttps://img.qb5200.com/download-x/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函数处理结果对比如下。
加载全部内容