图像二值化处理

二值化是常用的图像处理手段之一,通过二值化处理我们可以突出图像中感兴趣的部分,过滤掉多余的部分(比如噪音)。OpenCV 中二值化处理主要是调用这两个函数:

下面对不同的二值化方法进行详细说明。注意,下面的讨论都是对灰度图进行二值化处理,但这种方法可以很容易地移植到多通道图像上。

简单二值化

最简单的二值化是这个样子的:对每一个像素点采用相同的二值化阈值,当像素值小于这个阈值时,像素值设为0,相反,设为255。这时,我们调用cv.threshold这个方法,它总共接受4个输入:第一个是要处理的图像;第二个是二值化阈值;第三个是图像的最大值,一般是255;第四个是二值化的类型。第四个输入有下面五个选择

  • cv.THRESH_BINARY
  • cv.THRESH_BINARY_INV
  • cv.THRESH_TRUNC
  • cv.THRESH_TOZERO
  • cv.THRESH_TOZERO_INV

对于这五种选择的差别详见OpenCV文档

代码例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('gradient.png',0)
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
ret,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 xrange(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()

代码结果如下:



自适应二值化(Adaptive Threshold)

在简单二值化处理中,我们对整张图片采用了一样的阈值,但很多时候,我们会想对图片中不同的地方采用不同的阈值。比如一张图片不同区域的光照条件不一样,这时我们就会用到自适应二值化(cv.adaptiveThreshold)。在这种方法中,算法基于一个像素点周围的区域决定这个像素点的二值化阈值。除了简单二值化中提到的输入参数,cv.adaptiveThreshold还有自己的三个参数:

  1. adaptiveMethod 告诉函数该使用哪一种自适应方法:
    • cv.ADAPTIVE_THRESH_MEAN_C:阈值是周围区域的平均值减去一个常量 C
    • cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是周围区域的高斯平均值减去一个常量 C
  2. block 周围区域的大小
  3. C 减去的常量

代码例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('sudoku.png',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 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 Binarization)

别猜了,大津是一个日本名字。大津二值化是由大津展之发明的,所以就用他的名字命名了。和简单二值化相似,大津二值化也是一个全局二值化的方法,也就是对整张图片采用同一样的阈值。但和简单二值化不同的是,大津二值化不用人为地提供一个阈值,它根据整张图片的像素点的直方图分布计算合适的阈值。

这样说可能比较抽象,想象一张图片,它由两种不同的像素点组成,比如说0和128。相应的,它的像素点直方图只有两个峰值,分别位于0和128。一个好的阈值应该在0和128的中间,也就是64,这样就可以把这张图片上的像素点分开了。相似的,大津二值化先计算一个这样的合适的阈值,然后用这个阈值对整张照片进行二值化。

由于是全局二值化,我们还是使用cv.threshold()这个方法,同时我们需要在最后一个输入参数中添加cv.THRESH_OTSU。输入的阈值参数将会被忽略,取而代之的是大津二值化计算出来的阈值。

代码例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('noisy2.png',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 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()

代码结果如下: