OpenCVChapter9.边缘检测与图像分割

Posted zstar-_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCVChapter9.边缘检测与图像分割相关的知识,希望对你有一定的参考价值。

最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。
【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487
程序仓库:https://github.com/zstar1003/OpenCV-Learning

边缘检测

Roberts算子/Prewitt算子/Sobel算子/Laplacian算子

边缘检测的原理和matlab实现在我之前这两篇博文中提到过,这里不再赘述。
【计算机视觉】基础图像知识点整理【计算机视觉】数字图像处理基础知识题
此次来看OpenCV的实现方式。

OpenCV并没有直接提供相应的函数接口,因此通过自定义卷积核可以实现各种边缘检测算子。
示例程序:

"""
边缘检测(Roberts算子, Prewitt算子, Sobel算子, Laplacian算子)
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)

# 自定义卷积核
# Roberts 边缘算子
kernel_Roberts_x = np.array([[1, 0], [0, -1]])
kernel_Roberts_y = np.array([[0, -1], [1, 0]])
# Prewitt 边缘算子
kernel_Prewitt_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
kernel_Prewitt_y = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]])
# Sobel 边缘算子
kernel_Sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
kernel_Sobel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
# Laplacian 边缘算子
kernel_Laplacian_K1 = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
kernel_Laplacian_K2 = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])

# 卷积运算
imgBlur = cv2.blur(img, (3, 3))  # Blur 平滑后再做 Laplacian 变换
imgLaplacian_K1 = cv2.filter2D(imgBlur, -1, kernel_Laplacian_K1)
imgLaplacian_K2 = cv2.filter2D(imgBlur, -1, kernel_Laplacian_K2)
imgRoberts_x = cv2.filter2D(img, -1, kernel_Roberts_x)
imgRoberts_y = cv2.filter2D(img, -1, kernel_Roberts_y)
imgRoberts = np.uint8(cv2.normalize(abs(imgRoberts_x) + abs(imgRoberts_y), None, 0, 255, cv2.NORM_MINMAX))
imgPrewitt_x = cv2.filter2D(img, -1, kernel_Prewitt_x)
imgPrewitt_y = cv2.filter2D(img, -1, kernel_Prewitt_y)
imgPrewitt = np.uint8(cv2.normalize(abs(imgPrewitt_x) + abs(imgPrewitt_y), None, 0, 255, cv2.NORM_MINMAX))
imgSobel_x = cv2.filter2D(img, -1, kernel_Sobel_x)
imgSobel_y = cv2.filter2D(img, -1, kernel_Sobel_y)
imgSobel = np.uint8(cv2.normalize(abs(imgSobel_x) + abs(imgSobel_y), None, 0, 255, cv2.NORM_MINMAX))

plt.figure(figsize=(12, 8))
plt.subplot(341), plt.title('Origin'), plt.imshow(img, cmap='gray'), plt.axis('off')
plt.subplot(345), plt.title('Laplacian_K1'), plt.imshow(imgLaplacian_K1, cmap='gray'), plt.axis('off')
plt.subplot(349), plt.title('Laplacian_K2'), plt.imshow(imgLaplacian_K2, cmap='gray'), plt.axis('off')
plt.subplot(342), plt.title('Roberts'), plt.imshow(imgRoberts, cmap='gray'), plt.axis('off')
plt.subplot(346), plt.title('Roberts_X'), plt.imshow(imgRoberts_x, cmap='gray'), plt.axis('off')
plt.subplot(3, 4, 10), plt.title('Roberts_Y'), plt.imshow(imgRoberts_y, cmap='gray'), plt.axis('off')
plt.subplot(343), plt.title('Prewitt'), plt.imshow(imgPrewitt, cmap='gray'), plt.axis('off')
plt.subplot(347), plt.title('Prewitt_X'), plt.imshow(imgPrewitt_x, cmap='gray'), plt.axis('off')
plt.subplot(3, 4, 11), plt.title('Prewitt_Y'), plt.imshow(imgPrewitt_y, cmap='gray'), plt.axis('off')
plt.subplot(344), plt.title('Sobel'), plt.imshow(imgSobel, cmap='gray'), plt.axis('off')
plt.subplot(348), plt.title('Sobel_X'), plt.imshow(imgSobel_x, cmap='gray'), plt.axis('off')
plt.subplot(3, 4, 12), plt.title('Sobel_Y'), plt.imshow(imgSobel_y, cmap='gray'), plt.axis('off')
plt.tight_layout()
plt.show()

LoG算子(Marr-Hildreth 算法)

Marr-Hildreth 算法是改进的边缘检测算子,是平滑算子与 Laplace 算子的结合,因而兼具平滑和二阶微分的作用,其定位精度高,边缘连续性好,计算速度快。

示例程序:

"""
LoG边缘检测算子
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal

