OpenCV 笔记(8):图像的边缘和梯度

1. 图像的边缘

灰度图像的分割算法大多都基于两个性质:灰度的不连续性和灰度的相似性

对于不连续性的灰度,可以以灰度突变为基础分割一幅图像,比如通过图像的边缘,基于边缘进行图像分割。对于相似的灰度,可以通过区域进行图像分割。本文主要介绍图像边缘相关的内容和原理。

图像的边缘是图像中亮度变化比较大的点。图像边缘点的出现一般是由于深度的不连续、物体表面方向的变换、物体属性变化或者场景照明变化引起。

边缘是连通的边缘像素集合。

常见的边缘类型有三种:

  • 阶梯形边缘:即从一个灰度到比它高好多的另一个灰度。

  • 屋顶形边缘:它的灰度是慢慢增加到一定程度,然后慢慢减少。

  • 线性边缘:它的灰度从一个级别跳到另一个灰度级别之后然后回来。

真实的图像边缘可能含有噪声,不一定符合上述理想的边缘模型。

2. 图像的梯度

图像可以看成是一个二维离散函数,为了衡量图像灰度的变化率,可以对二维离散函数进行求导(连续的函数可以直接求导,离散的函数只能通过一些方法来近似)。一阶或二阶导数都可以检测局部灰度突变。

2.1 差分

差分 ,又名差分函数差分运算 ,一般是指有限差分,是数学中的一个概念,将原函数 f(x) 映射到 f(x+a) - f(x+b) 。

差分运算,相应于微分运算,是微积分中重要的一个概念。差分对应离散,微分对应连续。

差分可分为前向差分、反向差分中心差分。

2.1.1 前向差分

当自变量从 <math xmlns="http://www.w3.org/1998/Math/MathML"> x k x_k </math>xk变到 <math xmlns="http://www.w3.org/1998/Math/MathML"> x k + 1 x_{k+1} </math>xk+1时,函数的改变量 <math xmlns="http://www.w3.org/1998/Math/MathML"> △ y = f ( x k + 1 ) − f ( x k ) \triangle y = f(x_{k+1})-f(x_k) </math>△y = f(xk+1)−f(xk),称为函数 f(x) 在点 <math xmlns="http://www.w3.org/1998/Math/MathML"> x k x_k </math>xk的步长为 <math xmlns="http://www.w3.org/1998/Math/MathML"> h k = x k + 1 − x k h_k = x_{k+1} - x_k </math>hk = xk+1 − xk的一阶(前向)差分。

通常记为, <math xmlns="http://www.w3.org/1998/Math/MathML"> △ y k = y k + 1 − y k \triangle y_k = y_{k+1}-y_k </math>△yk = yk+1−yk,其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> △ \triangle </math>△为差分算子。

同理, <math xmlns="http://www.w3.org/1998/Math/MathML"> △ 2 f k = △ f x k + 1 − △ f x k \triangle^2f_k = \triangle fx_{k+1} - \triangle fx_k </math>△2fk = △ fxk+1 − △ fxk为 <math xmlns="http://www.w3.org/1998/Math/MathML"> x k x_k </math>xk处的二阶差分。

<math xmlns="http://www.w3.org/1998/Math/MathML"> △ n f k = △ n − 1 f x k + 1 − △ n − 1 f x k \triangle^nf_k = \triangle^{n-1}fx_{k+1} - \triangle^{n-1}fx_k </math>△nfk = △n−1fxk+1 − △n−1fxk为 <math xmlns="http://www.w3.org/1998/Math/MathML"> x k x_k </math>xk处的 n 阶差分。

2.1.2 反向差分

一阶反向差分为: <math xmlns="http://www.w3.org/1998/Math/MathML"> △ y k = y k − y k − 1 \triangle y_k = y_k-y_{k-1} </math>△yk = yk−yk−1

2.1.3 中心差分

一阶中心差分为: <math xmlns="http://www.w3.org/1998/Math/MathML"> △ y k = y k + 1 − y k − 1 2 \triangle y_k = \frac{y_{k+1}-y_{k-1}}{2} </math>△yk = 2yk+1−yk−1

2.2 导数

数字函数的导数可用有限差分定义。

首先,将 <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x + △ x ) f(x+\triangle x) </math>f(x+△x)展开为 x 的泰勒级数,我们可以得到一维函数 f(x) 在任意点 x 处的一阶导数的近似:

<math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x + △ x ) = f ( x ) + △ x ∗ ∂ f ( x ) ∂ x + ( △ x ) 2 2 ! ∗ ∂ 2 f ( x ) ∂ x 2 + ( △ x ) 3 3 ! ∗ ∂ 3 f ( x ) ∂ x 3 + . . . f(x+\triangle x)=f(x)+\triangle x* \frac{\partial f(x)}{\partial x} + \frac{(\triangle x)^2}{2!}*\frac{\partial^2 f(x)}{\partial x^2} + \frac{(\triangle x)^3}{3!}*\frac{\partial^3 f(x)}{\partial x^3} + ... </math>f(x+△x)=f(x)+△x∗∂x∂f(x) + 2!(△x)2∗∂x2∂2f(x) + 3!(△x)3∗∂x3∂3f(x) + ...

则:f(x+\triangle x) = \sum_{n=0}^{\infty}\frac{(\triangle x)^n}{n!}*\frac{\partial^n f(x)}{\partial x^n} \tag{1}

  • 当 <math xmlns="http://www.w3.org/1998/Math/MathML"> △ x = 1 \triangle x=1 </math>△x=1时

f(x+1) = f(x)+ \frac{\partial f(x)}{\partial x} + \frac{1}{2!}*\frac{\partial^2 f(x)}{\partial x^2} + \frac{1}{3!}*\frac{\partial^3 f(x)}{\partial x^3} + ... = \sum_{n=0}^{\infty}\frac{1}{n!}*\frac{\partial^n f(x)}{\partial x^n} \tag{2}

对于一阶导数,我们只使用线性项,可得: <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x + 1 ) = f ( x ) + ∂ f ( x ) ∂ x f(x+1) = f(x) + \frac{\partial f(x)}{\partial x} </math>f(x+1) = f(x) + ∂x∂f(x)

由此,用前向差分得到灰度差:

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ f ( x ) ∂ x = f ′ ( x ) = f ( x + 1 ) − f ( x ) \frac{\partial f(x)}{\partial x} = f'(x) = f(x+1)-f(x) </math>∂x∂f(x) = f′(x) = f(x+1)−f(x)

  • 当 <math xmlns="http://www.w3.org/1998/Math/MathML"> △ x = − 1 \triangle x=-1 </math>△x=−1时

f(x-1) = f(x)- \frac{\partial f(x)}{\partial x} + \frac{1}{2!}*\frac{\partial^2 f(x)}{\partial x^2} - \frac{1}{3!}*\frac{\partial^3 f(x)}{\partial x^3} + ... = \sum_{n=0}^{\infty}\frac{(-1)^n}{n!}*\frac{\partial^n f(x)}{\partial x^n} \tag{3}

对于一阶导数,我们同样只使用线性项,可得: <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x − 1 ) = f ( x ) − ∂ f ( x ) ∂ x f(x-1) = f(x) - \frac{\partial f(x)}{\partial x} </math>f(x−1) = f(x) − ∂x∂f(x)

由此,用反向差分得到灰度差:

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ f ( x ) ∂ x = f ′ ( x ) = f ( x ) − f ( x − 1 ) \frac{\partial f(x)}{\partial x} = f'(x) = f(x)-f(x-1) </math>∂x∂f(x) = f′(x) = f(x)−f(x−1)

使用(2)式-(3)式,用中心差分可得:

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ f ( x ) ∂ x = f ′ ( x ) = f ( x + 1 ) − f ( x − 1 ) 2 \frac{\partial f(x)}{\partial x} = f'(x) = \frac{f(x+1)-f(x-1)}{2} </math>∂x∂f(x) = f′(x) = 2f(x+1)−f(x−1)

中心差分的误差较小,导数通常表示为中心差分。基于中心差分 的二阶导数,使用(2)式+(3)式,可得: <math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x + 1 ) + f ( x − 1 ) = 2 f ( x ) + ∂ 2 f ( x ) ∂ x 2 f(x+1)+f(x-1)= 2f(x) + \frac{\partial^2 f(x)}{\partial x^2} </math>f(x+1)+f(x−1)= 2f(x) + ∂x2∂2f(x)

