zoukankan      html  css  js  c++  java
  • 基于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
    ",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
    ",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
    ",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
    ",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函数处理结果对比如下。


      

    初入江湖,天下无敌;再学三年,寸步难行。
  • 相关阅读:
    【洛谷6776】[NOI2020] 超现实树(思维)
    【洛谷6773】[NOI2020] 命运(线段树合并优化DP)
    【洛谷5467】[PKUSC2018] PKUSC(计算几何)
    【洛谷3688】[ZJOI2017] 树状数组(二维线段树)
    【BZOJ4543】[POI2014] Hotel加强版(长链剖分优化DP)
    【洛谷5466】[PKUSC2018] 神仙的游戏(FFT)
    【BZOJ4574】[ZJOI2016] 线段树(动态规划)
    【洛谷7114】[NOIP2020] 字符串匹配(Z函数)
    扩展 KMP(Z 函数)学习笔记
    最后的胡言乱语
  • 原文地址:https://www.cnblogs.com/huangwei0521/p/12382238.html
Copyright © 2011-2022 走看看