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

1. 图像的边缘

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

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

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

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

常见的边缘类型有三种:

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

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

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

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

2. 图像的梯度

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

2.1 差分

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

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

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

2.1.1 前向差分

当自变量从 x k x_k xk变到 x k + 1 x_{k+1} xk+1时,函数的改变量 △ y = f ( x k + 1 ) − f ( x k ) \triangle y = f(x_{k+1})-f(x_k) △y = f(xk+1)−f(xk),称为函数 f(x) 在点 x k x_k xk的步长为 h k = x k + 1 − x k h_k = x_{k+1} - x_k hk = xk+1 − xk的一阶(前向)差分。

通常记为, △ y k = y k + 1 − y k \triangle y_k = y_{k+1}-y_k △yk = yk+1−yk,其中 △ \triangle △为差分算子。

同理, △ 2 f k = △ f x k + 1 − △ f x k \triangle^2f_k = \triangle fx_{k+1} - \triangle fx_k △2fk = △ fxk+1 − △ fxk为 x k x_k xk处的二阶差分。

△ 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 △nfk = △n−1fxk+1 − △n−1fxk为 x k x_k xk处的 n 阶差分。

2.1.2 反向差分

一阶反向差分为: △ y k = y k − y k − 1 \triangle y_k = y_k-y_{k-1} △yk = yk−yk−1

2.1.3 中心差分

一阶中心差分为: △ y k = y k + 1 − y k − 1 2 \triangle y_k = \frac{y_{k+1}-y_{k-1}}{2} △yk = 2yk+1−yk−1

2.2 导数

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

首先,将 f ( x + △ x ) f(x+\triangle x) f(x+△x)展开为 x 的泰勒级数,我们可以得到一维函数 f(x) 在任意点 x 处的一阶导数的近似:

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} + ... 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}

  • △ x = 1 \triangle x=1 △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}

对于一阶导数,我们只使用线性项,可得: f ( x + 1 ) = f ( x ) + ∂ f ( x ) ∂ x f(x+1) = f(x) + \frac{\partial f(x)}{\partial x} f(x+1) = f(x) + ∂x∂f(x)

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

∂ f ( x ) ∂ x = f ′ ( x ) = f ( x + 1 ) − f ( x ) \frac{\partial f(x)}{\partial x} = f'(x) = f(x+1)-f(x) ∂x∂f(x) = f′(x) = f(x+1)−f(x)

  • △ x = − 1 \triangle x=-1 △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}

对于一阶导数,我们同样只使用线性项,可得: f ( x − 1 ) = f ( x ) − ∂ f ( x ) ∂ x f(x-1) = f(x) - \frac{\partial f(x)}{\partial x} f(x−1) = f(x) − ∂x∂f(x)

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

∂ f ( x ) ∂ x = f ′ ( x ) = f ( x ) − f ( x − 1 ) \frac{\partial f(x)}{\partial x} = f'(x) = f(x)-f(x-1) ∂x∂f(x) = f′(x) = f(x)−f(x−1)

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

∂ 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} ∂x∂f(x) = f′(x) = 2f(x+1)−f(x−1)

中心差分的误差较小,导数通常表示为中心差分。基于中心差分 的二阶导数,使用(2)式+(3)式,可得: 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} 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}

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

∇ 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} ∇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 的形式 :

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

g ( x , y ) = f ( x , y ) + c ∇ 2 f ( x , y ) g(x,y)=f(x,y)+c\\nabla\^2f(x,y) g(x,y)=f(x,y)+c∇2f(x,y)

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

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) 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个分量组成。

∇ 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) = gradf(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} ∇ f(x,y) = gradf(x,y)=gx(x,y) gy(x,y) = ∂x∂f(x,y) ∂y∂f(x,y)

  • 向量 ∇ f \nabla f ∇ 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 范数来近似梯度的幅度:

M ( x , y ) = ∣ g x ( x , y ) ∣ + ∣ g y ( x , y ) ∣ M(x,y) = |g_x(x,y)| + |g_y(x,y)| M(x,y) = ∣gx(x,y)∣+∣gy(x,y)∣

  • 梯度的方向角

θ = arctan ⁡ g y ( x , y ) g x ( x , y ) \theta = \arctan \begin{bmatrix} \frac{g_y(x,y)}{g_x(x,y)} \end{bmatrix} θ = arctangx(x,y)gy(x,y)

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

3. 总结

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

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

相关推荐
半壶清水11 小时前
PaddlePaddle、easyocr与OpenCV工具识别书法图片内容,自动批量重命名图片文件的方法
人工智能·opencv·ocr·paddlepaddle
sali-tec12 小时前
C# 基于OpenCv的视觉工作流-章81-弯脚检测
图像处理·人工智能·opencv·算法·计算机视觉
sali-tec2 天前
C# 基于OpenCv的视觉工作流-章80-长短脚
图像处理·人工智能·opencv·算法·计算机视觉
yubo05092 天前
计算机视觉第十课:摄像头实时 颜色 + 形状 识别
python·opencv·计算机视觉
_李小白2 天前
【android opencv学习笔记】Day 28: 滤波算法之中值滤波器
android·opencv·学习
DogDaoDao2 天前
OpenCV 踩坑全指南
图像处理·人工智能·python·opencv·计算机视觉·matplotlib·rgb
保福寺研究僧3 天前
单像素相机
数码相机·opencv·计算机视觉
元直数字电路验证3 天前
OpenCV 图像缩放实验
人工智能·opencv·计算机视觉
yubo05093 天前
计算机视觉第八课:形状识别(自动认出 圆形、方形、三角形)
人工智能·opencv·计算机视觉
yubo05093 天前
计算机视觉第七课:颜色追踪(只框红色 / 蓝色 / 绿色物体)
人工智能·opencv·计算机视觉