即:\frac{\partial^2 f(x)}{\partial x^2} = f''(x) = f(x+1)-2f(x)+f(x-1) \tag{4}

通常,一阶导数和二阶导数对分析边缘有以下的结论:

  • 一阶导数通常在图像中产生较粗的边缘;

  • 二阶导数对精细细节,如细线、孤立点和噪声有较强的响应;

  • 二阶导数在灰度斜坡和灰度台阶过渡处会产生双边缘响应;

  • 二阶导数的符号可用于确定边缘的过渡是从亮到暗还是从暗到亮。

计算图像中每个像素位置的一阶导数和二阶导数的方法是空间卷积

当我们把图像看做是一个二维离散函数时,通过上述方式可得 x、y 方向的二阶导数:

\frac{\partial^2 f(x,y)}{\partial x^2} = f(x+1,y)-2f(x,y)+f(x-1,y) \tag{5}

以及

\frac{\partial^2 f(x,y)}{\partial y^2} = f(x,y+1)-2f(x,y)+f(x,y-1) \tag{6}

根据拉普拉斯算子,图像可以定义为

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ 2 f = ∂ 2 f ∂ x 2 + ∂ 2 f ∂ y 2 \nabla^2f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2} </math>∇2f = ∂x2∂2f + ∂y2∂2f

由(5)式+(6)式,可得:

\nabla^2f(x,y)= f(x+1,y)+f(x-1,y)+ f(x,y+1)+f(x,y-1)-4f(x,y) \tag{7}

(7)式可写成如下 filter mask 的形式 :

拉普拉斯可以对图像进行锐化,利用下面的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> g ( x , y ) = f ( x , y ) + c [ ∇ 2 f ( x , y ) ] g(x,y)=f(x,y)+c[\nabla^2f(x,y)] </math>g(x,y)=f(x,y)+c[∇2f(x,y)]

其中,f(x,y) 表示原图,g(x,y) 表示锐化后的图像,c = -1,则

<math xmlns="http://www.w3.org/1998/Math/MathML"> g ( x , y ) = f ( x , y ) − ∇ 2 f ( x , y ) = 5 f ( x , y ) − f ( x + 1 , y ) − f ( x − 1 , y ) − f ( x , y + 1 ) − f ( x , y − 1 ) g(x,y)=f(x,y) - \nabla^2f(x,y)= 5f(x,y)-f(x+1,y)-f(x-1,y)-f(x,y+1)-f(x,y-1) </math>g(x,y)=f(x,y)− ∇2f(x,y)= 5f(x,y)−f(x+1,y)−f(x−1,y)−f(x,y+1)−f(x,y−1)

对于不同的拉普拉斯核,c 可能会取不同的值。

下面的代码,展示了对灰度图像进行锐化以及对灰度图像的拉普拉斯变换:

cpp 复制代码
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat src = imread(".../girl.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);
    imshow("gray",gray);

    Mat dst, laplace;

    dst.create(src.size(), CV_8UC1);
    laplace.create(src.size(), CV_8UC1);

    int height = gray.rows;
    int width  = gray.cols;

    for (int i = 1; i < height-1; i++)
    {
        for (int j = 1; j < width-1; j++)
        {
            dst.at<uchar>(i, j) = saturate_cast<uchar>(5 * gray.at<uchar>(i, j) - gray.at<uchar>(i + 1, j) - gray.at<uchar>(i - 1, j)- gray.at<uchar>(i, j+1)- gray.at<uchar>(i, j-1));
            laplace.at<uchar>(i, j) = saturate_cast<uchar>(gray.at<uchar>(i+1, j)+ gray.at<uchar>(i-1, j)+ gray.at<uchar>(i, j+1)+ gray.at<uchar>(i, j-1)-4* gray.at<uchar>(i, j));
        }
    }
    imshow("dst", dst);
    imshow("laplace", laplace);

    waitKey(0);
    return 0;
}

除此之外,还有一些其他的拉普拉斯核:

后续的文章会详细介绍拉普拉斯相关的内容,以及如何在 OpenCV 中如何使用相关的函数,它的主要用途包括:

  • 图像锐化

  • 边缘检测

  • 模糊检测

2.3 图像的梯度及其性质

梯度 是一个有大小有方向的向量。梯度的方向是函数变化最快的方向。

