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函数处理结果对比如下。


      

    初入江湖,天下无敌;再学三年,寸步难行。
  • 相关阅读:
    网站安全编程 黑客入侵 脚本黑客 高级语法入侵 C/C++ C# PHP JSP 编程
    【算法导论】贪心算法,递归算法,动态规划算法总结
    cocoa2dx tiled map添加tile翻转功能
    8月30日上海ORACLE大会演讲PPT下载
    【算法导论】双调欧几里得旅行商问题
    Codeforces Round #501 (Div. 3) B. Obtaining the String (思维,字符串)
    Codeforces Round #498 (Div. 3) D. Two Strings Swaps (思维)
    Educational Codeforces Round 89 (Rated for Div. 2) B. Shuffle (数学,区间)
    洛谷 P1379 八数码难题 (BFS)
    Educational Codeforces Round 89 (Rated for Div. 2) A. Shovels and Swords (贪心)
  • 原文地址:https://www.cnblogs.com/huangwei0521/p/12382238.html
Copyright © 2011-2022 走看看