一、边缘检测原理

边缘(Edge)是指图像中局部灰度值(强度)发生显著变化的区域,通常存在于:

  • 目标与目标之间
  • 目标与背景之间
  • 区域与区域之间
  • 不同颜色或纹理之间

图像强度变化的两种典型形式:

  • 阶跃变化(Step Edge):图像灰度在某一点两侧发生突变,即像素值突然从一个灰度跳到另一个灰度。
  • 屋顶变化(Roof Edge)/线条变化:图像灰度突然升高,保持一小段距离后又下降回原值。
  • 前向差分(Forward Difference)计算梯度:

                        \frac{df(x,y))}{dx} = f(x,y)-f(x-1,y)

                当前像素与左侧像素的差值 → 近似水平变化率

  • 改进后(中心差分)

                        \frac{df(x,y)}{dx} = \frac{f(x+1,y)-f(x-1,y)}{2}

二、Sobel算子边缘检测

/* 用途:用于计算图像的一阶或高阶梯度,通过Sobel卷积算子检测图像在
       x方向或y方向的灰度变化,从而提取边缘信息 */
void cv::Sobel( InputArray src, OutputArray dst, int ddepth,
                         int dx, int dy, int ksize = 3,
                         double scale = 1, double delta = 0,
                         int borderType = BORDER_DEFAULT );
/*
src:输入图像
dst:输出图像,与输入图像具有相同的尺寸(但数据类型由ddepth决定)
ddepth:输出图像的数据类型(深度)
dx:x方向求导阶数
dy:y方向求导阶数
ksize:Sobel算子核大小(必须为1、3、5、7等奇数)
scale:缩放系数,对计算结果进行比例放大或缩小
delta:偏移量,在结果中额外加上的值
borderType:像素外推法选择标志
*/

三、Scharr算子边缘检测

/* 用途:用于计算图像在x方向或y方向的梯度,是Sobel算子的增强版本。
       在使用3×3核时,Scharr算子相比Sobel具有更高的精度和更好的旋转对称性,
       能更准确地检测图像边缘细节与灰度变化*/
void cv::Scharr( InputArray src, OutputArray dst, int ddepth,
                          int dx, int dy, double scale = 1, double delta = 0,
                          int borderType = BORDER_DEFAULT );
/*
src:输入图像
dst:输出图像,与输入图像具有相同的尺寸(数据类型由ddepth决定)
ddepth:输出图像的数据类型(深度)
dx:x方向求导阶数
dy:y方向求导阶数
scale:缩放系数,对计算结果进行比例放大或缩小
delta:偏移量,在结果中额外加上的值
borderType:像素外推法选择标志
*/

四、两种算子的生成

/* 用途:用于生成图像导数运算所需的一维卷积核(x方向与y方向),
       这些卷积核通常用于Sobel、Scharr等梯度计算,
       或与sepFilter2D配合实现自定义微分滤波。
       通过该函数可灵活构造一阶导数、二阶导数等算子,
       常用于边缘检测、梯度分析、特征提取以及高性能可分离卷积计算 */
void cv::getDerivKernels( OutputArray kx, OutputArray ky,
                                   int dx, int dy, int ksize,
                                   bool normalize = false, int ktype = CV_32F );
/*
kx:行滤波器系数的输出矩阵,尺寸为ksize * 1
ky:列滤波器系数的输出矩阵,尺寸为ksize * 1
dx:X方向导数的阶次
dy:y方向导数的阶次
ksize:滤波器的大小,可以选择的参数为FILTER_SCHARR,1,3,5或7
normalize:是否对滤波器系数进行归一化的标志,默认值为false,表示不进行系数归一化
ktype:滤波器系数类型,可以选择CV_32F或CV_64F,默认参数为CV_32F
*/

五、示例代码

    QString imgPath = QApplication::applicationDirPath() + "/Images";
    cv::String s_imgPath = imgPath.toLocal8Bit().data();
    Mat img = imread(s_imgPath + "/equalLena.jpg", IMREAD_ANYCOLOR);

    if (img.empty())
    {
        qDebug() << "图片加载失败, 请确认图像文件名称是否正确";
        return;
    }

    Mat resultX, resultY, resultXY;

    /*X方向一阶边缘*/
    Sobel(img, resultX, CV_16S, 1, 0, 3);
    convertScaleAbs(resultX, resultX);/*求取绝对值*/

    /*Y方向一阶边缘*/
    Sobel(img, resultY, CV_16S, 0, 1, 3);
    convertScaleAbs(resultY, resultY);

    /*整幅图像的一阶边缘*/
    resultXY = resultX + resultY;

    imshow("resultX", resultX);
    imshow("resultY", resultY);
    imshow("resultXY", resultXY);
    waitKey(0);

    /*X方向一阶边缘*/
    Scharr(img, resultX, CV_16S, 1, 0);
    convertScaleAbs(resultX, resultX);/*求取绝对值*/

    /*Y方向一阶边缘*/
    Scharr(img, resultY, CV_16S, 0, 1);
    convertScaleAbs(resultY, resultY);/*求取绝对值*/

    resultXY = resultX + resultY;
   
    imshow("resultX", resultX);
    imshow("resultY", resultY);
    imshow("resultXY", resultXY);
    waitKey(0);


    /*生成边缘检测器*/
    Mat sobel_x1, sobel_y1;/*存放分离的sobel算子*/
    Mat scharr_x, scharr_y;/*存放分离的scharr算子*/
    Mat sobelX1, scharrX;/*存放最终算子*/

    getDerivKernels(sobel_x1, sobel_y1, 1, 0, 3);
    sobel_x1 = sobel_x1.reshape(CV_8U, 1);
    sobelX1 = sobel_y1 * sobel_x1;/*计算滤波器*/

    getDerivKernels(scharr_x, scharr_y, 1, 0, FILTER_SCHARR);
    scharr_x = scharr_x.reshape(CV_8U, 1);
    scharrX = scharr_y * scharr_x;/*计算滤波器*/

    cout << "X sobel: " << endl << sobelX1 << endl;
    cout << "X scharr: " << endl << scharrX << endl;


    waitKey(0);
    destroyAllWindows();