图像梯度是指图像某个像素在 x 和 y 两个方向上的变化率(与相邻像素比较),它是一个二维向量,由 X 轴的变化、Y 轴的变化这2个分量组成。

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ f ( x , y ) = g r a d [ f ( x , y ) ] = [ g x ( x , y ) g y ( x , y ) ] = [ ∂ f ( x , y ) ∂ x ∂ f ( x , y ) ∂ y ] \nabla f(x,y) = grad[f(x,y)]=\begin{bmatrix} g_x(x,y) \\ g_y(x,y) \\ \end{bmatrix} = \begin{bmatrix} \frac{\partial f(x,y)}{\partial x} \\ \frac{\partial f(x,y)}{\partial y} \\ \end{bmatrix} </math>∇ f(x,y) = grad[f(x,y)]=[gx(x,y) gy(x,y) ] = [∂x∂f(x,y) ∂y∂f(x,y) ]

  • 向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ f \nabla f </math>∇ f的幅度(长度)

幅度由 M(x,y) 表示,根据欧几里得范数(L2 范数)可得:

M(x,y) = \parallel \nabla f(x,y)\parallel = \sqrt{g_x(x,y)^2 + g_y(x,y)^2} \tag{8}

其中, M(x,y) 是与原图像大小相同的图像,它是 x 和 y 在 f 的所有像素位置上变化时产生的,也被称为梯度图像

求梯度的幅度时,由于公式(8)的计算量比较大,可以用 L1 范数来近似梯度的幅度:

<math xmlns="http://www.w3.org/1998/Math/MathML"> M ( x , y ) = ∣ g x ( x , y ) ∣ + ∣ g y ( x , y ) ∣ M(x,y) = |g_x(x,y)| + |g_y(x,y)| </math>M(x,y) = ∣gx(x,y)∣+∣gy(x,y)∣

  • 梯度的方向角

<math xmlns="http://www.w3.org/1998/Math/MathML"> θ = arctan ⁡ [ g y ( x , y ) g x ( x , y ) ] \theta = \arctan \begin{bmatrix} \frac{g_y(x,y)}{g_x(x,y)} \end{bmatrix} </math>θ = arctan[gx(x,y)gy(x,y)]

经典的图像梯度算法是考虑图像的每个像素的某个邻域内的灰度变化,利用边缘临近的一阶或二阶导数变化规律,对原始图像中像素某个邻域设置梯度算子(例如 Sobel 算子、Roberts 算子、Robinson 算子、Laplace 算子等等),通过图像卷积实现对图像两个方向上梯度的计算。

3. 总结

本文介绍了图像边缘的含义,通过图像边缘引出图像的梯度的概念。用数学的方式推导出图像梯度并介绍了其相关的性质。梯度是一个基础且重要的概念,遍布机器学习、深度学习的领域。

介绍它们是为了后续介绍各种图像边缘检测算法做准备的,毕竟边缘检测是传统的图像分割算法。

相关推荐
Jack黄从零学c++2 小时前
opencv(c++)图像的灰度转换
c++·人工智能·opencv
青龙摄影4 小时前
【监控】如何调出电脑的中摄像头,从摄像头获取视频流
人工智能·opencv·计算机视觉
CP-DD4 小时前
OpenCV DNN
人工智能·opencv·dnn
慕容复之巅17 小时前
基于MATLAB+opencv人脸疲劳检测
开发语言·opencv·matlab
西木九21 小时前
解决:WSL2可视化opencv和pyqt冲突:QObject::moveToThread
python·opencv·pyqt
亦枫Leonlew1 天前
三维测量与建模笔记 - 3 Python Opencv实现相机标定
笔记·python·opencv·相机标定
三维重建--小博主1 天前
人群计数制作私有数据集教程-----自用
python·opencv·计算机视觉
zhanghongyi_cpp2 天前
图像分割(二)
人工智能·python·opencv·计算机视觉
PaLu-LI2 天前
ORB-SLAM2源码学习:Frame.cc: Frame::isInFrustum 判断地图点是否在当前帧的视野范围内
c++·人工智能·opencv·学习·算法·ubuntu·计算机视觉
baiyu332 天前
清理 brew 安装的 opencv 的依赖
人工智能·opencv·计算机视觉