img = cv2.imread("../img/lena.jpg", flags=0)


def ZeroDetect(img):  # 判断零交叉点
    h, w = img.shape[0], img.shape[1]
    zeroCrossing = np.zeros_like(img, np.uint8)
    for x in range(0, w - 1):
        for y in range(0, h - 1):
            if img[y][x] < 0:
                if (img[y][x - 1] > 0) or (img[y][x + 1] > 0) \\
                        or (img[y - 1][x] > 0) or (img[y + 1][x] > 0):
                    zeroCrossing[y][x] = 255
    return zeroCrossing

imgBlur = cv2.blur(img, (3, 3))  # Blur 平滑后再做 Laplacian 变换

# 近似的 Marr-Hildreth 卷积核 (5*5)
kernel_MH5 = np.array([
    [0, 0, -1, 0, 0],
    [0, -1, -2, -1, 0],
    [-1, -2, 16, -2, -1],
    [0, -1, -2, -1, 0],
    [0, 0, -1, 0, 0]])
imgMH5 = signal.convolve2d(imgBlur, kernel_MH5, boundary='symm', mode='same')  # 卷积计算
zeroMH5 = ZeroDetect(imgMH5)  # 判断零交叉点

# 由 Gauss 标准差计算 Marr-Hildreth 卷积核
sigma = 3  # Gauss 标准差,输入参数
size = int(2 * round(3 * sigma)) + 1  # 根据标准差确定窗口大小,3*sigma 占比 99.7%
print("sigma=:d, size=".format(sigma, size))
x, y = np.meshgrid(np.arange(-size / 2 + 1, size / 2 + 1), np.arange(-size / 2 + 1, size / 2 + 1))  # 生成网格
norm2 = np.power(x, 2) + np.power(y, 2)
sigma2, sigma4 = np.power(sigma, 2), np.power(sigma, 4)
kernelLoG = ((norm2 - (2.0 * sigma2)) / sigma4) * np.exp(- norm2 / (2.0 * sigma2))  # 计算 LoG 卷积核
# Marr-Hildreth 卷积运算
imgLoG = signal.convolve2d(imgBlur, kernelLoG, boundary='symm', mode='same')  # 卷积计算
# 判断零交叉点
zeroCrossing = ZeroDetect(imgLoG)

plt.figure(figsize=(10, 7))
plt.subplot(221), plt.title("Marr-Hildreth (sigma=0.5)"), plt.imshow(imgMH5, cmap='gray'), plt.axis('off')
plt.subplot(222), plt.title("Marr-Hildreth (sigma=3)"), plt.imshow(imgLoG, cmap='gray'), plt.axis('off')
plt.subplot(223), plt.title("Zero crossing (size=5)"), plt.imshow(zeroMH5, cmap='gray'), plt.axis('off')
plt.subplot(224), plt.title("Zero crossing (size=19)"), plt.imshow(zeroCrossing, cmap='gray'), plt.axis('off')
plt.tight_layout()
plt.show()

DoG算子

DoG算子是对LoG算子的简化。

示例程序:

