(三)OpenCV中的图像处理之图像阈值以及图像平滑
注释:本文翻译自OpenCV3.0.0 document->OpenCV-Python Tutorials,包括对原文档种错误代码的纠正
3.3 图像阈值
3.3.1 目标:
- 在这章,会学到简单的阈值、自适应阈值、大津阈值等
- 学会这些函数:cv2.threshold()、cv2.adaptiveThreshold()等
3.3.2 简单的阈值
如果像素值大于阈值,则分配一个值(可以是白色),否则分配另一个值(可以是黑色)。所使用的函数是cv2.threshold。第一个参数是源图像,它应该是灰度图像。第二个参数是用于对像素值进行分类的阈值。第三个参数是maxVal,它表示如果像素值大于(有时小于)阈值时给出的值。OpenCV中提供不同风格的阈值,有函数的第四个参数决定。不同的类型有:
- cv2.THRESH_BINARY
- cv2.THRESH_BINART_INV
- cv2.THRESH_TRUNC
- cv2.THRESH_TOZREO
- cv2.THRESH_TOZERO_INV
详细可参考文档说明。
该函数有两个输出,一个是稍后解释的回退,另一个是我们的阈值图像。
下面的示例展示了几种参数下的阈值:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('1.jpg')
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i])
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
对比如下:
3.3.3 自适应阈值
在上一节中,我们使用全局值作为阈值。但在不同区域的图像具有不同的照明条件的所有条件下可能并不好。在这种情况下,应使用自适应阈值。在此,该算法计算图像的小区域的阈值。所以我们得到同一图像不同区域的不同阈值,对于具有不同照明的图像会给我们呈现更好的结果。
自适应阈值方法有三个输入,一个输出:
Adaptive Method:
- cv2.ADAPTIVE_THRESH_MEAN_C:阈值是领域的平均值
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是高斯权重窗口的领域值的加权和
- Block Size:定义了领域的大小
- C:它只是一个常数,它从平均值或加权平均值中减去。
下面的代码比较了不同照明度图像的全局阈值和自适应阈值:
'''
自适应阈值:
1.不同区域的图像在不同照明条件下使用全局阈值可能并不好,这种情况下使用自使用阈值;
2.自适应阈值有三个输入一个输出:
cv2.ADAPTIVE_THRESH_MEAN_C:阈值是领域的平均值
cv2.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是高斯权重窗口的领域值的加权和
'''
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('1.jpg', 0)
img = cv2.medianBlur(img, 5)
# 全局阈值
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 自适应平均阈值
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
# 自适应高斯阈值
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
titles = ['Original', 'Global Thresholding(v=127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
结果如下:
3.3.4 Otsu’s Binarization(大津算法的二值化)
在全局阈值中,我们使用任意值作为阈。那么我们怎么知道我们选择的值是好的还是不好的呢?答案是:试错法。但是考虑到一个双峰图像,我们大概可以计算这些峰值的中间值作为阈值。这就是Otsu二值化。简单来说,它可以自动计算双峰图像的图像直方图中的阈值。(对于不是双峰的图像,二值化将不确定)。
为此,我们使用cv2.threshold()函数,但通过额外标志cv2.THRESH_OTSU。对于阈值,只需传入0,然后算法自动找到最佳阈值,并将它作为第二个输出值retVal返回。如果不使用Otsu阈值,retVal与传入的阈值相同。
查看下面的代码:
'''
大津算法的二值化:
1.可以计算双峰图像直方图中的阈值(对于不是双峰的图像,二值化将不确定)
2.对于传入的阈值不能确定其好坏,所以给cv2.threshole()函数传入cv2.THRESH_OTSU,
对于阈值只需传入0,然后算法自动找到最佳阈值
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('1.jpg', 0)
# 全局阈值
ret1, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# Otsu's 二值化阈值
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 在高斯滤波后的Otsu's二值化阈值
# 用5*5高斯内核过滤掉图像中的噪声
blur = cv2.GaussianBlur(img, (5, 5), 0)
ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 绘出以上所有图像以及它们的直方图
images = [img, 0, th1,
img, 0, th2,
blur, 0, th3]
titles = ['Original Noisy Image', 'Histogram', 'Global Thresholding[v=127]',
'Original Noisy Image', 'Histogram', "Otsu's Thresholding",
'Gaussian filtered Image', 'Histogram', "otsu's Thresholding"]
for i in range(3):
plt.subplot(3, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray')
plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])
plt.subplot(3, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([])
plt.subplot(3, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray')
plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([])
plt.show()
结果如下:
3.4图像平滑
3.4.1 目标:
- 学会用低通滤波模糊图像
- 对图像应用定制过滤器(2D卷积)
3.4.2 2维卷积(图像过滤)
像一维信号一样,图像也可以用各种低通滤波(low-passfilters,LPF),高通滤波(high-passfilters,HPF)等进行滤波。LPF有助于去除噪声、模糊图像等。HPF滤波器有助于找到图像边缘。
OpenCV中提供cv2.filter2D()来将内核和图像进行卷积。例如,我们会尝试对图像进行均值滤波。一个5*5的均值滤波器如下:
操作是这样的:将这个内核保持在一个像素之上,添加这个内核中的所有25个像素,取其平均值,并用新的平均值替换中心像素值。它继续对图像的所有像素进行此操作。
3.4.3 图像模糊(图像平滑)
图像模糊是通过低通滤波器内核卷积图像来实现的。它有助于去除噪音,它实际上是从图像中去除了高频内容(例如:噪声、边缘)。所以,边缘会在这个操作中被模糊(当然也有模糊技术不回模糊边缘的)。OpenCV重要提供四种模糊技术:均值滤波、高斯滤波、中值滤波、双边滤波
1.均值滤波
Note:如果不想使用标准化的框过滤器,请使用cv2.boxFilter().将参数normalize=False传递给函数。
这是通过使用归一化的盒式过滤器卷积图像来完成的。它只需要将内核区域下的所有像素的平均值取代中心像素。这由函数cv2.blur()和cv2.boxFilter()完成。可以查看文档了解关于更多内核的信息。我们应该指定内核的宽度和高度,3*3标准化的盒式过滤器将如下所示:
2.高斯滤波
在这里,使用高斯核,而不是盒式滤波核。它使用函数cv2.GaussianBlur()完成。我们应该指定内核的宽度和高度(应为正数和奇数)我们还应该指定X和Y方向上的标准偏差,sigmaX和sigmaY,如果仅指定sigmaX,那么sigmaY和sigmaX相同。如果两者均为零,则由内核大小计算。高斯模糊在图像中去除高斯噪声非常有效。
如果想创建一个高斯内核,用函数cv2.getGaussiankernel()
高斯滤波函数调用方式:
blur = cv2.GaussianBlur(img,(5,5),0)
3.中值滤波
中值滤波中,函数cv2.medianBlur()获取内核区域下所有像素的中值,中心元素被替换成该中值。这对图像中的椒盐噪声是非常有效的。有趣的是以上滤波中,中心元素是新计算的值,其可以是图像中的像素值或新值。但在中值滤波,中心元素总被图像中的某些像素值所取代。它有效地降低了噪声,其内核大小应为正奇数。
median = cv2.medianBlur(img,5)
4.双边滤波
Cv2.bilaterFilter()在保持锐利边缘的同时,非常有效地去除噪声,但与其他过滤器相比,操作比较慢。我们看到,高斯滤波利用像素周围的一个邻域,并找到其高斯平均值。这个高斯滤波器是单独的空间的函数,也就是在过滤是考虑附近的像素,不会考虑像素是否具有相同的强度,也不会考虑像素是否是边缘像素,所以高斯滤波也模糊边缘。
双边滤波器还在空间中采用高斯滤波器,但还有一个高斯滤波器,它是像素差异的函数。空间的高斯函数确保只有附近的像素被认为是模糊的,而强度差的高斯函数才能确保只有具有与中心像素强度相似的像素被认为是模糊的。因此,它保留边缘,因为边缘的像素将具有较大的强度变化。
以下示例显示使用双边筛选器。
blur = cv2.bilateralFilter(img,9,75,75)
示例代码一:5*5均值滤波栗子
# 5*5的均值滤波例子
'''
图像过滤:低通滤波(LPF),高通滤波(HPF),低通滤波有助于去除图像噪声,高通滤波有助于找到图像边缘
OpenCV中cv2.filter2D()来将内核和图像进行卷积,内核就是一个滤波器
均值滤波:示例代码如下
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('1.jpg')
# 构造一个5*5的均值滤波器
kernel = np.ones((5, 5), np.float32) / 25
dst = cv2.filter2D(img, -1, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original Image')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(dst), plt.title('Averaging Filter')
plt.xticks([]), plt.yticks([])
plt.show()
显示效果如下:
第二个栗子:图像平滑
'''
图像模糊(图像平滑):
图像模糊是通过低通滤波内核卷积来实现的(实际上是从图像中去除了高频内容)
OpenCV中提供四种模糊技术:均值滤波、高斯滤波、中值滤波、双边滤波
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('1.jpg')
# 均值滤波:使用盒式滤波盒,指定内核宽高:5*5
averageBlur = cv2.blur(img, (5, 5))
# 高斯滤波:不用盒式滤波盒,使用函数cv2.GaussianBlur()函数完成,
# 如果想创建一个高斯内核使用函数cv2.getGaussianKernel()
gaussianBlur = cv2.GaussianBlur(img, (5, 5), 0)
# 中值滤波:函数cv2.medianBlur()获取内核区域下所有元素的中值,中心元素被替换为中值
medianBlur = cv2.medianBlur(img, 5)
# 双边滤波:cv2.bilaterFilter()在保持锐利边缘的同时,非常有效地去除噪声
# 但与其它过滤器相比,操作较慢,
bilateBlur = cv2.bilateralFilter(img, 9, 75, 75)
titles = ['Original', 'Average', 'Gaussian', 'Median', 'Bilate']
images = [img, averageBlur, gaussianBlur, medianBlur, bilateBlur]
for i in range(5):
plt.subplot(2, 3, i + 1), plt.imshow(images[i]), plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
结果如下: