大津算法原理
在 opencv 中对图像进行二值化的过程中我们可能会使用大津算法,大津算法的主要作用是分割前景与背景部分区域,先将图像灰度化,假设图像有 [0,255] 个灰度等级,在数学中有方差这个概念,如果两个事物之间的方差越大,则他们的关联性则越小,在图像中计算前景与背景间的类间方差,方差越大,越能认为两部分关联性越小,即前景与背景区域,所以大津算法即寻找使背景与前景方差最大的阈值
有如下假设:
W0:背景像素点占整幅图像的比例
U0: 背景像素点的平均灰度
W1:前景像素点占整幅图像的比例
U1: 前景像素点的平均灰度
U: 整幅图像的平均灰度
g: 类间方差
有如下关系:
U = W0U0 + W1U1 --------------------------------- (1)
g = W1 ( U - U1)2 + W0 ( U - U0 )2-------------- (2)
将(2) 代入 (1) 有:
g=W0 W1 ( U0 - U1 )2
实现过程
循环遍历阈值 [0,255] 获取最大类间方差
import numpy as np
import cv2
import matplotlib.pyplot as plt
def otsuCompute(grayImg):
# 类间方差
g = 0
# 遍历矩阵
for i in range(0,256):
# 背景像素点占整幅图像的比例
W0 = 0.0
w0 = 0
# 背景图像平均灰度
U0 = 0
u0 = 0.0
# 前景图像占整幅图像的比例
W1 = 0.0
w1 = 0
# 前景图像的平均灰度
U1 = 0
u1 = 0.0
for element in grayImg.flat:
# 大于i为前景
if element>i:
w1+=1
u1+=element
else:
w0+=1
u0+=element
try:
W0 = w0/(w0+w1)
W1 = 1 - W0
U0 = u0/w0
U1 = u1/w1
if W1*W0*((U0 - U1)**2) > g:
g=W1*W0*((U0 - U1)**2)
thres=i
except:
pass
return thres
img=cv2.imread('img/water_coins.jpg',cv2.IMREAD_UNCHANGED)
grayImg=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# cv2.medianBlur(grayImg,5,grayImg)
# 大津算法二值化分割前景和背景
ret,thresOtsu=cv2.threshold(grayImg,0,255,cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)
ret1,thresMyOtsu=cv2.threshold(grayImg,otsuCompute(grayImg),255,cv2.THRESH_BINARY_INV)
print('Otsu threshold is {0}'.format(ret))
print('my Otsu threshold is {0}'.format(ret1))
cv2.imshow('src',img)
cv2.imshow('Otsu',thresOtsu)
cv2.imshow('my Otsu',thresMyOtsu)
cv2.waitKey()
cv2.destroyAllWindows()
结果如下:
可以看到与原生大津算法效果是相同的