ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

opencv——边缘检测算法(总结)

2021-10-18 20:02:24  阅读:204  来源: 互联网

标签:src 边缘 int 检测 图像 opencv 算法 算子


前言

别人的,觉得不错,留下来复习。

一、边缘检测算法

边缘检测算法是指利用灰度值的不连续性质,以灰度突变为基础分割出目标区域。对铝铸件表面进行成像后会产生一些带缺陷的区域,这些区域的灰度值比较低,与背景图像相比在灰度上会有突变,这是由于这些区域对光线产生散射所引起的。因此边缘检测算子可以用来对特征的提取。

1、一阶算子
一种是基于一阶微分的算子,也称基于搜索的算子,首先通过一阶导数计算边缘强度,然后采用梯度的方向来对边缘的局部方向进行寻找,同时根据该方向来寻找出局部梯度模的最大值,由此定位边缘,如Roberts Cross算子,Prewitt算子Sobel算子,Kirsch算子,Canny算子,罗盘算子等;

图像中的边缘区域,像素值会发生“跳跃”,对这些像素求导,在其一阶导数在边缘位置为极值,这就是Sobel算子使用的原理——极值处就是边缘。

2、二阶算子

另一种是基于二阶微分的算子,也称基于零交叉的算子,通过寻找由图像得到的二阶导数的过零点来定位检测边缘,如Marr-Hildreth算子,Laplacian算子,LOG算子等。如果对像素值求二阶导数,会发现边缘处的导数值为0。

二、一阶算子分析

一阶微分算子进行边缘检测的思路大致就是通过指定大小的核(kernal)(也称为算子)与图像进行卷积,将得到的梯度进行平方和或者最大值作为新的梯度赋值给对应的像素点,不同的一阶微分算子主要的不同在于其算子即核的元素不同以及核的大小不一样

以下是连续函数的一阶导数求导公式:因为图像是一个面,就相当于是灰度值关于x,y两个方向的函数,要求某一点的导数,则是各个方向的偏导数的平方和再进行开方运算。
 

离散函数的一阶导数公式:

y'=[y(x0+h)-y(x0-h)]/(2h);这是一维函数的一阶求导,h是步长,在图像处理中一般为1

首先复习一下什么是卷积?

卷积就是对应的元素相乘再进行累加的过程

实例图片: 

1、Roberts算子

Robert算子是用于求解对角线方向的梯度,因为根据算子GX和GY的元素设置可以看到,只有对角线上的元素非零,其本质就是以对角线作为差分的方向来检测。

检测垂直边缘的效果好于斜向边缘,定位精度高,对噪声敏感,无法抑制噪声的影响。  

不同方向的算子模板:                                    梯度的计算:

                    

代码示例:

#include<opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>

#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>

#include<iostream>


using namespace std;
using namespace cv;
Mat roberts(Mat srcImage);
int main(int argc, char** argv)
{
    Mat src,src_binary,src_gray;
    src = imread("D:/opencv练习图片/薛之谦.jpg");
    imshow("原图", src);
    cvtColor(src, src_gray, COLOR_BGR2GRAY);
    GaussianBlur(src_gray, src_binary, Size(3, 3),0, 0, BORDER_DEFAULT);
    Mat dstImage = roberts(src_binary);

    imshow("dstImage", dstImage);


    
    waitKey(0);
    return 0;
}
//roberts 边缘检测

Mat roberts(Mat srcImage)
{
    Mat dstImage = srcImage.clone();
    int nRows = dstImage.rows;
    int nCols = dstImage.cols;
    for (int i = 0; i < nRows - 1; i++) {
        for (int j = 0; j < nCols - 1; j++) {
            //根据公式计算
            int t1 = (srcImage.at<uchar>(i, j) -
                srcImage.at<uchar>(i + 1, j + 1))*
                (srcImage.at<uchar>(i, j) -
                    srcImage.at<uchar>(i + 1, j + 1));
            int t2 = (srcImage.at<uchar>(i + 1, j) -
                srcImage.at<uchar>(i, j + 1))*
                (srcImage.at<uchar>(i + 1, j) -
                    srcImage.at<uchar>(i, j + 1));
            //计算g(x,y)
            dstImage.at<uchar>(i, j) = (uchar)sqrt(t1 + t2);
        }
    }
    return dstImage;
}

效果展示:

2、Sobel算子

Sobel算子是主要用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导,用于计算图像灰度函数的近似梯度。

因为Sobel算子结合了高斯平滑和分化,因此结果会具有更多的抗噪性。

不同方向的算子模板:                                                                  梯度的计算:

                                     

 opencv中sobel函数的参数如下:

