图像阈值

优质
小牛编辑
133浏览
2023-12-01

图像阈值

目标

  • 在本教程中,您将学习简单阈值处理,自适应阈值处理,Otsu阈值处理等。
  • 您将学习以下函数:cv2.threshold, cv2.adaptiveThreshold等。

一、单的阈值

在这里,事情是直截了当的。如果像素值大于阈值,则为其分配一个值(可以是白色),否则为其分配另一个值(可以是黑色)。使用的函数是cv2.threshold。第一个参数是源图像,它应该是灰度图像。第二个参数是用于对像素值进行分类的阈值。第三个参数是maxVal,它表示如果像素值大于(有时小于)阈值则要给出的值。OpenCV提供不同类型的阈值,它由函数的第四个参数决定。不同的类型是:

  • cv2.THRESH_BINARY
  • cv2.THRESH_BINARY_INV
  • cv2.THRESH_TRUNC
  • cv2.THRESH_TOZERO
  • cv2.THRESH_TOZERO_INV

文档清楚地解释了每种类型的含义。请查看文档。

获得两个输出。第一个是retval,将在后面解释。第二个输出是我们的阈值图像。

代码:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('gradient.png',0)
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 Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in xrange(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])

plt.show()

注意

为了绘制多个图像,我们使用了plt.subplot()函数。请查看Matplotlib文档以获取更多详细信息。

结果如下:

简单的阈值处理

二、自适应阈值

在上一节中,我们使用全局值作为阈值。但在图像在不同区域具有不同照明条件的所有条件下可能并不好。在那种情况下,我们进行自适应阈值处理。在此,算法计算图像的小区域的阈值。因此,我们为同一图像的不同区域获得不同的阈值,并且它为具有不同照明的图像提供了更好的结果。

它有三个特殊输入参数和一个输出参数。

自适应方法 - 决定如何计算阈值。

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

Block Size - 它决定了邻域的大小。

C - 它只是从计算的平均值或加权平均值中减去常数。

下面的代码比较了具有不同照明的图像的全局阈值处理和自适应阈值处理:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('dave.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 Image', 'Global Thresholding (v = 127)',
            'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]

for i in xrange(4):
    plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

结果:

自适应阈值处理

三、Otsu’s 二值化

在第一节中,我告诉你第二个参数是retVal。当我们选择Otsu’s二值化时,它就会被使用。那是什么?

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

为此,使用了我们的cv2.threshold()函数,但传递了一个额外的标志cv2.THRESH_OTSU。对于阈值,只需传递零。然后算法找到最佳阈值并返回第二个输出,retVal。如果未使用Otsu阈值,则retVal与您使用的阈值相同。

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

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('noisy2.png',0)

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

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

# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(img,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.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 xrange(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()

结果:

Otsu’s阈值

四、Otsu’s二值化如何运作?

本节演示了Otsu二值化的Python实现,以展示它的实际工作原理。如果您不感兴趣,可以跳过这个。

由于我们正在使用双峰图像,因此Otsu的算法试图找到一个阈值(t),它最小化了由关系给出的加权类内方差:

Otsu’s阈值

哪里

Otsu’s阈值

它实际上找到了一个位于两个峰值之间的t值,这样两个类别的差异最小。它可以简单地在Python中实现,如下所示:

img = cv2.imread('noisy2.png',0)
blur = cv2.GaussianBlur(img,(5,5),0)

# find normalized_histogram, and its cumulative distribution function
hist = cv2.calcHist([blur],[0],None,[256],[0,256])
hist_norm = hist.ravel()/hist.max()
Q = hist_norm.cumsum()

bins = np.arange(256)

fn_min = np.inf
thresh = -1

for i in xrange(1,256):
    p1,p2 = np.hsplit(hist_norm,[i]) # probabilities
    q1,q2 = Q[i],Q[255]-Q[i] # cum sum of classes
    b1,b2 = np.hsplit(bins,[i]) # weights

    # finding means and variances
    m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2
    v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2

    # calculates the minimization function
    fn = v1*q1 + v2*q2
    if fn < fn_min:
        fn_min = fn
        thresh = i

# find otsu's threshold value with OpenCV function
ret, otsu = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print thresh,ret

(这里的一些功能可能是新的,但我们将在接下来的章节中介绍它们)

五、其他资源

  • 数字图像处理,Rafael C. Gonzalez

六、练习

Otsu的二值化有一些优化可用。您可以搜索并实施它。