FPGA——RGB转灰度图像,sobel边沿检测实现及验证

一、设计思路

采用加权平均值法

二、RGB转灰度图像代码

//RGB转灰度图像,延迟两拍输出
module rgb2gray(
   clk         ,     //时钟
   rst_n       ,     //复位
   rgb_vld     ,     //rgb输入有效标识
   rgb_hs      ,     //rgb输入行信号
   rgb_vs      ,     //rgb输入场信号
   red_8b_i    ,     //R输入
   green_8b_i  ,     //G输入
   blue_8b_i   ,     //B输入

   gray_8b_o   ,     //GRAY输出
   gray_vld    ,     //gray输出有效标识
   gray_hs     ,     //gray输出行信号
   gray_vs           //gray输出场信号
);
localparam PIX_W  =   8;
localparam GRAY_W =   16;

input                   clk;      
input                   rst_n;    
input                   rgb_vld;
input                   rgb_hs;
input                   rgb_vs;
input     [PIX_W-1:0]   red_8b_i;  
input     [PIX_W-1:0]   green_8b_i;
input     [PIX_W-1:0]   blue_8b_i; 

output    [PIX_W-1:0]   gray_8b_o;
output                  gray_vld;
output                  gray_hs;
output                  gray_vs;

reg       [PIX_W-1:0]   gray_8b_o;
wire                    gray_vld;
wire                    gray_hs;
wire                    gray_vs;

reg      [2:0]          gray_vld_tmp;
reg      [2:0]          gray_hs_tmp;
reg      [2:0]          gray_vs_tmp;

//典型灰度转换公式Gray = R*0.299+G*0.587+B*0.114=(R*77 + G*150 + B*29) >>8
wire      [GRAY_W-1:0]  red_x77;
wire      [GRAY_W-1:0]  green_x150;
wire      [GRAY_W-1:0]  blue_x29;
reg       [GRAY_W-1:0]  sum;

//乘法转换成移位相加方式
assign red_x77    = (red_8b_i  << 6) + (red_8b_i  << 3) + (red_8b_i  << 2) + red_8b_i;
assign green_x150 = (green_8b_i<< 7) + (green_8b_i<< 4) + (green_8b_i<< 2) + (green_8b_i<<1);
assign blue_x29   = (blue_8b_i << 4) + (blue_8b_i << 3) + (blue_8b_i << 2) + blue_8b_i;

always@(posedge clk or posedge rst_n)begin
   if(!rst_n)
      sum <= 0;
   else if(rgb_vld)
      sum <= red_x77 + green_x150 + blue_x29;
   else
      sum <= 0;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      gray_8b_o = 0;
   else
      gray_8b_o = sum[15:8];
end

always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      gray_vld_tmp   <=    0;
      gray_hs_tmp    <=    0;
      gray_vs_tmp    <=    0;
   end
   else begin
      gray_vld_tmp   <= {gray_vld_tmp[1:0],rgb_vld};
      gray_hs_tmp    <= {gray_hs_tmp[1:0],rgb_hs};
      gray_vs_tmp    <= {gray_vs_tmp[1:0],rgb_vs};
   end
end

assign gray_vld   =  gray_vld_tmp[1];
assign gray_hs    =  gray_hs_tmp[1];
assign gray_vs    =  gray_vs_tmp[1];

endmodule

三、仿真验证代码

