opencv-python 4.3. 图像阈值

发布时间 2023-03-28 16:03:36作者: 一枚码农

简单阈值处理

这种阈值处理的方法是简单易懂的。如果像素值大于阈值,则为其分配一个值(可以是白色),否则为其分配另一个值(可以是黑色)。使用的函数是cv.threshold。函数第一个参数是源图像,它应该是灰度图像。第二个参数是用于对像素值进行分类的阈值。第三个参数是maxVal,它表示如果像素值大于(有时小于)阈值则要给出的值。OpenCV提供不同类型的阈值,由函数的第四个参数决定。不同的类型有:

  • cv.THRESH_BINARY - 二进制阈值。把亮的处理成白色,暗的处理成黑色
  • cv.THRESH_BINARY_INV - 反二进制阈值。把亮的处理成黑色,暗的处理成白色
  • cv.THRESH_TRUNC - 截断阈值。亮的不能太亮,有上限,暗的不变
  • cv.THRESH_TOZERO threshold - 阈值化为0,比较亮的部分不变,比较暗的部分处理成黑色为0
  • cv.THRESH_TOZERO_INV threshold - 反阈值化为0,把比较亮的部分处理成0成黑色,小于等于阈值的像素点不变

函数将获得两个输出。第一个是retavl,它是用来进行Otsu's二值化,将在后面解释它的作用。第二个输出是我们的阈值图像。

import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\bg.jpg', 0)
rows, cols = img.shape

ret1, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
ret2, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
ret3, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
ret4, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
ret5, thresh5 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO_INV)

titles = ['Original Image', '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], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])

plt.show()

image

自适应阈值处理

在上面,我们使用全局值作为阈值,但在图像在不同区域具有不同照明条件的所有条件下可能并不好。在那种情况下,我们进行自适应阈值处理,算法计算图像的小区域的阈值,所以我们对同一幅图像的不同区域给出不同的阈值,这给我们在不同光照下的图像提供了更好的结果。
这种阈值处理方法有三个指定输入参数和一个输出参数。
Adaptive Method - 自适应方法,决定如何计算阈值。

  • cv.ADAPTIVE_THRESH_MEAN_C:阈值是邻域的平均值。
  • cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的加权和,其中权重是高斯窗口。

Block Size - 邻域大小,它决定了阈值区域的大小。
C - 它只是从计算的平均值或加权平均值中减去的常数。

import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\bg2.jpg', 0)

# 使用中值滤波模糊图像 中值滤波将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的中值代替
img = cv.medianBlur(img, 5)

ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 11, 2)
th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 11, 2)

titles = ['Original Image', '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()

image

Otsu's 二值化

在第一节中,我只告诉你另一个参数是retVal,但没告诉你它的作用。其实,它是用来进行Otsu's二值化。

在全局阈值处理中,我们使用任意值作为阈值,那么,我们如何知道我们选择的值是好还是不好?答案是,试错法。但如果是双峰图像(简单来说,双峰图像是直方图有两个峰值的图像)我们可以将这些峰值中间的值近似作为阈值,这就是Otsu二值化的作用。简单来说,它会根据双峰图像的图像直方图自动计算阈值。(对于非双峰图像,二值化不准确。)

为此,使用了我们的cv.threshold()函数,但是需要多传递一个参数cv.THRESH_OTSU。这时要吧阈值设为零。然后算法找到最佳阈值并返回第二个输出retVal。如果未使用Otsu二值化,则retVal与你设定的阈值相同。

请查看以下示例。输入图像是嘈杂的图像。在第一种情况下,我将全局阈值应用为值127。在第二种情况下,我直接应用了Otsu的二值化。在第三种情况下,我使用5x5高斯卷积核过滤图像以消除噪声,然后应用Otsu阈值处理。来看看噪声过滤如何改善结果。

import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\noise.jpg', 0)

# global thresholding
ret1, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)

# Otsu's thresholding
ret2, th2 = cv.threshold(img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

# Otsu's thresholding after Gaussian filtering
blur = cv.GaussianBlur(img, (5, 5), 0)
ret3, th3 = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

# plot all the images and their histograms
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.hist 将一个大区间划分为等间隔的小区间,并统计每个区间上样本出现的频数之和
    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()

image