当前位置:   article > 正文

图像二值化阈值调整——OTSU算法(大津法/最大类间方差法)_大津法求阈值

大津法求阈值

大津算法(OTSU算法)是一种常用的图像二值化方法,用于将灰度图像转化为二值图像。该算法由日本学者大津展之于1979年提出,因此得名。

大津算法的核心思想是通过寻找一个阈值,将图像的像素分为两个类别:前景和背景。具体步骤如下:

  1. 统计图像的灰度直方图,得到每个灰度级的像素数目。
  2. 遍历所有可能的阈值(0到255),计算根据该阈值将图像分为前景和背景的类内方差。
  3. 根据类内方差的最小值确定最佳阈值。

在大津算法中,类内方差是衡量前景和背景之间差异的度量。通过选择使类内方差最小的阈值,可以实现最佳的图像分割效果。

大津算法的优点是简单易懂,计算效率高。它适用于灰度图像的二值化处理,特别是对于具有双峰直方图的图像效果更好。然而,该算法对于具有非双峰直方图的图像可能产生较差的分割结果。因此,在应用大津算法之前,需要对图像的直方图进行分析,确保适用性。

大津算法在图像处理中被广泛应用,例如在文档图像处理、目标检测、图像分割等领域。

下面推导类间方差函数:

设阈值为灰度k(k\in \left [ 0,L-1 \right ],L=256)。这个阈值把图像像素分割成两类,C1类像素小于等于k,C2类像素大于k。设这两类像素各自的均值为m_1,m_2,图像全局均值为m_G。同时像素被分为C1和C2类的概率分别为p_1,p_2。则有:

p_1m_1+p_2m_2=m_G

p_1+p_2=1

根据方差的概念,类间方差表达式为:

\sigma ^2=p_1\left ( m_1-m_G \right )^2+p_2\left ( m_2-m_G \right )^2

展开:

\sigma ^2=p_1m_1^2+p_1m_G^2-2p_1m_1m_G+p_2m_2^2+p_2m_G^2-2p_2m_2m_G

合并2,5及3,6项可得:

\sigma ^2=p_1m_1^2+p_2m_2^2+m_G^2-2m_G^2=p_1m_1^2+p_2m_2^2-m_G^2

我们再把m_G=p_1m_1+p_2m_2代回得到:

\sigma ^2=(p_1-p_1^2)m_1^2+(p_2-p_2^2)m_2^2-2p_1p_2m_1m_2

再注意到p_1+p_2=1,所以p_1-p_1^2=p_1(1-p_1)=p_1p_2p_2-p_2^2=p_2(1-p_2)=p_1p_2,从而得到:

\sigma ^2=p_1p_2(m_1-m_2)^2

对于给定的阈值k,我们可以统计出灰度级的分布列:

灰度值01...255
p_ip_0p_1...p_{255}

显然根据分布列性质有\sum_{i=0}^{L-1}p_i=1(请注意这里的p_1,p_2是分布列中的,不是上面的定义)

那么有:

p_1=\sum_{i=0}^{k-1}p_i,p_2=\sum_{i=k}^{L-1}p_i,m_1=\sum_{i=0}^{k-1}ip_i,m_2=\sum_{i=k}^{L-1}ip_i

将k从\left [ 0,L-1 \right ]遍历,找出使得\sigma ^2最大的k值,这个k值就是阈值。

对于分割,这个分割就是二值化,OpenCV给了以下几种方式(同threshold):

cv2帮助文档:

Miscellaneous Image Transformations — OpenCV 3.0.0-dev documentationicon-default.png?t=N7T8https://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/miscellaneous_transformations.html?highlight=threshold#threshold代码实现:

首先是原理部分的实现,这部分我们使用numpy:

  1. import cv2
  2. import numpy as np
  3. def OTSU(img_gray, GrayScale):
  4. assert img_gray.ndim == 2, "must input a gary_img" # shape有几个数字, ndim就是多少
  5. img_gray = np.array(img_gray).ravel().astype(np.uint8)
  6. u1 = 0.0 # 背景像素的平均灰度值
  7. u2 = 0.0 # 前景像素的平均灰度值
  8. th = 0.0
  9. # 总的像素数目
  10. PixSum = img_gray.size
  11. # 各个灰度值的像素数目
  12. PixCount = np.zeros(GrayScale)
  13. # 各灰度值所占总像素数的比例
  14. PixRate = np.zeros(GrayScale)
  15. # 统计各个灰度值的像素个数
  16. for i in range(PixSum):
  17. # 默认灰度图像的像素值范围为GrayScale
  18. Pixvalue = img_gray[i]
  19. PixCount[Pixvalue] = PixCount[Pixvalue] + 1
  20. # 确定各个灰度值对应的像素点的个数在所有的像素点中的比例。
  21. for j in range(GrayScale):
  22. PixRate[j] = PixCount[j] * 1.0 / PixSum
  23. Max_var = 0
  24. # 确定最大类间方差对应的阈值
  25. for i in range(1, GrayScale): # 从1开始是为了避免w1为0.
  26. u1_tem = 0.0
  27. u2_tem = 0.0
  28. # 背景像素的比列
  29. w1 = np.sum(PixRate[:i])
  30. # 前景像素的比例
  31. w2 = 1.0 - w1
  32. if w1 == 0 or w2 == 0:
  33. pass
  34. else: # 背景像素的平均灰度值
  35. for m in range(i):
  36. u1_tem = u1_tem + PixRate[m] * m
  37. u1 = u1_tem * 1.0 / w1
  38. # 前景像素的平均灰度值
  39. for n in range(i, GrayScale):
  40. u2_tem = u2_tem + PixRate[n] * n
  41. u2 = u2_tem / w2
  42. # print(u1)
  43. # 类间方差公式:G=w1*w2*(u1-u2)**2
  44. tem_var = w1 * w2 * np.power((u1 - u2), 2)
  45. # print(tem_var)
  46. # 判断当前类间方差是否为最大值。
  47. if Max_var < tem_var:
  48. Max_var = tem_var # 深拷贝,Max_var与tem_var占用不同的内存空间。
  49. th = i
  50. return th
  51. def main():
  52. img = cv2.imread('6.jpg', 0)
  53. # 将图片转为灰度图
  54. th = OTSU(img, 256)
  55. print("使用numpy的方法:" + str(th)) # 结果为 136
  56. main()

然后是基于cv2的OTSU实现,cv2可直接指定使用:

  1. import cv2
  2. import matplotlib.pylab as plt
  3. def main2():
  4. img = cv2.imread('6.jpg', 0)
  5. ret, thresh1 = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)
  6. print(ret) # 结果是135.0
  7. titles = ['Original Image', 'After Binarization']
  8. images = [img, thresh1]
  9. for i in range(2):
  10. plt.subplot(1, 2, i+1)
  11. plt.imshow(images[i], 'gray')
  12. plt.title(titles[i])
  13. plt.xticks([])
  14. plt.yticks([])
  15. plt.show()
  16. main2()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/481138
推荐阅读
相关标签
  

闽ICP备14008679号