基于FPGA的RGB图像转灰度图像算法实现

2019/04/10 10:10
阅读数 39

一、前言

  最近学习牟新刚编著《基于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函数处理结果对比如下。

 

 

 

 

 

 

 

 


  

 

原文出处:https://www.cnblogs.com/huangwei0521/p/12382238.html

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部