1. 程式人生 > >基於FPGA的RGB影象轉灰度影象演算法實現

基於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