图像处理边缘检测算法————sobel算法

一、了解边缘检测

边缘检测的目的是标识数字图像中亮度变化明显的点。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。常用的边缘检测模板有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效果实现如下:

 

 非原创,学习过程记录,摘自小梅哥学习文档。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年12月27日
下一篇 2023年12月27日

相关推荐