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