OpenCV图像处理基础(二)

412 阅读7分钟

1、图像梯度

图像梯度反映了图像亮度变化的强度与方向,是图像边缘检测的基础。OpenCV 提供了多种计算图像梯度的算子,例如 Sobel 算子、Scharr 算子和 Laplacian 算子。

1.1 Sobel算子

原理:  Sobel 算子利用像素点上下、左右邻点的灰度加权差,在边缘处达到极值来检测边缘。 image.png 从上面的计算公式可以看出,Gx是的计算是右边减右左边,Gy的计算是下边减上边

优点:

  • 能够提供较为精确的边缘方向信息:
    Sobel 算子通过计算图像在水平和垂直方向上的梯度,可以确定边缘的方向(例如通过 arctan(sobely / sobelx) 计算梯度方向)。
  • 对噪声具有一定的平滑作用:
    Sobel 算子在计算梯度时,使用了高斯平滑(通过加权平均),因此对噪声有一定的抑制作用。

缺点:

  • 边缘定位精度不够高:
    由于 Sobel 算子使用了高斯平滑,虽然抑制了噪声,但也导致边缘的定位不够精确,边缘可能会变得模糊。
  • 对细边缘和弱边缘的检测效果较差:
    由于 Sobel 算子的梯度计算是基于局部区域的加权平均,细边缘和弱边缘可能会被忽略或削弱。

因此,Sobel 算子通常用于对边缘定位精度要求不高的场景,或者作为边缘检测的预处理步骤。

image.png

import cv2


def cv_show(img, name):
    cv2.imshow(name, img)
    cv2.waitKey()
    cv2.destroyAllWindows()


img = cv2.imread("test.png")
# cv2.CV_64F能够位扩展,支持负数
# 这里只计算x,所以dx是1,dy是0
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
# 取绝对值
sobelx = cv2.convertScaleAbs(sobelx)
cv_show(sobelx, "sobelx")
# 计算y
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobely = cv2.convertScaleAbs(sobely)
cv_show(sobely, "sobely")

# 分别计算x,y,再求和
# sum = alpha * x + beta * y + gamma
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5,0)
cv_show(sobelxy, "sobelxy")
# 不建议直接计算,效果不好

1.2 Scharr算子

和Sobel算子的计算方法一致,只是求和的各数值系数变大了,因此对于结果的差异更明显了

image.png

import cv2


def cv_show(img, name):
    cv2.imshow(name, img)
    cv2.waitKey()
    cv2.destroyAllWindows()


img = cv2.imread("test.png")

scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharrx = cv2.convertScaleAbs(scharrx)

scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharry = cv2.convertScaleAbs(scharry)

# 分别计算x,y,再求和
# sum = alpha * x + beta * y + gamma
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5,0)
cv_show(scharrxy, "sobelxy")

1.3 Laplacian算子

原理:  Laplacian 算子是一种二阶微分算子,用于计算图像的二阶导数。二阶导是一阶导的变化率,所以对变化更敏感,同时对噪音点也更敏感,拉普拉斯算子通常需要跟其它方法组合使用,不会单独使用。

image.png

特点:

  • 对噪声敏感。
  • 能够检测出图像中的细节和边缘。
import cv2
"""
拉普拉斯算子没有x,y的概念是因为根据公式,它是计算3X3的格子中中间点与周围点的差异,
不需要再单独计算上、下、左、右
"""

def cv_show(img, name):
    cv2.imshow(name, img)
    cv2.waitKey()
    cv2.destroyAllWindows()


img = cv2.imread("test.png")

