图像处理边缘检测算法————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
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于自实现opencv图像处理函数的静态车道线检测项目源码.zip # **基于自实现opencv图像处理函数的静态车道线检测项目** <!-- TOC --> - [**基于自实现opencv图像处理函数的静态车道线检测项目**](#基于自实现opencv图像处理函数的静态车道线检测项目) - [**1.项目背景**](#1项目背景) - [**2.实现**](#2实现) - [**2.1 基本思路: 去噪,边缘提取,车道检测**](#21-基本思路-去噪边缘提取车道检测) - [**2.2 实现过程**](#22-实现过程) - [**3. 结果分析**](#3-结果分析) - [**4. 后续**](#4-后续) <!-- /TOC --> ## **1.项目背景** **起源于选修邸慧军老师的《数字图像处理》这门课程的最终课程设计项目 :实现车道线检测** 1. 不能用神经网络(老师的原话是直接喂给神经网络就没有意思了),只能用数字图像处理方法 2. 除了读取与写入之外不能使用其他现成库函数,比如大名鼎鼎的opencv 3. 其实本来计划是MATLAB(教学也是MATLAB),后来我校突然就进美国名单里MATLAB用不了就改c++了 **数据集** 数据集由720*1280的jpg格式图片组成,实际上就是开车在公路沿途拍下来的,因为我发现数据集的图片之间很有连贯性。其中一张如图所示: ![avatar](./车道线检测/source/20.jpg) **环境** 我是win10 + vs2019。记得配置opencv,用来读取图片。 ## **2.实现** ### **2.1 基本思路: 去噪,边缘提取,车道检测** 但在刚刚开始的时候无从下手,因为选择太多了。 比如说去噪音可以高斯滤波,均值滤波,中值滤波。 边缘提取你又可以拉普拉斯锐化,sobel算子,Robert算子,scharr算子,candy检测。 于是我干脆把这些实现一个遍,再慢慢选择好了。 于是自实现了以下算法: 1. 彩色图像转灰度 2. 图像翻转,获得负片 3. 线性灰度转换 4. 对数灰度转换 5. 指数灰度转换 6. 方框滤波 7. 均值滤波 8. 高斯滤波 9. 中值滤波 10. 最值滤波 11. 拉普拉斯锐化 12. 漫水填充 13. 阈值分割 14. 自适应阈值分割 15. candy边缘检测 16. scharr边缘检测 17. sobel边缘检测 18. Robert边缘检测 19. 霍夫直线检测 20. 直方图均衡化 **所有算法源码均定义位于minicv.h下,最下面有接口参数说明;所有实现位于minicv.cpp下,关键步骤有注释** ### **2.2 实现过程** 0. **以此图片为实例** ![avatar](./车道线检测/source/20.jpg) 1. **转灰度图**,公式是GRAY = B * 0.114 + G * 0.587 + R * 0.299。目的是为了减少运算量,简化操作。 ```c++ _getGray(img) //有些函数会与opencv重名,所以我自实现的都带下划线。其实也可以自定义namespace ``` 效果: ![avatar](./车道线检测/source/21.png) 2. **均值滤波去噪音**。 实际上我认为施加平滑滤波的主要原因不是为了去除噪音,因为对于斑点噪音,实际上后面的霍夫变换基本上会忽略。 我认为施加滤波之后图像边模糊了(废话)——图片的边缘变宽了,一些细线变粗相当有利于后面的边缘检测。 另外,试了高斯滤波与均值滤波,效果相当,就选简单的均值滤波吧 施加均值滤波后效果如下 ```c++ _blur(img, 3, 3); //大小是 3*3 ``` ![avatar](./车道线检测/source/22.png) ​ 3. **幂指变换**。 本来是没有这一步的,但是到最后发现没有这一步对比较暗的图片实现效果比较差,因为后面有一步数阈值分割。 **我把这一步看作这个算法成功的关键之一(另

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值