一、了解边缘检测
边缘检测的目的是标识数字图像中亮度变化明显的点。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。常用的边缘检测模板有Laplacian算子、Roberts算子、Sobel算子、log(Laplacian-Gauss)算子、Kirsch算子和Prewitt算子等。
Sobel算子是常用的边缘检测模板,算法比较简单,实际应用中效率比 canny 边缘检测效率要高,但是边缘不如 Canny 检测的准确.
边缘检测效果:
二、边缘检测原理
Sobel 边缘检测提供了x水平和y垂直两个方向的滤波模板 ,可以只检测竖直边缘或垂直边缘或都检测。实现步骤:
1.采用上面的模板对原图像A 进行卷积。得到 Gx 为水平横向梯度幅值,Gy为垂直方向梯度幅值。(梯度:灰度值的变化情况,梯度幅值相当于2个相邻像素灰度值之间的差异。)
2. 由横向灰度值 Gx 和纵向灰度值 Gy,计算该点的灰度值:
FPGA是不擅长做平方和开根运算的,可以采用近似计算方法:
梯度方向的计算:
3. 设置一个阈值 threshold,对数据进行比较然后输出二值图像
三、模块设计
四、verilog代码实现
端口定义:
module sobel
(
input clk, //pixel clk
input reset_p,
input [7:0] data_in,
input data_in_valid,
input data_in_hs,
input data_in_vs,
input [7:0] threshold,
output reg data_out,
output reg data_out_valid,
output reg data_out_hs,
output reg data_out_vs
);
首先产生3*3的模板:
reg [DATA_WIDTH-1:0] row0_col0;
reg [DATA_WIDTH-1:0] row0_col1;
reg [DATA_WIDTH-1:0] row0_col2;
reg [DATA_WIDTH-1:0] row1_col0;
reg [DATA_WIDTH-1:0] row1_col1;
reg [DATA_WIDTH-1:0] row1_col2;
reg [DATA_WIDTH-1:0] row2_col0;
reg [DATA_WIDTH-1:0] row2_col1;
reg [DATA_WIDTH-1:0] row2_col2;
调用IP核,存储两行数据
//line data
wire [DATA_WIDTH-1:0] line0_data;
wire [DATA_WIDTH-1:0] line1_data;
wire [DATA_WIDTH-1:0] line2_data;
//3xline data
shift_register_2taps
#(
.DATA_WIDTH ( DATA_WIDTH )
)shift_register_2taps(
.clk (clk ),
.shiftin (data_in ),
.shiftin_valid (data_in_valid ),
.shiftout ( ),
.taps0x (line0_data ),
.taps1x (line1_data )
);
assign line2_data = data_in;
3*3模板的9个数:
//----------------------------------------------------
// 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 reset_p) begin
if(reset_p) 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)
if(data_in_valid) 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
else begin
row0_col2 <= row0_col2;
row0_col1 <= row0_col1;
row0_col0 <= row0_col0;
row1_col2 <= row1_col2;
row1_col1 <= row1_col1;
row1_col0 <= row1_col0;
row2_col2 <= row2_col2;
row2_col1 <= row2_col1;
row2_col0 <= row2_col0;
end
else 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
end
对输入的行场同步信号打拍,方便将图像处理后的信号打拍输出
//
reg data_in_valid_dly1;
reg data_in_valid_dly2;
reg data_in_hs_dly1;
reg data_in_hs_dly2;
reg data_in_vs_dly1;
reg data_in_vs_dly2;
always @(posedge clk)
begin
data_in_valid_dly1 <= data_in_valid;
data_in_valid_dly2 <= data_in_valid_dly1;
data_in_hs_dly1 <= data_in_hs;
data_in_hs_dly2 <= data_in_hs_dly1;
data_in_vs_dly1 <= data_in_vs;
data_in_vs_dly2 <= data_in_vs_dly1;
end
计算 Gx 为水平横向梯度幅值(左右两列求和),Gy为垂直方向梯度幅值(上下两行求和)的绝对值,因此先判断 Gx和Gy为正的情况,正负数的绝对值去绝对值的结果不一样。
wire Gx_is_positive;
wire Gy_is_positive;
//----------------------------------------------------
// mask x mask y
//[-1,0,1] [ 1, 2, 1]
//[-2,0,2] [ 0, 0, 0]
//[-1,0,1] [-1,-2,-1]
//----------------------------------------------------
assign Gx_is_positive = (row0_col2 + row1_col2*2 + row2_col2) >= (row0_col0 + row1_col0*2 + row2_col0);
assign Gy_is_positive = (row0_col0 + row0_col1*2 + row0_col2) >= (row2_col0 + row2_col1*2 + row2_col2);
求Gx、Gy的绝对值:
always @(posedge clk or posedge reset_p) begin
if(reset_p)
Gx_absolute <= 'd0;
else if(data_in_valid_dly1) begin
if(Gx_is_positive)
Gx_absolute <= (row0_col2 + row1_col2*2 + row2_col2) - (row0_col0 + row1_col0*2 + row2_col0);
else
Gx_absolute <= (row0_col0 + row1_col0*2 + row2_col0) - (row0_col2 + row1_col2*2 + row2_col2);
end
end
always @(posedge clk or posedge reset_p) begin
if(reset_p)
Gy_absolute <= 'd0;
else if(data_in_valid_dly1) begin
if(Gy_is_positive)
Gy_absolute <= (row0_col0 + row0_col1*2 + row0_col2) - (row2_col0 + row2_col1*2 + row2_col2);
else
Gy_absolute <= (row2_col0 + row2_col1*2 + row2_col2) - (row0_col0 + row0_col1*2 + row0_col2);
end
end
将Gx、Gy的绝对值相加,但是需要设置一个阈值 threshold,对数据进行比较然后输出二值图像:
//----------------------------------------------------
//result
//----------------------------------------------------
always @(posedge clk or posedge reset_p) begin
if(reset_p)
data_out <= 1'b0;
else if(data_in_valid_dly2) begin
data_out <= ((Gx_absolute+Gy_absolute)>threshold) ? 1'b0 : 1'b1;
end
end
将图像经过灰度处理后,再进行sobel算法提取边缘,FPGA效果实现如下:
非原创,学习过程记录,摘自小梅哥学习文档。
文章出处登录后可见!
已经登录?立即刷新