图像处理边缘检测算法————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效果实现如下:

 

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

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Canny边缘检测算法图像处理领域非常常用,它能够在图像中准确地检测出边缘,是数字图像处理中最流行的算法之一。下面是一个简单的Verilog实现,可以实现Canny算法边缘检测。 首先,我们需要定义一些参数,如图像大小、高斯滤波器的大小、梯度阈值等。然后,我们需要对原始图像进行高斯滤波,以平滑图像并减少噪声。接下来,我们计算每个像素的梯度,以及每个像素的方向。然后,我们将梯度值与梯度阈值进行比较,并将具有较高梯度值的像素标记为边缘像素。最后,我们使用非极大值抑制算法,以滤除非极大值像素。 下面是一个简单的Canny边缘检测的Verilog实现: ``` module canny_edge_detection(input clk, input reset, input [7:0] image_in, output reg [7:0] edge_out); // define image size and filter size parameter WIDTH = 640; parameter HEIGHT = 480; parameter KERNEL_SIZE = 5; // define threshold values parameter LOW_THRESHOLD = 20; parameter HIGH_THRESHOLD = 50; reg [7:0] image_data [0:WIDTH-1][0:HEIGHT-1]; reg [7:0] smoothed_image [0:WIDTH-1][0:HEIGHT-1]; reg [7:0] gradient_map [0:WIDTH-1][0:HEIGHT-1]; reg [7:0] gradient_direction [0:WIDTH-1][0:HEIGHT-1]; reg [7:0] edge_map [0:WIDTH-1][0:HEIGHT-1]; // define Gaussian kernel reg [7:0] kernel [0:KERNEL_SIZE-1]; initial begin kernel[0] = 1; kernel[KERNEL_SIZE-1] = 1; for (int i=1; i<KERNEL_SIZE-1; i=i+1) begin kernel[i] = 2; end end // apply Gaussian filter to input image always @(posedge clk) begin if (reset) begin for (int i=0; i<WIDTH; i=i+1) begin for (int j=0; j<HEIGHT; j=j+1) begin image_data[i][j] = 0; smoothed_image[i][j] = 0; gradient_map[i][j] = 0; gradient_direction[i][j] = 0; edge_map[i][j] = 0; end end end else begin for (int i=0; i<WIDTH; i=i+1) begin for (int j=0; j<HEIGHT; j=j+1) begin image_data[i][j] = image_in; smoothed_image[i][j] = 0; gradient_map[i][j] = 0; gradient_direction[i][j] = 0; edge_map[i][j] = 0; end end for (int i=KERNEL_SIZE/2; i<WIDTH-KERNEL_SIZE/2; i=i+1) begin for (int j=KERNEL_SIZE/2; j<HEIGHT-KERNEL_SIZE/2; j=j+1) begin reg [15:0] sum = 0; for (int k=0; k<KERNEL_SIZE; k=k+1) begin for (int l=0; l<KERNEL_SIZE; l=l+1) begin sum = sum + image_data[i-k+KERNEL_SIZE/2][j-l+KERNEL_SIZE/2] * kernel[k] * kernel[l]; end end smoothed_image[i][j] = sum >> (KERNEL_SIZE*2-1); end end end end // calculate gradient and direction always @(posedge clk) begin if (reset) begin for (int i=0; i<WIDTH; i=i+1) begin for (int j=0; j<HEIGHT; j=j+1) begin gradient_map[i][j] = 0; gradient_direction[i][j] = 0; end end end else begin for (int i=1; i<WIDTH-1; i=i+1) begin for (int j=1; j<HEIGHT-1; j=j+1) begin reg [15:0] gx = smoothed_image[i+1][j-1] + 2*smoothed_image[i+1][j] + smoothed_image[i+1][j+1] - smoothed_image[i-1][j-1] - 2*smoothed_image[i-1][j] - smoothed_image[i-1][j+1]; reg [15:0] gy = smoothed_image[i-1][j+1] + 2*smoothed_image[i][j+1] + smoothed_image[i+1][j+1] - smoothed_image[i-1][j-1] - 2*smoothed_image[i][j-1] - smoothed_image[i+1][j-1]; gradient_map[i][j] = $sqrt(gx*gx + gy*gy); gradient_direction[i][j] = $atan2(gy, gx); end end end end // threshold gradient map and mark edge pixels always @(posedge clk) begin if (reset) begin for (int i=0; i<WIDTH; i=i+1) begin for (int j=0; j<HEIGHT; j=j+1) begin edge_map[i][j] = 0; end end end else begin for (int i=1; i<WIDTH-1; i=i+1) begin for (int j=1; j<HEIGHT-1; j=j+1) begin if (gradient_map[i][j] > HIGH_THRESHOLD) begin edge_map[i][j] = 1; end else if (gradient_map[i][j] < LOW_THRESHOLD) begin edge_map[i][j] = 0; end else begin edge_map[i][j] = -1; end end end for (int i=1; i<WIDTH-1; i=i+1) begin for (int j=1; j<HEIGHT-1; j=j+1) begin if (edge_map[i][j] == 1) begin if ((gradient_direction[i][j] > -22.5 && gradient_direction[i][j] <= 22.5) || (gradient_direction[i][j] > 157.5 && gradient_direction[i][j] <= -157.5)) begin if (gradient_map[i][j] > gradient_map[i][j+1] && gradient_map[i][j] > gradient_map[i][j-1]) begin edge_map[i][j] = 1; end else begin edge_map[i][j] = 0; end end else if ((gradient_direction[i][j] > 22.5 && gradient_direction[i][j] <= 67.5) || (gradient_direction[i][j] > -157.5 && gradient_direction[i][j] <= -112.5)) begin if (gradient_map[i][j] > gradient_map[i+1][j+1] && gradient_map[i][j] > gradient_map[i-1][j-1]) begin edge_map[i][j] = 1; end else begin edge_map[i][j] = 0; end end else if ((gradient_direction[i][j] > 67.5 && gradient_direction[i][j] <= 112.5) || (gradient_direction[i][j] > -112.5 && gradient_direction[i][j] <= -67.5)) begin if (gradient_map[i][j] > gradient_map[i+1][j] && gradient_map[i][j] > gradient_map[i-1][j]) begin edge_map[i][j] = 1; end else begin edge_map[i][j] = 0; end end else if ((gradient_direction[i][j] > 112.5 && gradient_direction[i][j] <= 157.5) || (gradient_direction[i][j] > -67.5 && gradient_direction[i][j] <= -22.5)) begin if (gradient_map[i][j] > gradient_map[i+1][j-1] && gradient_map[i][j] > gradient_map[i-1][j+1]) begin edge_map[i][j] = 1; end else begin edge_map[i][j] = 0; end end end end end end end // output edge map always @(posedge clk) begin if (reset) begin edge_out = 0; end else begin edge_out = edge_map[i][j]; end end endmodule ``` 注意,这只是一个简单的实现,可能需要进行优化以提高性能和准确性。此外,由于边缘检测算法本身的复杂性,可能需要使用更高级的工具来实现更复杂的算法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值