`timescale 1ns / 1ns

module rgb2gray_tb();

localparam PIX_W  =   8;
localparam GRAY_W =   16;

reg                   clk;      
reg                   rst_n;    
reg                   rgb_vld;
reg                   rgb_hs;
reg                   rgb_vs;
reg     [PIX_W-1:0]   red_8b_i;  
reg     [PIX_W-1:0]   green_8b_i;
reg     [PIX_W-1:0]   blue_8b_i; 

wire    [PIX_W-1:0]   gray_8b_o;
wire                  gray_vld;
wire                  gray_hs;
wire                  gray_vs;

reg     [7:0]         rgb_check;
reg     [7:0]         rgb_check_pre;
reg                   er;

rgb2gray rgb2gray(
   .clk         (clk),        //时钟
   .rst_n       (rst_n),      //复位
   .rgb_vld     (rgb_vld),    //rgb输入有效标识
   .rgb_hs      (rgb_hs),     //rgb输入行信号
   .rgb_vs      (rgb_vs),     //rgb输入场信号
   .red_8b_i    (red_8b_i),   //R输入
   .green_8b_i  (green_8b_i), //G输入
   .blue_8b_i   (blue_8b_i),  //B输入

   .gray_8b_o   (gray_8b_o),  //GRAY输出
   .gray_vld    (gray_vld),   //gray输出有效标识
   .gray_hs     (gray_hs),    //gray输出行信号
   .gray_vs     (gray_vs)     //gray输出场信号
);

//时钟周期,单位ns,在这里修改时钟周期
parameter CYCLE = 20;

//复位时间,此时表示复位3个时钟周期的时间
parameter RST_TIME = 3;

//生成本地时钟50M
initial begin
   clk = 1;
   forever
   #(CYCLE/2)
   clk=~clk;
end

//产生复位信号
initial begin
   rst_n = 1;
   #2;
   rst_n = 0;
   #(CYCLE * RST_TIME);
   rst_n = 1;
end

initial begin
   #1;
   //赋初值
   rgb_vld     =  0;
   red_8b_i    =  0;
   green_8b_i  =  0;
   blue_8b_i   =  0;
   #(10*CYCLE);
   //开始赋值
   rgb_vld     =  1;
   red_8b_i    =  17;
   green_8b_i  =  85;
   blue_8b_i   =  69;
   repeat(256)begin
      #(CYCLE);
      red_8b_i    =  red_8b_i    +  1'b1;
      green_8b_i  =  green_8b_i  +  1'b1;
      blue_8b_i   =  blue_8b_i   +  1'b1;
   end
   rgb_vld     =  0;
   #(2000);
   $stop;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      rgb_check_pre <= 0;
      rgb_check <= 0;
   end
   else begin
      rgb_check_pre <= ((77*red_8b_i + 150*green_8b_i + 29*blue_8b_i) >> 8);
      rgb_check <= rgb_check_pre;
   end
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      er <= 0;
   else if(rgb_check != gray_8b_o)
      er <= 1;
   else
      er <= 0;
end

endmodule

四、sobel边沿检测

module sobel(
   clk            ,
   rst_n          ,
   data_in        ,
   data_in_vld    ,
   data_in_hs     ,
   data_in_vs     ,
   threshold      ,
   data_out       ,
   data_out_vld   ,
   data_out_hs    ,
   data_out_vs     
);

parameter DATA_W = 8;

input                      clk            ;
input                      rst_n          ;
input     [DATA_W-1:0]     data_in        ;
input                      data_in_vld    ;
input                      data_in_hs     ;
input                      data_in_vs     ;
input     [DATA_W-1:0]     threshold      ;

output                     data_out       ;
output                     data_out_vld   ;
output                     data_out_hs    ;
output                     data_out_vs    ;

reg                        data_out       ;
wire                       data_out_vld   ;
wire                       data_out_hs    ;
wire                       data_out_vs    ;

wire     [DATA_W-1:0]      line0_data;
wire     [DATA_W-1:0]      line1_data;
wire     [DATA_W-1:0]      line2_data;

reg      [DATA_W-1:0]      row0_col0;
reg      [DATA_W-1:0]      row0_col1;
reg      [DATA_W-1:0]      row0_col2;

reg      [DATA_W-1:0]      row1_col0;
reg      [DATA_W-1:0]      row1_col1;
reg      [DATA_W-1:0]      row1_col2;

reg      [DATA_W-1:0]      row2_col0;
reg      [DATA_W-1:0]      row2_col1;
reg      [DATA_W-1:0]      row2_col2;

wire                       sobelx_positive;
wire                       sobely_positive;

reg      [DATA_W+1:0]      sobelx_absolute;
reg      [DATA_W+1:0]      sobely_absolute;


reg      [2:0]             data_vld_tmp;
reg      [2:0]             data_vld_hs;
reg      [2:0]             data_vld_vs;

//3xline data
shift_register_2taps #(
   .DATA_W ( DATA_W )
)
shift_register_2taps(
   .clk           (clk        ),
   .data_in       (data_in    ),
   .shift_in_vld  (data_in_vld),
   .shiftout      (           ),
   .row1_data_out (line1_data),
   .row0_data_out (line0_data)
);

assign line2_data = data_in;

//----------------------------------------------------
// matrix 3x3 data
// row0_col0   row0_col1   row0_col2
// row1_col0   row1_col1   row1_col2
// row2_col0   row2_col1   row2_col2
//----------------------------------------------------
always @(posedge clk or posedge rst_n) begin
   if(!rst_n) begin
      row0_col0 <= 'd0;
      row0_col1 <= 'd0;
      row0_col2 <= 'd0;

      row1_col0 <= 'd0;
      row1_col1 <= 'd0;
      row1_col2 <= 'd0;

      row2_col0 <= 'd0;
      row2_col1 <= 'd0;
      row2_col2 <= 'd0;
   end
   else if(data_in_hs && data_in_vs && data_in_vld)begin
         row0_col2 <= line0_data;
         row0_col1 <= row0_col2;
         row0_col0 <= row0_col1;

         row1_col2 <= line1_data;
         row1_col1 <= row1_col2;
         row1_col0 <= row1_col1;

         row2_col2 <= line2_data;
         row2_col1 <= row2_col2;
         row2_col0 <= row2_col1;
   end
end

//----------------------------------------------------
// mask x          mask y
//[-1,0,1]       [ 1, 2, 1]
//[-2,0,2]       [ 0, 0, 0]
//[-1,0,1]       [-1,-2,-1]
//----------------------------------------------------
assign sobelx_positive = (row0_col2 + row1_col2*2 + row2_col2) >= (row0_col0 + row1_col0*2 + row2_col0);
assign sobely_positive = (row0_col0 + row0_col1*2 + row0_col2) >= (row2_col0 + row2_col1*2 + row2_col2);

always @(posedge clk or posedge rst_n) begin
   if(!rst_n)
      sobelx_absolute <= 'd0;
   else if(data_in_vld) begin
      if(sobelx_positive)
         sobelx_absolute <= (row0_col2 + row1_col2*2 + row2_col2) - (row0_col0 + row1_col0*2 + row2_col0);
   else
      sobelx_absolute <= (row0_col0 + row1_col0*2 + row2_col0) - (row0_col2 + row1_col2*2 + row2_col2);
   end
end

always @(posedge clk or posedge rst_n) begin
   if(!rst_n)
      sobely_absolute <= 'd0;
   else if(data_in_vld) begin
      if(sobely_positive)
         sobely_absolute <= (row0_col0 + row0_col1*2 + row0_col2) - (row2_col0 + row2_col1*2 + row2_col2);
      else
         sobely_absolute <= (row2_col0 + row2_col1*2 + row2_col2) - (row0_col0 + row0_col1*2 + row0_col2);
   end
end

always @(posedge clk or posedge rst_n) begin
   if(!rst_n)
      data_out <= 1'b0;
   else if(data_in_vld) begin
      data_out <= ((sobelx_absolute+sobely_absolute)>threshold) ? 1'b0 : 1'b1;
   end
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      data_vld_tmp   <=    0;
      data_vld_hs    <=    0;
      data_vld_vs    <=    0;
   end
   else begin
      data_vld_tmp   <=    {data_vld_tmp[1:0],data_in_vld};
      data_vld_hs    <=    {data_vld_hs[1:0],data_in_hs};
      data_vld_vs    <=    {data_vld_vs[1:0],data_in_vs};
   end
end

assign data_out_vld  = data_vld_tmp[2];
assign data_out_hs   = data_vld_hs[2];
assign data_out_vs   = data_vld_vs[2];

endmodule 
posted @ 2021-03-02 15:06  AdriftCore  阅读(310)  评论(0编辑  收藏  举报