OTSU(大津算法)

确定图像二值化分割阈值

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

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

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

缺点

  • 对图像噪声敏感
  • 只能针对单一目标分割
  • 当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,效果不好
类间平方差

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

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

类间方差公式

OTSU(大津算法)_.net

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

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

公式求解:

  • m1可以想象为从0到假定阈值TH处每个灰度值与该灰度值出现的频率的乘积再除以该区域的总频率,就是它的平均阈值
    OTSU(大津算法)_灰度值_02
  • k——假定阈值TH
  • i——灰度值
  • pi——每个灰度值出现的频率
  • p1——0~k区域每个灰度值出现概率的总和
  • m2:
    OTSU(大津算法)_灰度值_03
  • L: 图像的像素级,通常都是255
  • p2: k+1~L区域每个灰度值出现概率的总和
  • p1、p2
    OTSU(大津算法)_灰度值_04
  • 全局的平均阈值
    OTSU(大津算法)_方差_05
  • 类间方差表达式
    OTSU(大津算法)_方差_06
实现
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)

参考内容