• 【老文新发】Otsu大津法详解及python实现


    原文:A Threshold Selection Method from Gray-Level Histograms
    A Fast Algorithm for Multilevel Thresholding

    前言

    大津法包含两个重要的概念:类间方差(between-class variance)类内方差(within-class variance)

    两者的详细关系推导可后文。

    大津法又称为最大类间方差法是有原因的。因为这个算法的目的就是最大化类间方差,且这个最优阈值一定存在。

    大津法作为阈值自动分割的经典算法,其思想很巧妙,值得学习。

    大津法推导

    在这里插入图片描述
    如图所示,右边分割分明,其类间的差异大,区分明显,所以其类间方差更大。

    大津法就是要实现这个过程。

    我们先做图像的直方图,统计每个很小像素区间包含的像素个数。

    即将图像像素值分为 [ 1 , 2 , 3 , …. , L] 个区间。用 n i n_{i} ni 表示各个水平像素值的像素个数,总像素个数与 n i n_{i} ni关系为:
    N = n 1 + n 2 + … + n L N=n_1+n_2+\ldots+n_L N=n1+n2++nL
    像元个数 n i ni ni 比上总像元数 N 即可得到某个像素区间出现的频率,定义 p i pi pi
    p i = n i / N i , p i ≥ 0 , ∑ i = 1 L p i = 1 p_i=n_i / N_i, p_i \geq 0, \sum_{i=1}^L p_i=1 pi=ni/Ni,pi0,i=1Lpi=1
    根据原文,是利用一个阈值 k 把图像分为两类 C 0 、 C 1 。 k ∈ [ 1 , 2 , 3 , … . , L ] C0 、C1。k∈[ 1 , 2 , 3 , …. , L] C0C1k[1,2,3,.,L]
    我们分别求出这个阈值前后的局部频率之和,定义如下:
    C 0 = [ 1 , k ] C 1 = [ k + 1 , L ] w 0 = Pr ⁡ ( C 0 ) = ∑ i = 1 k p i = w ( k ) w 1 = Pr ⁡ ( C 1 ) = ∑ i = k + 1 L p i = 1 − w ( k )

    C0=[1,k]C1=[k+1,L]w0=Pr(C0)=i=1kpi=w(k)w1=Pr(C1)=i=k+1Lpi=1w(k)" role="presentation" style="position: relative;">C0=[1,k]C1=[k+1,L]w0=Pr(C0)=i=1kpi=w(k)w1=Pr(C1)=i=k+1Lpi=1w(k)
    C0=[1,k]C1=[k+1,L]w0=Pr(C0)=i=1kpi=w(k)w1=Pr(C1)=i=k+1Lpi=1w(k)
    则灰度图像频率直方图的总的数学期望和 C0 、C1的数学期望如下:
    u 0 = ∑ i = 1 k i ∗ Pr ⁡ ( i ∣ C 0 ) = ∑ i = 1 k i ∗ p i / w 0 = u ( k ) w ( k ) u 1 = ∑ i = k + 1 L i ∗ Pr ⁡ ( i ∣ C 1 ) = ∑ i = k + 1 L i ∗ p i / w 1 = u T − u ( k ) 1 − w ( k ) u T = u ( L ) = ∑ i = 1 L i ∗ p i
    u0=i=1kiPr(iC0)=i=1kipi/w0=u(k)w(k)u1=i=k+1LiPr(iC1)=i=k+1Lipi/w1=uTu(k)1w(k)uT=u(L)=i=1Lipi" role="presentation" style="position: relative;">u0=i=1kiPr(iC0)=i=1kipi/w0=u(k)w(k)u1=i=k+1LiPr(iC1)=i=k+1Lipi/w1=uTu(k)1w(k)uT=u(L)=i=1Lipi
    u0=i=1kiPr(iC0)=i=1kipi/w0=w(k)u(k)u1=i=k+1LiPr(iC1)=i=k+1Lipi/w1=1w(k)uTu(k)uT=u(L)=i=1Lipi

    上式各个变量之间的关系如下:
    w 0 u 0 + w 1 u 1 = u T w 0 + w 1 = 1 w_0 u_0+w_1 u_1=u_T \quad w_0+w_1=1 w0u0+w1u1=uTw0+w1=1
    期望有了,计算一下对应的方差:
    σ 0 2 = ∑ i = 1 k ( i − u 0 ) 2 Pr ⁡ ( i ∣ C 0 ) = ∑ i = 1 k ( i − u 0 ) 2 p i / w 0 σ 1 2 = ∑ i = k + 1 L ( i − u 1 ) 2 Pr ⁡ ( i ∣ C 1 ) = ∑ i = k + 1 L ( i − u 1 ) 2 p i / w 0 σ T 2 = ∑ i = 1 L ( i − u T ) 2 p i
    σ02=i=1k(iu0)2Pr(iC0)=i=1k(iu0)2pi/w0σ12=i=k+1L(iu1)2Pr(iC1)=i=k+1L(iu1)2pi/w0σT2=i=1L(iuT)2pi" role="presentation" style="position: relative;">σ02=i=1k(iu0)2Pr(iC0)=i=1k(iu0)2pi/w0σ12=i=k+1L(iu1)2Pr(iC1)=i=k+1L(iu1)2pi/w0σT2=i=1L(iuT)2pi
    σ02=i=1k(iu0)2Pr(iC0)=i=1k(iu0)2pi/w0σ12=i=k+1L(iu1)2Pr(iC1)=i=k+1L(iu1)2pi/w0σT2=i=1L(iuT)2pi

    根据文献:Introduction to statistical pattern recognition,260-267。类内误差、类间误差、总误差有如下关系:
    类内误差 σ w 2 = w 0 σ 0 2 + w 1 σ 1 2 类内误差 \sigma_w^2=w_0 \sigma_0^2+w_1 \sigma_1^2 类内误差σw2=w0σ02+w1σ12
    类间误差 σ b 2 = w 0 ( u 0 − u T ) 2 + w 1 ( u 1 − u T ) 2 类间误差\sigma_b^2=w_0\left(u_0-u_T\right)^2+w_1\left(u_1-u_T\right)^2 类间误差σb2=w0(u0uT)2+w1(u1uT)2

    总误差 σ w 2 + σ b 2 = σ T 2 总误差\sigma_w^2+\sigma_b^2=\sigma_T^2 总误差σw2+σb2=σT2
    注意:总误差是与阈值k无关的,但类间误差和类内误差是与阈值k相关的函数
    σ b 2 = w 0 ( u 0 − u T ) 2 + w 1 ( u 1 − u T ) 2 = w 0 ( u 0 − ( w 0 u 0 + w 1 u 1 ) ) 2 + w 1 ( u 1 − ( w 0 u 0 + w 1 u 1 ) ) 2 = w 0 w 1 ( u 1 − u 0 ) 2

    σb2=w0(u0uT)2+w1(u1uT)2=w0(u0(w0u0+w1u1))2+w1(u1(w0u0+w1u1))2=w0w1(u1u0)2" role="presentation" style="position: relative;">σb2=w0(u0uT)2+w1(u1uT)2=w0(u0(w0u0+w1u1))2+w1(u1(w0u0+w1u1))2=w0w1(u1u0)2
    σb2=w0(u0uT)2+w1(u1uT)2=w0(u0(w0u0+w1u1))2+w1(u1(w0u0+w1u1))2=w0w1(u1u0)2
    然后再分别把w0,u1,u0带入,可得到:
    σ b 2 = [ u T w ( k ) − u ( k ) ] 2 w ( k ) [ 1 − w ( k ) ] \sigma_b^2=\frac{\left[u_T w(k)-u(k)\right]^2}{w(k)[1-w(k)]} σb2=w(k)[1w(k)][uTw(k)u(k)]2

    则求解最大类间误差为:
    σ b 2 ( k ∗ ) = max ⁡ 1 ≤ k < L σ b 2 ( k ) \sigma_b^2\left(k^*\right)=\max _{1 \leq kσb2(k)=1k<Lmaxσb2(k)

    由上述 σ b 2 \sigma_b^2 σb2 的分母可以发现, w ( k ) w(k) w(k) 可以取到 1 也可以取到0,因此在边界上 σ b 2 \sigma_b^2 σb2 可以无穷大,而在开基 ( 0 , 1 ) (0,1) (0,1) 则类间方差有限,因此在定义域
    S ∗ = k : w 0 w 1 = w ( k ) [ 1 − w ( k ) ] > 0 S^*=k: w_0 w_1=w(k)[1-w(k)]>0 S=k:w0w1=w(k)[1w(k)]>0
    因此必定存在一个阈值k使得两类类间方差最大。
    以下是python代码实现:

    def otsu(gray_img):
        n_count = gray_img.size
    
        gray_img_array = gray_img.flatten()
        index = np.flatnonzero(gray_img_array)
        gray_img_data = gray_img_array[index]
        
        threshold_t = 0
        max_g = 0
        
        t = np.linspace(start=-1, stop=1, num=256)
        # 遍历每一个灰度层
        for i in range(len(t)):
        	# 使用numpy直接对数组进行运算
            n0 = gray_img_data[np.where(gray_img_data < t[i])]
            n1 = gray_img_data[np.where(gray_img_data >= t[i])]
            w0 = len(n0) / n_count
            w1 = len(n1) / n_count
            u0 = np.mean(n0) if len(n0) > 0 else 0.
            u1 = np.mean(n1) if len(n0) > 0 else 0.
            
            g = w0 * w1 * (u0 - u1) ** 2
            if g > max_g:
                max_g = g
                threshold_t = t[i]
        print('类间方差最大阈值:', threshold_t)
        gray_img[gray_img < threshold_t] = 0
        gray_img[gray_img >= threshold_t] = 1
        return gray_img
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    这个在opencv中已经有实现,可以直接调用

    import cv2
    t, otsu = cv2.threshold(img, 0, 255, cv2>THRESH_BINARY + cv2.THRESH_OTSU)
    
    • 1
    • 2

    多分类最大类间方差法

    根据以上公式类推到多分类的最大类间方差法,假设有 m − 1 \mathrm{m}-1 m1 个阈值 { t 1 , t 2 , … , t M − 1 } \{\mathrm{t} 1, \mathrm{t} 2, \ldots, \mathrm{tM}-1\} {t1,t2,,tM1} 将图像分为 M \mathrm{M} M 类, C 1 C_1 C1 C 2 … C M C_2 \ldots C_M C2CM 。 则存在一组阈值 { t 1 ∗ , t 2 ∗ , … , t M − 1 ∗ } \left\{t 1^*, \mathrm{t} 2^*, \ldots, \mathrm{tM}-1^*\right\} {t1,t2,,tM1} 使得
    { t 1 ∗ , t 2 ∗ , … , t M − 1 ∗ } = Arg ⁡ Max ⁡ { σ B 2 ( t 1 , t 2 , … , t M − 1 ) } , 1 ≤ t 1 < … < t M − 1 < L

    {t1,t2,,tM1}=ArgMax{σB2(t1,t2,,tM1)},1t1<<tM1<L" role="presentation" style="position: relative;">{t1,t2,,tM1}=ArgMax{σB2(t1,t2,,tM1)},1t1<<tM1<L
    {t1,t2,,tM1}=ArgMax{σB2(t1,t2,,tM1)},1t1<<tM1<L

    成立
    其中:
    σ B 2 = ∑ k = 1 M ω k ( μ k − μ T ) 2 ω k = ∑ i ∈ C k p i , μ k = ∑ i ∈ C k i p i / ω ( k ) .

    σB2=k=1Mωk(μkμT)2ωk=iCkpi,μk=iCkipi/ω(k)." role="presentation" style="position: relative;">σB2=k=1Mωk(μkμT)2ωk=iCkpi,μk=iCkipi/ω(k).
    σB2ωkμk=k=1Mωk(μkμT)2=iCkpi,=iCkipi/ω(k).

    因为
    ∑ k = 1 M ω k = 1 μ T = ∑ k = 1 M ω k μ k .

    k=1Mωk=1μT=k=1Mωkμk." role="presentation" style="position: relative;">k=1Mωk=1μT=k=1Mωkμk.
    k=1Mωk=1μT=k=1Mωkμk.
    因此
    σ B 2 ( t 1 , t 2 , … , t M − 1 ) = ∑ k = 1 M ω k μ k 2 − μ T 2 \sigma_{\mathrm{B}}{ }^{2}\left(\mathrm{t}_{1}, \mathrm{t}_{2}, \ldots, \mathrm{t}_{\mathrm{M}-1}\right)=\sum_{k=1}^{\mathrm{M}} \omega_{\mathrm{k}} \mu_{\mathrm{k}}^{2}-\mu_{\mathrm{T}}{ }^{2} σB2(t1,t2,,tM1)=k=1Mωkμk2μT2

    μ T \mu \mathrm{T} μT 与间值无关,因此求上式的最大值可转为:
    { t 1 ∗ , t 2 ∗ , … , t M − 1 ∗ } = Arg ⁡ Max ⁡ { ( σ B ′ ) 2 { { t 1 , t 2 , … , t M − 1 } } 1 ≤ t 1 < … < t M − 1 < L ( σ B ) 2 = ∑ k = 1 M ω k μ k 2

    {t1,t2,,tM1}=ArgMax{(σB)2{{t1,t2,,tM1}}1t1<<tM1<L(σB)2=k=1Mωkμk2" role="presentation" style="position: relative;">{t1,t2,,tM1}=ArgMax{(σB)2{{t1,t2,,tM1}}1t1<<tM1<L(σB)2=k=1Mωkμk2
    {t1,t2,,tM1}=ArgMax{(σB)2{{t1,t2,,tM1}}1t1<<tM1<L(σB)2=k=1Mωkμk2

    类间方差、类内方差和总方差关系

    https://blog.csdn.net/qq_42164483/article/details/119064535
    https://blog.csdn.net/m0_38024332/article/details/104226806
    以上这两篇博文讲的也很详细,内容有所参考,欢迎访问阅读。

  • 相关阅读:
    JavaScript命名冲突不可避免?冲突源有哪些
    ATA-8061射频功率放大器在心室导管式扩压电式测力传感器中的应用
    三元组顺序表表示的稀疏矩阵转置Ⅱ
    C语言 - 数组
    JDBC中ResultSet的使用
    开发工程师必备————【Day29】Django补充(四)
    吴恩达机器学习-可选实验:使用ScikitLearn进行线性回归(Linear Regression using Scikit-Learn)
    场景交互与场景漫游-场景漫游器(6)
    【二分】二分模板+二分题目
    Nginx多IP端口路由配置
  • 原文地址:https://blog.csdn.net/stone_tigerLI/article/details/134547688