第六章: 阈值处理
- 为什么要对图像进行阈值处理?
当同一幅图像不同部分具有不同的亮度时,对其进行阈值处理后,我们可以得到一张对比度不同的图片。图像阈值处理也是图像分割操作的一个环节。
一、简单阈值处理(全局阈值处理)
二、自适应阈值处理(采取局部阈值的方式进行处理)
三、Otsu阈值处理(自动寻找最佳的全局阈值)
一、简单阈值处理
- 阈值处理函数:cv2.threshold(src, thresh, maxval, type, dst=None)
Argument:
src: 原图像,就是要进行阈值处理的原图对象。
thresh: 阈值,就是进行分类的阈值。
maxval: 当type指定为THRESH_BINARY或THRESH_BINARY_INV时,才需要设置该值。指的是高于(低于)阈值时赋予的新值。
type: 指定阈值处理的方法,常用的有:
• cv2.THRESH_BINARY(黑白二值)
• cv2.THRESH_BINARY_INV(黑白二值反转)
• cv2.THRESH_TRUNC (截断阈值化处理)
• cv2.THRESH_TOZERO
• cv2.THRESH_TOZERO_INV
dst: 目标图像
该函数有两个返回值,第一个retVal(得到的阈值值,因为有时要自动找阈值),第二个就是阈值化后的图像。# 例6.1 观察cv2.THRESH_BINARY 二值化阈值处理方法的效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = np.random.randint(0, 256, (4,5), np.uint8) t, dst = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) #大于127的像素点都处理成第三个参数值,小于127的像素点都处理成0 lena = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp') t1, dst1 = cv2.threshold(lena, 127, 255, cv2.THRESH_BINARY) plt.subplot(121), plt.imshow(dst) plt.subplot(122), plt.imshow(dst1) plt.show()
说明:
对于一幅色彩均衡的图像,我们一般直接将阈值设为127,这个阈值是经验获得的,设置这个阈值是比较合适的。所以一般我们进行阈值处理的时候都那127这个数试。
但是,当有的图像色彩分布不均衡时,我们再那127这个阈值,效果就会很差,此时我们就要考虑别的阈值处理方法,比如自适应阈值处理方法或者Otsu方法。# 例6.2 观察cv2.THRESH_BINARY_INV 反二值化阈值处理方法的效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = np.random.randint(0, 256, (4,5), np.uint8) t, dst = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) #大于127的像素点都处理成0,小于127的像素点都处理成第三个像素值 lena = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp') t1, dst1 = cv2.threshold(lena, 127, 255, cv2.THRESH_BINARY_INV) plt.subplot(121), plt.imshow(dst) plt.subplot(122), plt.imshow(dst1) plt.show()
# 例6.3 观察cv2.HTRESH_TRUNC 截断阈值处理方法的效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = np.random.randint(0, 256, (4,5), np.uint8) t, dst = cv2.threshold(img, 127, -6, type=cv2.THRESH_TRUNC) #将大于127的像素点都设置为127,将小于等于127的像素点都保持不变。 lena = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp') t1, dst1 = cv2.threshold(lena, 127, 127, cv2.THRESH_TRUNC) #此时第三个参数是没有意义的。 plt.subplot(121), plt.imshow(dst) plt.subplot(122), plt.imshow(dst1) plt.show()
# 例6.4 观察cv2.HTRESH_TOZERO_INV 超阈值零处理方法的效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = np.random.randint(0, 256, (4,5), np.uint8) t, dst = cv2.threshold(img, 127, -6, type=cv2.THRESH_TOZERO_INV) #将大于阈值127的像素点都设置为0,将小于等于127的像素点都保持不变。 lena = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp') t1, dst1 = cv2.threshold(lena, 127, 0, cv2.THRESH_TOZERO_INV) #此时第三个参数是没有意义的。 plt.subplot(121), plt.imshow(dst) plt.subplot(122), plt.imshow(dst1) plt.show()
# 例6.5 观察cv2.HTRESH_TOZERO 低阈值零处理方法的效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = np.random.randint(0, 256, (4,5), np.uint8) t, dst = cv2.threshold(img, 127, -6, type=cv2.THRESH_TOZERO) #将大于阈值127的像素点都保持不变,将小于等于127的像素点都设置为0。 lena = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp') t1, dst1 = cv2.threshold(lena, 127, 0, cv2.THRESH_TOZERO) #此时第三个参数是没有意义的。 plt.subplot(121), plt.imshow(dst) plt.subplot(122), plt.imshow(dst1) plt.show()
二、自适应阈值处理
全局阈值简单粗暴,当一幅图像的色彩不均衡时,用全局阈值分割图像效果就不会很好,此时我们要采用自适应阈值来处理图像。
自适应阈值就是在图像上每一个小区域计算与其对应的阈值。 - 自适应阈值处理API:
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
src: 要进行处理的原始图像
maxValue: 当参数thresholdType为cv2.THRESH_BINARY或者cv2.THRESH_BINARY_INV时,该参数表示高于(低于)阈值时赋予的新值。
adaptiveMethod: 计算自适应阈值的方法。这里有2种方法,也就是这个参数有2个可选值。
第一种方法是:adaptiveMethod=cv2.ADAPTIVE_THRESH_MEAN_C, 这种方法是将邻域所有像素点的权重值都取一样。
第二种方法是:adaptiveMethod=cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 这种方法是通过高斯公式获得邻域所有像素点的权重值。这种权重值的选取就跟各邻域像素点到目标像素点的距离有关,距离越近权重越高,距离越远权重越低。
thresholdType: 表示阈值处理方式,这个参数是和maxValue一起搭配使用的。这个参数只能选择cv2.THRESH_BINARY或者cv2.THRESH_BINARY_INV中的一个。
blockSize:表示邻域尺寸的大小,一般取3,5,7等。
C: 是常量,阈值等于平均值或者加权平均值减去这个常数。# 例6.6 观察cv2.threshold() 和 cv2.adaptiveThreshold()处理效果的差异 import cv2 import numpy as np import matplotlib.pyplot as plt computer = cv2.imread(r'C:\Users\25584\Desktop\computer.jpg', 0) t1, thresh_127 = cv2.threshold(computer, 127, 255, cv2.THRESH_BINARY) adap_mean = cv2.adaptiveThreshold(computer, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 3) adap_gaussian = cv2.adaptiveThreshold(computer, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 5, 3) plt.subplot(141), plt.imshow(computer) plt.subplot(142), plt.imshow(thresh_127) plt.subplot(143), plt.imshow(adap_mean) plt.subplot(144), plt.imshow(adap_gaussian) plt.show()
三、Otsu阈值处理(自动寻找最佳的全局阈值)
我们在使用cv2.threshold()进行阈值处理的时候,要传入一个参数就是阈值这个值,这个值我们是凭经验,凭概率,设定了一个127这个值作为阈值。但是有的图像它可能不适合这个阈值,如果我们手动一个个阈值去试就非常费劲,Otusu方法可以帮助我们自动遍历所有可能的阈值,并找出最佳的那个阈值。
普通的阈值处理函数:cv2.threshold(src, thresh, maxval, type, dst=None)
Argument:
src: 原图像,就是要进行阈值处理的原图对象。
thresh: 阈值,就是进行分类的阈值。
maxval: 当type指定为THRESH_BINARY或THRESH_BINARY_INV时,才需要设置该值。指的是高于(低于)阈值时赋予的新值。
type: 指定阈值处理的方法,常用的有:
• cv2.THRESH_BINARY(黑白二值)
• cv2.THRESH_BINARY_INV(黑白二值反转)
• cv2.THRESH_TRUNC (截断阈值化处理)
• cv2.THRESH_TOZERO
• cv2.THRESH_TOZERO_INV
dst: 目标图像
该函数有两个返回值,第一个retVal(得到的阈值值,因为有时要自动找阈值),第二个就是阈值化后的图像。Otsu阈值处理就是把上面的函数的参数type多传入一个参数cv2.THRESH_OTSU就可实现Otsu阈值分割:
t, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
这里就要把thresh参数设为0 , type参数设为cv2.THRESH_BINARY+cv2.THRESH_OTSU# 例6.7 实验Otsu阈值处理 import cv2 import numpy as np import matplotlib.pyplot as plt img = np.zeros((5,5), np.uint8)+123 img[2:6, 2:6]=126 t1, result_127 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) t2, result_otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) img, result_127, result_otsu, t1, t2
(array([[123, 123, 123, 123, 123], [123, 123, 123, 123, 123], [123, 123, 126, 126, 126], [123, 123, 126, 126, 126], [123, 123, 126, 126, 126]], dtype=uint8), array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], dtype=uint8), array([[ 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0], [ 0, 0, 255, 255, 255], [ 0, 0, 255, 255, 255], [ 0, 0, 255, 255, 255]], dtype=uint8), 127.0, 123.0)
# 例6.8 观察一幅图像使用普通二值化处理和Otsu阈值处理的效果差异 import cv2 import numpy as np import matplotlib.pyplot as plt tiffany = cv2.imread(r'C:\Users\25584\Desktop\tiffany.bmp') t1, result_127 = cv2.threshold(tiffany, 127, 255, cv2.THRESH_BINARY) #普通二值化处理 tiffany_gray = cv2.cvtColor(tiffany, cv2.COLOR_BGR2GRAY) #把3通道图像先转换为二维图像,这样才能用自动阈值搜索 t2, result_otsu = cv2.threshold(tiffany_gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) #Otsu阈值处理,注意Otsu阈值处理只能处理二维图像,所以三维要转换一下 result_otsu1 = cv2.cvtColor(result_otsu, cv2.COLOR_GRAY2RGB) #为了matplotlib显示得好看,就把色彩空间再转换一下 plt.subplot(131), plt.imshow(tiffany[:,:,::-1]) plt.subplot(132), plt.imshow(result_127) plt.subplot(133), plt.imshow(result_otsu1) plt.show()
说明:在本例中,由于原图整体的亮度就非常高,所以很多像素的值都大于127,此时我们还用127作为阈值,就得到了大量的白色区域。如果我们用otsu方法处理,就会得到较好的效果。