六、Laplacian算子边缘检测

1、Laplacian算子

Laplacian算子是一种基于二阶导数的边缘检测算法,主要用于识别图像中的快速亮度变化,即边缘。由于它直接与图像的二阶导数相关,因此能够有效地找到边缘位置,但对噪声比较敏感。

Sobel和Scharr边缘检测算法存在的问题:

  • 分别计算两个方向边缘
  • 边缘与方向相关性较大
  • Laplacian算子:

                                        Laplacian(f) = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2}

  • 方向无关
  • 容易受到噪声的影响
  • 3 * 3的Laplacian算子:

                                                ​​​​​​​        \begin{bmatrix} 0 & 1 & 0\\ 1 & -4 & 1\\ 0 & 1 & 0 \end{bmatrix}

2、Laplacian边缘检测函数

/* 用途:用于计算图像的二阶导数(Laplacian算子),通过检测灰度变化的“变化率”,
       强调图像中灰度突变的位置,从而突出边缘信息。
       与Sobel(一阶导数)相比,Laplacian对边缘方向不敏感,
       能同时检测各个方向的边缘,但对噪声也更敏感 */
void cv::Laplacian( InputArray src, OutputArray dst, int ddepth,
                             int ksize = 1, double scale = 1, double delta = 0,
                             int borderType = BORDER_DEFAULT );
/*
src:输入原图像,可以是灰度图像和彩色图像
dst:输出图像,与输入图像src具有相同的尺寸和通道数
ddepth:输出图像的数据类型(深度),根据输入图像的数据类型不同拥有不同的取值范围
ksize:滤波器的大小,必须为正奇数
scale:对导数计算结果进行缩放的缩放因子,默认系数为1,不进行缩放
delta:偏值,在计算结果中加上偏值
borderType:像素外推法选择标志
*/

七、Canny算子边缘检测

1、Canny边缘检测原理介绍

Canny边缘检测的主要步骤:

1> 使用高斯滤波平滑图像

在进行边缘检测之前,首先需要对原始图像进行平滑处理。因为图像在采集、传输过程中不可避免会引入噪声,而边缘检测本质上依赖于灰度变化率(导数),这种运算会对噪声非常敏感,甚至会把噪声误判为边缘。

常用高斯滤波平滑图像滤波器:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        G = \frac{1}{139}\begin{bmatrix} 2 & 4 & 5 & 4 & 2\\ 4 & 9 & 12 & 9 & 4\\ 5 & 12 & 15 & 12 & 5\\ 4 & 9 & 12 & 9 & 4\\ 2 & 4 & 5 & 4 & 2 \end{bmatrix}      

2> 计算图像中每个像素的梯度方向和幅度

在平滑后的图像上,计算每个像素点的灰度变化情况。通常通过Sobel算子分别计算水平方向和垂直方向的变化量,得到两个梯度分量 Ix 和 Iy​。

然后将这两个分量合成为:

        \theta = acttan\left ( \frac{I_y}{I_x} \right )               G = acttan\sqrt{I_{x}^2+I_{y}^2}

  • 梯度方向 θ:表示变化发生的方向,用于确定边缘的走向
  • 梯度幅值 G:表示灰度变化的强度,用来判断该点是否可能是边缘

3> 应用非极大值抑制算法边缘检测带来的杂散响应

在上一步中得到的边缘通常是比较“粗”的带状结构,而不是理想的单像素边缘。因此需要进一步细化。

非极大值抑制的核心思想是:沿着梯度方向,只保留局部最大的那个像素点,其余的全部抑制为0。也就是说,如果某个像素点不是该方向上的“最高点”,就认为它不是最真实的边缘位置。

经过这一步处理后,边缘会从“宽线条”变成“细线条”,更加精确。

4> 应用双阈值法划分强边缘和弱边缘