void cv::Sobel   (     
    InputArray  src,    // 输入图像
    OutputArray  dst,   // 输出图像
    int      ddepth,    // 输出图像深度,-1 表示等于 src.depth()
    int      dx,        // 水平方向的阶数
    int      dy,        // 垂直方向的阶数
    int     ksize = 3,    // 卷积核的大小,常取 1, 3, 5, 7 等奇数
    double  scale = 1,    // 缩放因子,应用于计算结果
    double  delta = 0,    // 增量数值,应用于计算结果
    int borderType = BORDER_DEFAULT // 边界模式
)

3、Prewitt算子

Prewitt算子利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的。

这两个方向模板一个检测水平边缘,一个检测垂直边缘。

不同方向的算子模板:                                                                 

Prewitt算子定位精度不如Sobel算子,在真正的使用中,一般不会用到这个算子,效果较差,因此不多做分析。

4、Canny算子

可以说,Canny 边缘检测算法是被业界公认的性能最为优良的边缘检测算法之一。Canny算法不是像Roberts、Prewitt、Sobel等这样简单梯度算子或锐化模板,它是在梯度算子基础上,引入了一种能获得抗噪性能好、定位精度高的单像素边缘的计算策略。

Canny 算子,在一阶微分的基础上,增加了非最大值抑制和双阈值检测,是边缘检测算子中最常用的一种,常被其它算子作为标准算子来进行优劣比较。

4.1算法步骤

1) 用高斯滤波器对输入图像做平滑处理 (大小为 5x5 的高斯核)去噪

 2) 计算图像的梯度强度和角度方向 ( x 和 y 方向上的卷积核)

 角度方向近似为四个可能值,即 0, 45, 90, 135

3) 对图像的梯度强度进行非极大抑制

   可看做边缘细化:只有候选边缘点被保留,其余的点被移除

4) 利用双阈值检测和连接边缘

    若候选边缘点大于上阈值,则被保留;小于下阈值,则被舍弃;处于二者之间,须视其所连接的像素点,大于上阈值则被保留,反之舍弃

 opencv中canny函数的参数如下:

void cv::Canny (     
    InputArray    image,    // 输入图像 (8位)
    OutputArray   edges,    // 输出图像 (单通道,8位)
    double      threshold1,  // 下阈值
    double      threshold2,  // 上阈值
    int         apertureSize = 3,
    bool        L2gradient = false
)

 一般 上阈值 下阈值 = 2 ~ 3

  L2gradient 默认 flase,表示图像梯度强度的计算采用近似形式;若为 true,则表示采用更精确的形式

5、Laplace(拉普拉斯算子)

索贝尔算子 (Sobel) 和拉普拉斯算子 (Laplace) 都是用来对图像进行边缘检测的,不同之处在于,前者是求一阶导,后者是求二阶导。

常用算子模块:

  opencv中Laplace函数的参数如下:

void    Laplacian (     
    InputArray     src,
    OutputArray    dst,
    int       ddepth,
    int       ksize = 1,
    double    scale = 1,
    double    delta = 0,
    int       borderType = BORDER_DEFAULT
)

                                                                                     

 简单对比

 在进行 Sobel,Laplacian 和 Canny 边缘检测之前,统一调用 GaussianBlur 来降低图像噪声

using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
    Mat src,src_binary,src_gray;
    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y, dst;
    src = imread("D:/opencv练习图片/薛之谦.jpg");
    imshow("原图", src);
    GaussianBlur(src, src, Size(3, 3), 0);
    cvtColor(src, src_gray, COLOR_BGR2GRAY);
    Sobel(src_gray, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);//求x方向梯度
    convertScaleAbs(grad_x, abs_grad_x);//转换格式 8u
    Sobel(src_gray, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);//求y方向梯度  
    convertScaleAbs(grad_y, abs_grad_y);
    //合并梯度
    addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
    imshow("solbel算子", dst);    
    Canny(src_gray, dst, 100, 300);
    imshow("Canny算子", dst);
    Laplacian(src_gray, dst, -1, 3);
    imshow("Laplace算子", dst);

    waitKey(0);
    return 0;

}

效果展示:

LOG滤波器方法通过检测二阶导数过零点来判断边缘点。LOG滤波器中的a正比于低通滤波器的宽度,a越大,平滑作用越显著,去除噪声越好,但图像的细节也损失越大,边缘精度也就越低。所以在边缘定位精度和消除噪声级间存在着矛盾,应该根据具体问题对噪声水平和边缘点定位精度要求适当选取。

讨论和比较了几种常用的边缘检测算子。梯度算子计算简单,但精度不高,只能检测出图像大致的轮廓,而对于比较细的边缘可能会忽略。Prewitt 和Sobel 算子比Roberts 效果要好一些。LOG 滤波器和Canny 算子的检测效果优于梯度算子,能够检测出图像较细的边缘部分。不同的系统,针对不同的环境条件和要求,选择合适的算子来对图像进行边缘检测。

标签:src,边缘,int,检测,图像,opencv,算法,算子
来源: https://blog.csdn.net/Cris_Wang7/article/details/120833010

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有