OTSU(大津算法)

确定图像二值化分割阈值

不受图像亮度和对比度的影响

用于图像分割过程中,自动计算出一个最佳全局阈值的算法

通过最大类间平方差的方法来区分图像前景及背景

缺点

  • 对图像噪声敏感

  • 只能针对单一目标分割

  • 当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,效果不好

类间平方差

OTSU算法的核心是类间平方差

类间平方差:将图像的阈值划分为两个区域寻找这两个区域阈值间的最大方差

类间方差公式

假设存在一个阈值TH将整幅图(灰度值为0~255)分为了两个部分:

  • 第一部分是小于假定阈值TH的那部分。这一部分的平均灰度值为m1,这一部分在整幅图中出现的概率为P1
  • 第二部分是大于假定阈值TH的那部分。这部分的平均灰度值为m2,出现的概率为P2
  • MG则是全局的平均阈值

公式求解:

  • m1可以想象为从0到假定阈值TH处每个灰度值与该灰度值出现的频率的乘积再除以该区域的总频率,就是它的平均阈值

    image-20220418103410873

    • k——假定阈值TH
    • i——灰度值
    • pi——每个灰度值出现的频率
    • p1——0~k区域每个灰度值出现概率的总和
  • m2:

    image-20220418103525218

    • L: 图像的像素级,通常都是255
    • p2: k+1~L区域每个灰度值出现概率的总和
  • p1、p2

    image-20220418103623291

  • 全局的平均阈值

    image-20220418103642677

  • 类间方差表达式

    image-20220418103707818

实现
import cv2
import numpy as np

img = cv2.imread("./lena.jpg",0)
h,w = img.shape

hist = [0 for i in range(256)]

for i in range(w):
    for j in range(h):
        hist[img.item(j,i)]+=1
        
ave_hist = list(map(lambda i: i/(w*h),hist))
M=0
for i in range(256):
    M += i*ave_hist[i]
otsu_value = []
for i in range(256):
    m1,m2,p1,p2=0,0,0,0
    for v1 in range(0,i+1):
        p1+=ave_hist[v1]
        m1+=ave_hist[v1]*v1
    if p1:
        m1 /= p1 
    else:
        m1=-1
    for v2 in range(i+1,256):
        p2+=ave_hist[v2]
        m2+=ave_hist[v2]*v2
    if p2:
        m2 /= p2 
    else:
        m2=-1
    M = p1*m1+p2*m2
    thes = p1*(m1-M)**2 + p2*(m2-M)**2
    otsu_value.append(thes)
max_v = max(otsu_value)
idx = otsu_value.index(max_v)
        
print(len(ave_hist))
print("thes:",idx)

opencv 内部的函数:

ret, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
参考内容

https://blog.csdn.net/m0_54351828/article/details/120642025

https://blog.csdn.net/weixin_40647819/article/details/90179953