基於FPGA的RGB影象轉灰度影象演算法實現
一、前言
最近學習牟新剛編著《基於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函式處理結果對比如下。
&n