大律法OTSU【详细】(python实现)
文章目录1. 前言图像的灰度直方图计算直方图1.使用opencv的函数2. 使用Numpy函数2. OTSU算法简介3. 数学原理4. python实现5. 参考文章1. 前言图像的灰度直方图灰度直方图是关于灰度级分布的函数,是对图像中灰度级分布的统计。灰度直方图是将数字图像中的所有像素,按照灰度值的大小,统计其出现的频率。灰度直方图是灰度级的函数,它表示图像中具有某种灰度级的像素的个数,反映了图
一、 前言
图像的灰度直方图
灰度直方图是关于灰度级分布的函数,是对图像中灰度级分布的统计。灰度直方图是将数字图像中的所有像素,按照灰度值的大小,统计其出现的频率。灰度直方图是灰度级的函数,它表示图像中具有某种灰度级的像素的个数,反映了图像中某种灰度出现的频率。【from百度百科】
计算直方图
1.使用opencv的函数
cv2.calcHist(images, channels, mask, histSize, ranges)
- 参数1:要计算的原图,以方括号的传入,如:[img]。
- 参数2:类似前面提到的dims,灰度图写[0]就行,彩色图B/G/R分别传入[0]/[1]/[2]。
- 参数3:要计算的区域ROI,计算整幅图的话,写None。
- 参数4:也叫bins,子区段数目,如果我们统计0-255每个像素值,bins=256;如果划分区间,比如0-15,16-31…240-255这样16个区间,bins=16。
- 参数5:range,要计算的像素值范围,一般为[0,256)。
eg:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('test.jpg')
# hist是256x1数组,每个值对应于该图像中具有相应像素值的像素数
hist = cv.calcHist([img],[0],None,[256],[0,256])
# 绘制直方图
plt.plot(hist)
plt.show()
2. 使用Numpy函数
bincount(x, weights=None, minlength=None)
相对应的函数详解可参考:numpy.bincount详解
eg:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('test.jpg')
# ravel()函数将二维矩阵展平变成一维数组
hist = np.bincount(img.ravel(), minlength=256)
# 绘制直方图
plt.plot(hist)
plt.show()
二、 OTSU算法简介
OTSU算法是一种图像二值化算法。其算法假设图像中存在一个阈值 T ,判断图像中每个像素与T的大小关系,可以将所有像素分为背景 C0 和 前景 C1 两类,且当选取到最佳T阈值时,背景部分与前景部分的差别最大,而OTSU 算法利用最大类间方差来衡量这一差别。由于OTSU算法采用了最大类间方法的思想,因此该算法也被OTSU最大类间方差法
缺点:对图像噪声敏感;只能针对单一目标分割;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好
三、 数学原理
该算法的原理主要涉及 均值、方差等概念和部分公式的推导
-
将图片的像素值分为 [1, 2, …, L] 个水平,用ni 表示各个水平像素值的像素个数,那么很容易得到总像素个数N为:N = n1 + n2 + … + nL
-
利用像素值对应个数与总数的商作为某个像素值出现的概率,定义pi
p i = n i / N , p i ≥ 0 , ∑ i = 1 L p i = 1 (1) p_i = n_i/N, p_i ≥ 0, \sum_{i=1}^L p_i = 1 \tag1 pi=ni/N,pi≥0,i=1∑Lpi=1(1) -
定义两个量w0 , w1为C0,C1的局部概率之和,并且得到二者的关系
w 0 = P r ( C 0 ) = ∑ i = 1 k p i = w ( k ) (2) w_0 = Pr(C_0) = \sum_{i=1}^k p_i = w(k) \tag2 w0=Pr(C0)=i=1∑kpi=w(k)(2)
w 1 = P r ( C 1 ) = ∑ i = k + 1 L p i = 1 − w ( k ) (3) w_1 = Pr(C_1) = \sum_{i=k+1}^L p_i = 1 - w(k) \tag3 w1=Pr(C1)=i=k+1∑Lpi=1−w(k)(3) -
由此我们得到总的数学期望【期望是平均数随样本趋于无穷的极限,所以可以将期望 ≈ 均值】和C0 , C1各自的数学期望并指出三者的关系,式中 i 代表像素值,除于各自概率的和用于进行归一。
u 0 = ∑ i = 1 k P r ( i ∣ C 0 ) = ∑ i = 1 k i ∗ p i / w 0 = u ( k ) w ( k ) (4) u_0 = \sum_{i=1}^k Pr(i|C_0) = \sum_{i=1}^k i * p_i / w_0 = \frac{u(k)}{w(k)} \tag4 u0=i=1∑kPr(i∣C0)=i=1∑ki∗pi/w0=w(k)u(k)(4)
u 1 = ∑ i = k + 1 L P r ( i ∣ C 1 ) = ∑ i = k + 1 L i ∗ p i / w 1 = u ( T ) − u ( k ) 1 − w ( k ) (5) u_1 = \sum_{i=k+1}^L Pr(i|C_1) = \sum_{i=k+1}^L i * p_i / w_1 = \frac{u(T) - u(k)}{1 - w(k)} \tag5 u1=i=k+1∑LPr(i∣C1)=i=k+1∑Li∗pi/w1=1−w(k)u(T)−u(k)(5)
w ( k ) = ∑ i = 1 k p i (6) w(k) = \sum_{i=1}^k p_i \tag6 w(k)=i=1∑kpi(6)
u ( k ) = ∑ i = 1 k i ∗ p i (7) u(k) = \sum_{i=1}^k i * p_i \tag7 u(k)=i=1∑ki∗pi(7)
u T = u ( L ) = ∑ i = 1 L i ∗ p i (8) u_T = u(L) = \sum_{i=1}^L i * p_i \tag8 uT=u(L)=i=1∑Li∗pi(8)
上述公式间的关系为:
w 0 u 0 + w 1 u 1 = u T , w 0 + w 1 = 1 (9) w_0 u_0 + w_1 u_1 = u_T , w_0 + w_1 = 1 \tag9 w0u0+w1u1=uT,w0+w1=1(9) -
各自局部方差和总方差为
σ 0 2 = ∑ i = 1 k ( i − u 0 ) 2 P r ( i ∣ C 0 ) = ∑ i = 1 k ( i − u 0 ) 2 p i / w 0 (10) σ^2_0 = \sum_{i=1}^k (i - u_0)^2 Pr(i|C_0) = \sum_{i=1}^k (i - u_0)^2 p_i / w_0 \tag{10} σ02=i=1∑k(i−u0)2Pr(i∣C0)=i=1∑k(i−u0)2pi/w0(10)
σ 1 2 = ∑ i = k + 1 L ( i − u 1 ) 2 P r ( i ∣ C 1 ) = ∑ i = k + 1 L ( i − u 1 ) 2 p i / w 1 (11) σ^2_1 = \sum_{i=k+1}^L (i - u_1)^2 Pr(i|C_1) = \sum_{i=k+1}^L (i - u_1)^2 p_i / w_1 \tag{11} σ12=i=k+1∑L(i−u1)2Pr(i∣C1)=i=k+1∑L(i−u1)2pi/w1(11)
σ T 2 = ∑ i = 1 L ( i − u T ) 2 p i (12) σ^2_T = \sum_{i=1}^L (i - u_T)^2 p_i \tag{12} σT2=i=1∑L(i−uT)2pi(12) -
within_class variance 类内差异
σ w 2 = w 0 σ 0 2 + w 1 σ 1 2 (13) σ^2_w = w_0 σ^2_0 + w_1 σ^2_1 \tag{13} σw2=w0σ02+w1σ12(13)
between_class variance 类间差异
σ b 2 = w 0 ( u 0 − u T ) 2 + w 1 ( u 1 − u T ) 2 (14) σ^2_b = w_0(u_0 - u_T)^2 + w_1(u_1 - u_T)^2 \tag{14} σb2=w0(u0−uT)2+w1(u1−uT)2(14) -
当最大化类间差距时,即最小化类内差距,因为 二者的和为定值。
σ w 2 + σ b 2 = σ T 2 (15) σ^2_w + σ^2_b = σ^2_T \tag{15} σw2+σb2=σT2(15)
四、 python实现
假设一图像为灰度图,大小为M*N,背景部分较暗,OTSU算法步骤如下:
- 选取一个分割阈值T,统计计算属于前景的像素点数(像素值小于T)在全部像素点的占比 w 0 w_0 w0 以及其平均灰度 u 0 u_0 u0 ; 同理统计属于背景部分的 w 1 w_1 w1 和 u 1 u_1 u1 ;
- 计算图像全部像素的平均灰度 u T = w 0 ∗ u 0 + w 1 ∗ u 1 u_T = w_0 * u_0 + w_1 * u_1 uT=w0∗u0+w1∗u1 以及类间方差
g = w 0 ∗ ( u 0 − u T ) 2 + w 1 ∗ ( u 1 − u T ) 2 = w 0 ∗ w 1 ∗ ( u 0 − u 1 ) 2 (16) g = w_0 * (u_0 - u_T)^2 + w_1 * (u_1 - u_T)^2 = w_0 * w_1 * (u_0 - u_1)^2 \tag{16} g=w0∗(u0−uT)2+w1∗(u1−uT)2=w0∗w1∗(u0−u1)2(16)
- 遍历全部分割阈值T,重复1-2步骤,得到使类间方差 g 最大的阈值T;
import numpy as np
import cv2 as cv
#将图片转为灰度图
# https://blog.csdn.net/weixin_43635647/article/details/99625105
img = cv.imread('test.jpg', 0)
cv.imshow("img", img)
cv.waitKey()
def OTSU(img_gray, GrayScale):
assert img_gray.ndim == 2, "must input a gary_img" #shape有几个数字, ndim就是多少
img_gray = np.array(img_gray).ravel().astype(np.uint8)
u1=0.0#背景像素的平均灰度值
u2=0.0#前景像素的平均灰度值
th=0.0
#总的像素数目
PixSum=img_gray.size
#各个灰度值的像素数目
PixCount=np.zeros(GrayScale)
#各灰度值所占总像素数的比例
PixRate=np.zeros(GrayScale)
#统计各个灰度值的像素个数
for i in range(PixSum):
#默认灰度图像的像素值范围为GrayScale
Pixvalue=img_gray[i]
PixCount[Pixvalue]=PixCount[Pixvalue]+1
#确定各个灰度值对应的像素点的个数在所有的像素点中的比例。
for j in range(GrayScale):
PixRate[j]=PixCount[j]*1.0/PixSum
Max_var = 0
#确定最大类间方差对应的阈值
for i in range(1,GrayScale):#从1开始是为了避免w1为0.
u1_tem=0.0
u2_tem=0.0
#背景像素的比列
w1=np.sum(PixRate[:i])
#前景像素的比例
w2=1.0-w1
if w1==0 or w2==0:
pass
else:#背景像素的平均灰度值
for m in range(i):
u1_tem=u1_tem+PixRate[m]*m
u1 = u1_tem * 1.0 / w1
#前景像素的平均灰度值
for n in range(i,GrayScale):
u2_tem = u2_tem + PixRate[n]*n
u2 = u2_tem / w2
#print(u1)
#类间方差公式:G=w1*w2*(u1-u2)**2
tem_var=w1*w2*np.power((u1-u2),2)
#print(tem_var)
#判断当前类间方差是否为最大值。
if Max_var<tem_var:
Max_var=tem_var#深拷贝,Max_var与tem_var占用不同的内存空间。
th=i
return th
th = OTSU(img,256)
print("使用numpy的方法:" + str(th)) # 结果为 135
直接使用 opencv的函数
import numpy as np
import cv2 as cv
#该函数返回的第一个值就是输入的thresh值,第二个就是处理后的图像
img = cv.imread('test.jpg',0)
retVal, a_img = cv.threshold(img, 0, 255, cv.THRESH_OTSU)
print("使用opencv函数的方法:" + str(retVal)) # 结果为 134
cv.imshow("a_img",a_img)
cv.waitKey()
五、 参考文章
更多推荐
所有评论(0)