laplacian = cv2.Laplacian(img, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
cv_show(laplacian, "laplacian")

2. 图像滤波

图像滤波是图像处理中常用的技术,用于去除图像中的噪声、平滑图像、增强图像细节等。OpenCV 提供了多种图像滤波方法,例如均值滤波、高斯滤波、中值滤波等。

2.1 均值滤波

原理:
用图像中像素点邻域内所有像素的平均值代替该像素点的值。

特点:

  • 算法简单,计算速度快。
  • 对随机噪声(如椒盐噪声)有一定的去噪效果。
  • 会使图像变得模糊,尤其是边缘部分。
  • 对高斯噪声的去噪效果不如高斯滤波。

适用场景:

  • 需要快速去噪且对图像细节要求不高的场景。
  • 处理椒盐噪声等随机噪声。

2.2 高斯滤波

原理:
用高斯函数计算邻域内像素的加权平均值,权重随着距离中心像素的距离增加而减小。

特点:

  • 对高斯噪声有较好的去噪效果。
  • 比均值滤波更好地保留图像边缘信息。
  • 计算量略大于均值滤波。

适用场景:

  • 需要去除高斯噪声且保留图像边缘信息的场景。
  • 对图像细节要求较高的场景。

2.3 中值滤波

原理

中值滤波的原理非常简单:

  1. 选择一个滑动窗口(通常是正方形或圆形)在图像上移动。
  2. 对窗口内的所有像素值进行排序。
  3. 取排序后的中间值(中值)作为当前像素的新值。
特点
  • 对椒盐噪声有极好的去噪效果:
    椒盐噪声是一种随机出现的黑白像素点,中值滤波通过取中值的方式能够有效去除这些异常值。
  • 能够较好地保留图像边缘信息:
    由于中值滤波是基于排序的非线性操作,它不会像均值滤波那样使边缘变得模糊。
  • 对高斯噪声的去噪效果一般:
    中值滤波主要针对椒盐噪声,对高斯噪声的去噪效果不如高斯滤波。
  • 计算量较大:
    中值滤波需要对邻域内的像素进行排序,因此计算量比均值滤波和高斯滤波更大。
适用场景
  • 去除图像中的椒盐噪声。
  • 需要保留图像边缘信息的场景。
  • 对高斯噪声的去噪要求不高的场景。
import cv2

img = cv2.imread("test.png")

# 均值滤波,简单的平均卷积操作
blur = cv2.blur(img, (3, 3))
cv2.imshow("blur", blur)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 方框滤波,基本和均值一样,可以选择归一化
box = cv2.boxFilter(img, -1, (3, 3), normalize=True)  # normalize为True后和均值滤波一样,
# 如果normalize为False,会发生越界的情况,图像显示的现象为过曝
cv2.imshow("box", box)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 高斯滤波,高斯分布,越靠近中间的权重越大,高斯滤波对高斯噪声效果好
gaussian = cv2.GaussianBlur(img, (5, 5), 1)
cv2.imshow("gaussian", gaussian)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 中值滤波,在框框内的数,从小到大排序,取中间的值为结果值
median = cv2.medianBlur(img, 5)
cv2.imshow("median", median)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.5 总结

image.png 从左往右依次是经过均值滤波、高斯滤波、中值滤波的结果,从图像的显示来看,中值滤波的效果最好。

滤波类型原理去椒盐噪声去高斯噪声边缘保留计算速度适用场景
均值滤波邻域像素的平均值一般一般边缘模糊快速去噪,对细节要求不高
高斯滤波邻域像素的加权平均值(高斯权重)一般边缘保留较好较慢去除高斯噪声,保留边缘信息
中值滤波邻域像素的中值非常好一般边缘保留较好较慢去除椒盐噪声,保留边缘信息

为什么中值滤波对椒盐噪声更有效?

椒盐噪声是一种随机出现的极亮或极暗的像素点,这些像素点的值通常与周围像素的值差异很大。中值滤波通过取邻域像素的中值来替代当前像素值,能够有效过滤掉这些异常值,而不会显著影响图像的其他部分。 相比之下,均值滤波和高斯滤波会将这些异常值纳入计算,导致去噪效果不理想。

总结

  • 中值滤波是去除椒盐噪声的最佳选择,能够有效去除噪声并保留图像边缘信息。
  • 对于高斯噪声,高斯滤波是更好的选择,高斯滤波虽然对高斯噪声有较好的去噪效果,但也会使图像变得略微模糊。。
  • 如果需要快速去噪且对边缘保留要求不高,可以使用均值滤波

4.边缘检测算法

4.1 Canny边缘检测

image.png 思想是搜素局部极大值,抑制非极大值元素,检测的过程中在同一目标的位置上会产生大量的候选边界,这些候选边界相互之间可能会有重叠,此时我们需要利用非极大值抑制找到最佳的目标边界,消除冗余的边界。

import cv2

# 读取图像
img = cv2.imread('image.jpg', 0)  # 以灰度模式读取图像

# Canny 边缘检测
edges = cv2.Canny(img, 100, 200)  # 100 和 200 分别表示低阈值和高阈值

# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()