OpenCV Sobel

Sobel 算子是一种常用的图像处理工具,它描述了图像中每个像素点处的亮度或颜色值变化方向强度。它通常用于分析图像的边缘、纹理、形状和其他特征。

在二维图像中,梯度通常是两个分量的矢量,分别表示在水平和垂直方向上的变化率,这两个分量通常被称为 x 梯度和 y 梯度。

1. 图像的梯度

计算图像的梯度,可以使用 opencv 中的 Sobel 函数来完成,其函数声明如下:

cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)
  1. src:输入图像,通常是单通道的灰度图像d
  2. ddepth:表示结果图像的数据类型,它可以取以下值之一:
    • cv2.CV_8U:8位无符号整数
    • cv2.CV_16U:16位无符号整数
    • cv2.CV_32F:32位浮点数
    • cv2.CV_64F:64位浮点数
  3. dx:x方向上的导数阶数,通常为0、1或2
  4. dy:y方向上的导数阶数,通常为0、1或2
  5. dst:存储运算的结果,如果未提供此参数,将创建一个与输入图像相同大小和深度的新图来存储结果
  6. ksize:Sobel核的大小,它通常是一个奇数值,例如3、5或7,较大的内核将导致更强烈的平滑效果
  7. scale:可选的比例因子,用于调整结果图像的强度。默认值为1
  8. delta:可选的偏移值,它将添加到结果中,默认值为0
  9. borderType:这是用于处理边界的可选参数,通常情况下可以采用默认值 cv2.BORDER_DEFAULT
import cv2
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')


# 显示图像
def show_image(image, title, index):
    plt.subplot(3, 2, index)
    plt.imshow(image, cmap='gray')
    plt.title(title)
    plt.axis('off')


# 计算图像梯度
def calculate_gradient(image, kszie):
    gradient_x = cv2.Sobel(image, ddepth=cv2.CV_8U, dx=1, dy=0, ksize=kszie)
    gradient_y = cv2.Sobel(image, ddepth=cv2.CV_8U, dx=0, dy=1, ksize=kszie)
    return gradient_x, gradient_y


def test():

    image = cv2.imread('demo.png')
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    gx, gy = calculate_gradient(image, kszie=1)
    show_image(gx, 'x kszie=1', 1)
    show_image(gy, 'y kszie=1', 2)

    gx, gy = calculate_gradient(image, kszie=3)
    show_image(gx, 'x kszie=3', 3)
    show_image(gy, 'y kszie=3', 4)

    gx, gy = calculate_gradient(image, kszie=5)
    show_image(gx, 'x kszie=5', 5)
    show_image(gy, 'y kszie=5', 6)

    plt.show()

if __name__ == '__main__':
    test()

梯度的计算就是使用表示不同方向的 filter 对原始图像进行卷积计算,如下代码示例:

import cv2
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')


# 1x1 滤波器
sobel_x_1 = np.array([[-1, 0, 1]], dtype=float)
sobel_y_1 = np.array([[-1],
                      [0],
                      [1]], dtype=float)

# 3x3 滤波器
sobel_x_3 = np.array([[-1, 0, 1],
                      [-2, 0, 2],
                      [-1, 0, 1]], dtype=float)
sobel_y_3 = np.array([[-1, -2, -1],
                      [ 0,  0,  0],
                      [ 1,  2,  1]], dtype=float)

# 5x5 滤波器
sobel_x_5 = np.array([[-1, -2, 0, 2, 1],
                      [-4, -8, 0, 8, 4],
                      [-6, -12, 0, 12, 6],
                      [-4, -8, 0, 8, 4],
                      [-1, -2, 0, 2, 1]], dtype=np.float32)
sobel_y_5 = np.array([[-1, -4, -6, -4, -1],
                      [-2, -8, -12, -8, -2],
                      [0, 0, 0, 0, 0],
                      [2, 8, 12, 8, 2],
                      [1, 4, 6, 4, 1]], dtype=np.float32)


# 显示图像
def show_image(image, title, index):
    plt.subplot(3, 2, index)
    plt.imshow(image, cmap='gray', vmin=0, vmax=255)
    plt.title(title)
    plt.axis('off')


# 计算图像梯度
def calculate_gradient(image, sobel_x, sobel_y):

    gradient_x = cv2.filter2D(image, ddepth=cv2.CV_8U, kernel=sobel_x, borderType=cv2.BORDER_DEFAULT)
    gradient_y = cv2.filter2D(image, ddepth=cv2.CV_8U, kernel=sobel_y, borderType=cv2.BORDER_DEFAULT)
    return gradient_x, gradient_y


def test():

    image = cv2.imread('demo.png')
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    gx, gy = calculate_gradient(image, sobel_x_1, sobel_y_1)
    show_image(gx, 'x kszie=1', 1)
    show_image(gy, 'y kszie=1', 2)

    gx, gy = calculate_gradient(image, sobel_x_3, sobel_y_3)
    show_image(gx, 'x kszie=3', 3)
    show_image(gy, 'y kszie=3', 4)

    gx, gy = calculate_gradient(image, sobel_x_5, sobel_y_5)
    show_image(gx, 'x kszie=5', 5)
    show_image(gy, 'y kszie=5', 6)

    plt.show()

if __name__ == '__main__':
    test()

默认使用 borderType=cv2.BORDER_DEFAULT,则先 pad 周边像素,如下图所示:

然后,计算每个像素经过卷积后的值,得到 x 和 y 方向的图像梯度矩阵:

2. 方向和幅值

  • 梯度幅度:理解为图像边缘的强度,梯度的模长(或者说梯度的幅值)越大,说明此处图像变化的程度越大,也就越有可能是图像的边缘
  • 梯度方向:两个方向梯度的夹角,可以理解为明亮变化的主要方向
  1. G 表示梯度幅值
  2. θ 表示梯度方向
import cv2
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')


def show_image(image, title, index):
    plt.subplot(1, 2, index)
    plt.imshow(image, cmap='gray', vmin=0, vmax=255)
    plt.title(title)
    plt.axis('off')


def test():

    image = cv2.imread('demo.png')
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    gx = cv2.Sobel(image, cv2.CV_32F, 1, 0, ksize=3)
    gy = cv2.Sobel(image, cv2.CV_32F, 0, 1, ksize=3)

    # 1. 方法1
    # 计算幅度和方向,angleInDegrees 为 True 将会返回角度,否则返回弧度
    magnitude, direction = cv2.cartToPolar(gx, gy, angleInDegrees=True)

    # 2. 方法2
    magnitude = np.sqrt(gx ** 2 + gy ** 2)
    direction = np.arctan2(gy, gx)
    # 转换为角度
    direction = (np.degrees(direction) + 360) % 360

    show_image(direction, 'direction', 1)
    show_image(magnitude, 'magnitude', 2)
    plt.show()


if __name__ == '__main__':
    test()

未经允许不得转载:一亩三分地 » OpenCV Sobel
评论 (0)

8 + 8 =