【进阶】Otsu处理

Otsu算法是由日本学者OTSU于1979年提出的一种对图像进行二值化的高效算法。

1. 引入

在使用函数cv2.threshold()进行阈值处理时,需要自定义一个阈值,并以此阈值作为图像阈值处理的依据。通常情况下处理的图像都是色彩均衡的,这时直接将阈值设为127是比较合适的。**但是,有时图像灰度级的分布是不均衡的,如果此时还将阈值设置为127,那么阈值处理的结果就是失败的。**例如,有一个图像img,其像素值为:

[[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]]

此时,如果仍然以127作为阈值,那么阈值处理结果是:

[[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]]

可以看出这不是我们想要的结果。通过观察,我们可以发现如果以125为阈值进行分割,可以得到比较好的结果:

[[0 0 0 0 0]

[0 0 0 0 0]

[0 0 255 255 255]

[255 255 255 255 255]

[255 255 255 255 255]]

上图只是一个5x5的图像,非常非常小,现实中的图像都是很大、很复杂的,不可能通过肉眼找到合适的分割阈值。因此,日本学者OTSU找到了一种能够根据当前图像给出最佳的类间分割阈值的方法,叫做Otsu方法,其思想是,Otsu方法会遍历所有可能阈值,从而找到最佳的阈值。

2. Otsu阈值分割实现

通过在函数cv2.threshold()中对参数type的类型多传递一个参数cv2.THRESH_OTSU,即可实现Otsu方式的阈值分割。需要注意的是,在使用Otsu方法时,要把阈值设为0(thresh = 0)。其函数格式为:

retval, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

注意以上代码与普通阈值分割的三点不同之处:

  • 参数type新增一个参数cv2.THRESH_OTSU

  • 设定的阈值为0。

  • 返回值retval是Otsu方法计算得到并使用的最佳阈值。

测试

"""
Author: Will Wang
Email: WillWang1998@163.com
"""
import cv2
import numpy as np

image = np.zeros((5, 5), dtype=np.uint8) # uint8:unsigned int 8bit 无符号整型 范围[0:255] 
image[0:6, 0:6] = 123
image[2:6, 2:6] = 126

print(f'origin image = \n{image}')

retval1, threshold1 = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
print(f'retval1={retval1}, threshold1 = \n {threshold1}')

retval2, otsu = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(f'retval2={retval2}, otsu = \n{otsu}')

结果如下:

image.png

如果对图像进行处理,对比效果如何?

image.png

左图为原始图像;中间为进行普通二值化阈值处理,以127为阈值的处理结果;右图是采用采用cv2.THRESH_OTSU的处理结果。可以看到,原始图像整体的亮度比较高,即像素点的值>127的占比较大,所以,采用Otsu可以得到更好的阈值处理结果。