目录
前言
一、图像阈值
1.1 简单的阈值法
1.2 自适应阈值
二、平滑图像
2.1 二维卷积(图像滤波)
2.2 图像模糊
2.2.1均值模糊
2.2.2高斯模糊
2.2.3 中值滤波
2.2.4 双边滤波
三、形态转换
1、腐蚀
2、膨胀
3、开运算
4、闭运算
四、图像梯度
Sobel 和 Scharr 微分
参考文
前言
在本博文中,进行图像阈值、平滑图像、形态转换、图像梯度的学习以及介绍。
一、图像阈值
在本部分中,
- 你会学到简单阈值法,自适应阈值法,以及 Otsu 阈值法(俗称大津法)等。
- 你会学到如下函数:cv.threshold,cv.adaptiveThreshold 等。
1.1 简单的阈值法
此方法是直截了当的。如果像素值大于阈值,则会被赋为一个值(可能为白色),否则会赋为另一个值(可能为黑色)。使用的函数是 cv.threshold。第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是 maxval,它表示像素值大于(有时小于)阈值时要给定的值。opencv 提供了不同类型的阈值,由函数的第四个参数决定。不同的类型有:
- cv.THRESH_BINARY
- cv.THRESH_BINARY_INV
- cv.THRESH_TRUNC
- cv.THRESH_TOZERO
- cv.THRESH_TOZERO_INV
文档清楚地解释了每种类型的含义。请查看文档。
获得两个输出。第一个是 retval,稍后将解释。第二个输出是我们的阈值图像。
下面是一个使用 OpenCV 的 cv.threshold()
函数进行图像二值化的示例,说明如何应用不同的阈值类型来处理图像。
import cv2
import numpy as np
# 读取图像,并将其转换为灰度图
img = cv2.imread('images/demo2.png', 0)
# 定义阈值和最大值
threshold_value = 127
max_value = 255
# 应用不同类型的阈值操作
_, thresh_binary = cv2.threshold(img, threshold_value, max_value, cv2.THRESH_BINARY)
_, thresh_binary_inv = cv2.threshold(img, threshold_value, max_value, cv2.THRESH_BINARY_INV)
_, thresh_trunc = cv2.threshold(img, threshold_value, max_value, cv2.THRESH_TRUNC)
_, thresh_tozero = cv2.threshold(img, threshold_value, max_value, cv2.THRESH_TOZERO)
_, thresh_tozero_inv = cv2.threshold(img, threshold_value, max_value, cv2.THRESH_TOZERO_INV)
# 显示结果
cv2.imshow('Original Image', img)
cv2.imshow('Binary Threshold', thresh_binary)
cv2.imshow('Binary Inverse Threshold', thresh_binary_inv)
cv2.imshow('Truncate Threshold', thresh_trunc)
cv2.imshow('ToZero Threshold', thresh_tozero)
cv2.imshow('ToZero Inverse Threshold', thresh_tozero_inv)
# 等待按键输入并关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
-
输入图像:代码读取并将输入图像转换为灰度图像。
-
阈值参数:
threshold_value = 127
,像素值大于127的地方将根据选择的阈值类型做相应处理,max_value = 255
表示阈值操作后的最大值。 -
阈值操作:
-
cv.THRESH_BINARY
:如果像素值大于阈值,则设置为max_value
,否则为 0。 -
cv.THRESH_BINARY_INV
:与THRESH_BINARY
相反,像素值大于阈值时为 0,否则为max_value
。 -
cv.THRESH_TRUNC
:像素值大于阈值时设置为阈值,其他值保持不变。 -
cv.THRESH_TOZERO
:大于阈值的像素值保持不变,小于阈值的像素值设为 0。 -
cv.THRESH_TOZERO_INV
:小于阈值的像素值保持不变,大于阈值的像素值设为 0。
-
1.2 自适应阈值
在前一节中,我们使用一个全局变量作为阈值。但在图像在不同区域具有不同照明条件的条件下,这可能不是很好。在这种情况下,我们采用自适应阈值。在此,算法计算图像的一个小区域的阈值。因此,我们得到了同一图像不同区域的不同阈值,对于不同光照下的图像,得到了更好的结果。
它有三个“特殊”输入参数,只有一个输出参数。
Adaptive Method-它决定如何计算阈值。
- cv.ADAPTIVE_THRESH_MEAN_C 阈值是指邻近地区的平均值。
- cv.ADAPTIVE_THRESH_GAUSSIAN_C 阈值是权重为高斯窗的邻域值的加权和。
Block Size-它决定了计算阈值的窗口区域的大小。
C-它只是一个常数,会从平均值或加权平均值中减去该值。
下面的代码比较了具有不同照明的图像的全局阈值和自适应阈值:
import cv2
import numpy as np
# 读取图像,并将其转换为灰度图
img = cv2.imread('images/demo2.png', 0)
# 使用全局阈值
_, global_thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 使用自适应阈值 - 均值方法
adaptive_thresh_mean = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
# 使用自适应阈值 - 高斯方法
adaptive_thresh_gaussian = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
# 显示结果
cv2.imshow('Original Image', img)
cv2.imshow('Global Thresholding', global_thresh)
cv2.imshow('Adaptive Mean Thresholding', adaptive_thresh_mean)
cv2.imshow('Adaptive Gaussian Thresholding', adaptive_thresh_gaussian)
# 等待按键输入并关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
二、平滑图像
2.1 二维卷积(图像滤波)
与一维信号一样,图像也可以通过各种低通滤波器(LPF)、高通滤波器(HPF)等进行过滤。LPF 有助于消除噪音、模糊图像等。HPF 滤波器有助于在图像中找到边缘。
opencv 提供了函数 cv.filter2D(),用于将内核与图像卷积起来。
二维卷积在图像处理中是一种非常基础且重要的操作。通过卷积,我们可以应用不同的滤波器(即内核)对图像进行操作,达到不同的效果。常见的滤波操作包括低通滤波器(LPF)和高通滤波器(HPF)。这些滤波器可以用于去除噪声、模糊图像、增强边缘等。
-
低通滤波器 (LPF):通常用于平滑图像、去噪声。例如:均值滤波、高斯滤波。
-
高通滤波器 (HPF):用于增强图像中的边缘、锐化图像。例如:Sobel 滤波、Laplacian 滤波。
OpenCV 提供的 cv.filter2D()
函数可以实现任意的内核卷积操作。
具体操作说明:
-
cv.filter2D()
函数的参数如下:-
src:输入图像。
-
ddepth:输出图像的深度,通常与输入图像深度相同。
-
kernel:滤波器矩阵,即卷积核(大小一般为奇数,比如 3x3、5x5)。
-
import cv2
import numpy as np
# 读取图像
img = cv2.imread('images/demo2.png')
# 创建一个高通滤波器(用于锐化图像)
kernel = np.array([[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]])
# 应用二维卷积
sharpened_image = cv2.filter2D(img, -1, kernel)
# 显示原始图像和锐化后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Sharpened Image', sharpened_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2 图像模糊
图像模糊是通过将图像与低通滤波核卷积来实现的。它有助于消除噪音。它实际上从图像中删除高频内容(例如:噪声、边缘)。所以在这个操作中边缘有点模糊。(好吧,有一些模糊技术不会使边缘太模糊)。OpenCV 主要提供四种模糊技术。
在图像处理中,模糊(平滑处理)是为了减少图像中的噪声和细节,通常用于预处理或后处理阶段。OpenCV 提供了多种模糊技术,常用的有均值模糊、高斯模糊、中值滤波和双边滤波。每种模糊方法的原理和效果略有不同,下面分别说明并举例。
2.2.1均值模糊
原理:均值模糊是最简单的一种滤波方式。它通过取图像中某个区域的所有像素值的均值,来平滑该区域。这种模糊方法会导致图像细节损失较多。
import cv2
# 读取图像
img = cv2.imread('images/demo2.png')
# 使用均值模糊,窗口大小为 5x5
mean_blur = cv2.blur(img, (5, 5))
# 显示原图像和均值模糊后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Mean Blur', mean_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2.2高斯模糊
原理:高斯模糊使用高斯分布的权重对图像进行平滑。与均值模糊不同,高斯模糊给邻近像素分配不同的权重,中心像素的权重较高,边缘像素的权重较低。这使得高斯模糊在保留图像细节的同时,有效去除噪声。
import cv2
# 读取图像
img = cv2.imread('input_image.jpg')
# 使用高斯模糊,窗口大小为 5x5,标准差为 0
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 0)
# 显示原图像和高斯模糊后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Gaussian Blur', gaussian_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2.3 中值滤波
原理:中值滤波是非线性滤波的一种,它将窗口内的所有像素按大小排序,然后用中值替换中心像素。这种方法特别适合处理椒盐噪声,并且在保留边缘的同时有效去噪。
import cv2
# 读取图像
img = cv2.imread('input_image.jpg')
# 使用中值滤波,窗口大小为 5x5
median_blur = cv2.medianBlur(img, 5)
# 显示原图像和中值滤波后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Median Blur', median_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2.4 双边滤波
原理:双边滤波是一种保边滤波器,它不仅考虑空间距离,还考虑像素值的相似度。双边滤波可以在平滑图像的同时保留边缘信息,因此特别适用于边缘保留的去噪。
import cv2
# 读取图像
img = cv2.imread('input_image.jpg')
# 使用双边滤波,d为滤波器直径,sigmaColor控制颜色邻近值,sigmaSpace控制空间邻近值
bilateral_blur = cv2.bilateralFilter(img, 9, 75, 75)
# 显示原图像和双边滤波后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Bilateral Filter', bilateral_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
三、形态转换
形态学变换是基于图像形状的一些简单操作。它通常在二值图像上执行。它需要两个输入,一个是我们的原始图像,第二个是决定操作性质的结构元素或内核。两个基本的形态学操作是腐蚀和膨胀。接下来如开,闭,梯度等也会介绍。在下图的帮助下,我们将逐一看到它们:
1、腐蚀
腐蚀的基本概念就像土壤侵蚀一样,只侵蚀前景对象的边界(总是尽量保持前景为白色)。那它有什么作用呢?内核在图像中滑动(如二维卷积)。只有当内核下的所有像素都为 1 时,原始图像中的像素(1 或 0)才会被视为 1,否则会被侵蚀(变为零)。
所以根据内核的大小,边界附近的所有像素都将被丢弃。因此,前景对象的厚度或大小在图像中减少或只是白色区域减少。它有助于消除小的白色噪音(如我们在“颜色空间”一章中所看到的),分离两个连接的对象等。
作为一个例子,我将使用一个 5x5 内核,内核元素均为1。让我们看看它是如何工作的:
import cv2
import numpy as np
# 读取二值图像
image = cv2.imread('binary_image.jpg', 0)
# 创建5x5的结构元素(内核)
kernel = np.ones((5, 5), np.uint8)
# 执行腐蚀操作
eroded_image = cv2.erode(image, kernel, iterations=1)
# 显示原始图像和腐蚀后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Eroded Image', eroded_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2、膨胀
它与腐蚀正好相反。这里,如果内核下至少有一个像素为“1”,则像素元素为“1”。所以它会增加图像中的白色区域,或者增加前景对象的大小。通常情况下,在去除噪音的情况下,腐蚀后会膨胀。因为,腐蚀消除了白噪声,但它也缩小了我们的对象。所以我们扩大它。由于噪音消失了,它们不会再回来,但我们的目标区域会增加到腐蚀之前的状态。它还可用于连接对象的断开部分。
# 执行膨胀操作
dilated_image = cv2.dilate(image, kernel, iterations=1)
# 显示膨胀后的图像
cv2.imshow('Dilated Image', dilated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
3、开运算
开只是腐蚀的另一个名称,随后是膨胀。正如我们上面所解释的,它对消除噪音很有用。在这里,我们使用 cv.morphologyEx()。
# 执行开运算
opened_image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
# 显示开运算后的图像
cv2.imshow('Opened Image', opened_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
4、闭运算
关闭与打开相反,膨胀后腐蚀。它在填充前景对象内的小孔或对象上的小黑点时很有用。
# 执行闭运算
closed_image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
# 显示闭运算后的图像
cv2.imshow('Closed Image', closed_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
四、图像梯度
OpenCv 提供三种类型的梯度滤波器或高通滤波器,Sobel、Scharr 和 Laplacian。我们会逐步介绍。
Sobel 和 Scharr 微分
原理
-
Sobel 算子是一种边缘检测算子,它结合了高斯平滑与微分运算,从而提高了对噪声的抵抗力。Sobel 算子通过两个方向(水平和垂直)来计算图像的梯度。
-
Scharr 算子是 Sobel 算子的一个变种,具有更好的旋转不变性和更高的梯度响应。Scharr 算子在某些情况下能够提供更精确的边缘检测,尤其是细节较多的图像。
Sobel 和 Scharr 内核
-
Sobel 内核(3x3):
-
Scharr 内核(3x3):
import cv2
import numpy as np
# 读取图像并转换为灰度图
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Sobel 微分
sobel_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3) # X方向
sobel_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3) # Y方向
sobel_magnitude = cv2.magnitude(sobel_x, sobel_y) # 计算梯度幅值
# Scharr 微分
scharr_x = cv2.Scharr(image, cv2.CV_64F, 1, 0) # X方向
scharr_y = cv2.Scharr(image, cv2.CV_64F, 0, 1) # Y方向
scharr_magnitude = cv2.magnitude(scharr_x, scharr_y) # 计算梯度幅值
# 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Sobel Magnitude', sobel_magnitude)
cv2.imshow('Scharr Magnitude', scharr_magnitude)
cv2.waitKey(0)
cv2.destroyAllWindows()