经过非极大值抑制后,图像中仍然存在一些不确定的边缘,需要进一步筛选。Canny算法采用双阈值策略,而不是简单的单阈值。

具体做法是设置一个高阈值和一个低阈值:

  • 梯度值高于高阈值的像素 → 明确的强边缘
  • 梯度值介于两者之间 → 可能的弱边缘
  • 梯度值低于低阈值 → 直接认为不是边缘

这种方法的优势在于:既能保留明显边缘,又不会因为阈值过高导致边缘断裂。

5> 消除孤立的弱边缘(边缘连接 / 滞后处理)

在弱边缘中,既包含真实边缘(只是强度较低),也包含噪声。为了区分它们,Canny引入“边缘连接”策略。

具体规则是:

  • 如果一个弱边缘像素与强边缘相连(8邻域连接),则认为它属于真实边缘 → 保留
  • 如果一个弱边缘像素是孤立的,没有连接到强边缘 → 认为是噪声 → 删除

通过这一步,可以有效去除孤立噪声点,同时保证边缘的连续性。

2、Canny算法函数

/* 用途:用于进行高质量边缘检测,通过一整套优化流程(高斯滤波、梯度计算、
       非极大值抑制、双阈值筛选和边缘连接)提取图像中的清晰边缘。
       相比Sobel、Laplacian等基础算子,Canny能够有效抑制噪声、
       保留真实边缘并去除伪边缘 */
void cv::Canny( InputArray image, OutputArray edges,
                         double threshold1, double threshold2,
                         int apertureSize = 3, bool L2gradient = false );
/*
image:输入图像,必须是CV_8U单通道或者三通道图像
edges:输出图像,与输入图像具有相同尺寸的单通道图像,且数据类型为CV_8U
threshold1:第一个滞后阈值(低阈值----弱边缘判定)
threshold2:第二个滞后阈值(高阈值----强边缘判定)
apertureSize:Sobel算子的直径
L2gradient:计算图像梯度幅值的标志(是否使用更精确的梯度计算方式)
*/

八、示例代码

    QString imgPath = QApplication::applicationDirPath() + "/Images";
    cv::String s_imgPath = imgPath.toLocal8Bit().data();
    Mat img = imread(s_imgPath + "/equalLena.jpg", IMREAD_ANYCOLOR);

    if (img.empty())
    {
        qDebug() << "图片加载失败, 请确认图像文件名称是否正确";
        return;
    }
    Mat result, result_g, result_G;

    /*未滤波提取边缘*/
    Laplacian(img, result, CV_16S, 3, 1, 0);
    convertScaleAbs(result, result);

    /*滤波后提取Laplacian边缘*/
    GaussianBlur(img, result_g, Size(3, 3), 5, 0);
    Laplacian(result_g, result_G, CV_16S, 3, 1, 0);
    convertScaleAbs(result_G, result_G);

    imshow("result", result);
    imshow("result_G", result_G);

    waitKey(0);

    Mat resultHigh, resultLow, resultG;

    /*大阈值检测图像边缘*/
    Canny(img, resultHigh, 100, 200, 3);
    
    /*小阈值检测图像边缘*/
    Canny(img, resultLow, 20, 40, 3);

    /*高斯模糊后检测图像边缘*/
    GaussianBlur(img, resultG, Size(3, 3), 5);
    Canny(resultG, resultG, 100, 200, 3);
    
    imshow("resultHigh", resultHigh);
    imshow("resultLow", resultLow);
    imshow("resultG", resultG);

    waitKey(0);
    destroyAllWindows();

九、四种边缘检测算子对比表

对比维度 Sobel算子 Scharr算子 Laplacian算子 Canny算子
算子类型 一阶梯度 一阶梯度(优化版) 二阶导数 多阶段算法
数学本质 一阶偏导近似 改进的一阶偏导 二阶偏导(散度) 梯度 + 优化流程
是否有方向性 有(x/y) 有(x/y) 有(梯度方向)
核心公式 梯度 ∇f 梯度 ∇f(更精确) ∇²f 多步骤(非单一公式)
卷积核大小 3×3 / 5×5 固定3×3 3×3(常用) 不固定(组合)
抗噪声能力 中等(带平滑) 中等(优于Sobel) 差(极敏感) 强(高斯滤波)
边缘定位精度 中等 高(但不稳定) 很高
边缘连续性 一般 一般 较差(易断裂) 很好
是否产生双边缘 可能 可能 容易产生 基本不会
对细节敏感度 很高(含噪声) 高(受阈值控制)
计算复杂度 较高
实时性 一般
典型问题 方向误差 基本无明显问题 噪声放大 参数敏感
是否需要预处理 可选 可选 必须平滑 内置高斯
是否需要后处理 可选 可选 可选 已包含
OpenCV函数 Sobel() Scharr() Laplacian() Canny()

精度优先:Canny > Scharr > Sobel > Laplacian
速度优先:Sobel ≈ Scharr ≈ Laplacian > Canny
稳定性:Canny > Scharr > Sobel > Laplacian

更多推荐