"""
DoG边缘检测算子
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal

img = cv2.imread("../img/lena.jpg", flags=0)


# 高斯核低通滤波器,sigmaY 缺省时 sigmaY=sigmaX
kSize = (5, 5)
imgGaussBlur1 = cv2.GaussianBlur(img, (5, 5), sigmaX=1.0)  # sigma=1.0
imgGaussBlur2 = cv2.GaussianBlur(img, (5, 5), sigmaX=2.0)  # sigma=2.0
imgGaussBlur3 = cv2.GaussianBlur(img, (5, 5), sigmaX=4.0)  # sigma=4.0
imgGaussBlur4 = cv2.GaussianBlur(img, (5, 5), sigmaX=16.0)  # sigma=16.0

# 高斯差分算子 (Difference of Gaussian)
imgDoG1 = imgGaussBlur2 - imgGaussBlur1  # sigma=1.0,2.0
imgDoG2 = imgGaussBlur3 - imgGaussBlur2  # sigma=2.0,4.0
imgDoG3 = imgGaussBlur4 - imgGaussBlur3  # sigma=4.0,16.0

plt.figure(figsize=(10, 6))
plt.subplot(231), plt.title("GaussBlur (sigma=2.0)"), plt.imshow(imgGaussBlur2, cmap='gray'), plt.axis('off')
plt.subplot(232), plt.title("GaussBlur (sigma=4.0)"), plt.imshow(imgGaussBlur3, cmap='gray'), plt.axis('off')
plt.subplot(233), plt.title("GaussBlur (sigma=16.)"), plt.imshow(imgGaussBlur4, cmap='gray'), plt.axis('off')
plt.subplot(234), plt.title("DoG (sigma=1.0,2.0)"), plt.imshow(imgDoG1, cmap='gray'), plt.axis('off')
plt.subplot(235), plt.title("DoG (sigma=2.0,4.0)"), plt.imshow(imgDoG2, cmap='gray'), plt.axis('off')
plt.subplot(236), plt.title("DoG (sigma=4.0,16.)"), plt.imshow(imgDoG3, cmap='gray'), plt.axis('off')
plt.tight_layout()
plt.show()

Canny算子

Canny算子执行的基本步骤为:
(1)使用高斯滤波对图像进行平滑;
(2)用一阶有限差分计算梯度幅值和方向;
(3)对梯度幅值进行非极大值抑制(NMS);
(4)用双阈值处理和连通性分析来检测和连接边缘

OpenCV提供了函数cv.Canny实现Canny边缘检测算子。

cv.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]]) → edges

参数说明:

  • image:输入图像,8-bit 灰度图像,不适用彩色图像
  • edges:输出边缘图像,8-bit 单通道图像,大小与输入图像相同
  • threshold1:第一阈值 TL
  • threshold2:第二阈值 TH
  • apertureSize:Sobel 卷积核的孔径,可选项,默认值 3
  • L2gradient: 计算图像梯度幅值 标志符,默认值为 True 表示 L2 法,False 表示 L1 法

示例程序:

"""
Canny边缘检测算子
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)

# 高斯核低通滤波器,sigmaY 缺省时 sigmaY=sigmaX
kSize = (5, 5)
imgGauss1 = cv2.GaussianBlur(img, kSize, sigmaX=1.0)  # sigma=1.0
imgGauss2 = cv2.GaussianBlur(img, kSize, sigmaX=10.0)  # sigma=2.0

# 高斯差分算子 (Difference of Gaussian)
imgDoG = imgGauss2 - imgGauss1  # sigma=1.0, 10.0

# Canny 边缘检测, kSize 为高斯核大小,t1,t2为阈值大小
t1, t2 = 50, 150
imgCanny = cv2.Canny(imgGauss1, t1, t2)

plt.figure(figsize=(10, 6))
plt.subplot(131), plt.title("Origin"), plt.imshow(img, cmap='gray'), plt.axis('off')
plt.subplot数字图像处理边缘检测与图像分割

用matlab如何通过图像分割来检测边界

数字图像处理帧差法与Kirsch边缘检测实现运动目标识别与分割

计算机视觉图像分割与特征提取——基于LogCanny的边缘检测

图像分割基于matlab GUI医学图像均值聚类+OUST+区域生长法图像分割含Matlab源码 2210期

图像分割基于matlab GUI医学图像均值聚类+OUST+区域生长法图像分割含Matlab源码 2210期