script day.log

大学生がなんとなく始めた、趣味やら生活のことを記録していく。

2値化手法について(判別分析法)

モード法,P-タイル法をやれば、次は判別分析法(大津の2値化)ですよね.
makose3p1229.hatenablog.com

makose3p1229.hatenablog.com

判別分析法の考え方

モード法はヒストグラムが明確なピークを持たなければ適切な閾値を求めることが困難である.
しかし,判別分析法では自動的かつ統計的処理により最適な閾値を求めることが可能である.
判別分析法とは,画像の濃度値のヒストグラム閾値\thetaで2つのクラスに分割したとき,
2つのクラス間の分離が最も良くなるように閾値を定める方法である.
実際には2つのクラスのクラス間分散とクラス内分散の比を最大にするように閾値\thetaを決める.
与えられた画像データをk以下の濃度値を持つ画素と,
kより大きい濃度値を持つ画素の2つのグループに分け,
それぞれクラス1,クラス2とする.クラスiの画素数を全画素数で正規化した値を
\omega_i(k),平均濃度値をM_i(k),分散を\sigma_i^{2}(k)とする(i=1,2)
全画素の平均濃度値をM_Tとおくと,クラス間分散\sigma_B^{2}


\begin{align}
 \sigma_B^{2} &= \omega_1(M_1 - M_T)^{2} + \omega_2(M_2 - M_T)^{2} \nonumber \\
              &= \omega_1\omega_2(M_1 - M_2)^{2} \nonumber
\end{align}

で,クラス内分散\sigma_W^{2}


\begin{align}
 \sigma_W^{2} = \omega_1\sigma_1^{2} + \omega_2\sigma_2^{2} \nonumber
\end{align}

で与えられる.ここで,\sigma_B^{2}/\sigma_W^{2}を最大にするには
\sigma_B^{2}を最大にすればよく,したがって\sigma_B^{2}
最大にするkを閾値\thetaとすれば良い.
\sigma_B^{2}(k)を求めるには以下の計算をすれば良い.
画像のヒストグラムh(k)とすると,
濃度レベルkまでの濃度分布における0次モーメント\bar{\omega}(k)
及び1次モーメント\bar{M}(k)はそれぞれ次の漸化式


\begin{align}
 \bar{\omega}(k) &= \bar{\omega}(k-1) + \frac{h(k)}{MN} \nonumber \\
 \bar{M}(k) &= \bar{M}(k-1) + k\frac{h(k)}{MN} \nonumber
\end{align}

で表され,クラス間分散\sigma_B^{2}

\sigma_B^{2}(k) = \frac{[M_T\bar{\omega}(k) - \bar{M}(k)]^{2}}{\bar{\omega}(k)[1-\bar{\omega}(k)]}

となる.ただし,\bar{\omega}(0)=h(0)/MN,\bar{M}(0)=0である.
そして,\sigma_B^{2}(k)を最大にするkを閾値\thetaとする.

判別分析法の実装

私が実装した判別分析法による閾値の決定部分を以下に示す.

 int histgram[256] = {0};
 int sum = 0;
        for (int i = 0; i < HEIGHT; i++){
            for (int j = 0; j < WIDTH; j++){
                histgram[image[i][j]]++;
                sum += image[i][j];
            }
        }

        double ave = sum / (HEIGHT * WIDTH);
        double ave1,ave2;
        double max = 0.0;
    
        for(int i=0;i<256;i++){
            int cnt1 = 0;
            int cnt2 = 0;
            long data = 0;
            double brkup1 = 0.0;
            double brkup2 = 0.0;	
            double tmp = 0;
		
            for(int j=0;j<i;j++){
                cnt1 += histgram[j];
                data += histgram[j] * j;
            }

            if(cnt1 != 0){
                ave1 = (double)data / (double)cnt1;

                for(int j=0;j<i;j++){
                    brkup1 += pow( (j- ave1), 2 ) * histgram[j];
                }
                brkup1 /= (double)cnt1; 
            }
		
            data = 0;
		
            for(int j=i;j<256;j++){
                cnt2 += histgram[j];
                data += histgram[j] * j;
            }
            if(cnt2 != 0){
                ave2 = (double)data / (double)cnt2;

                for(int j=i;j<256;j++){
                    brkup2 += pow( (j - ave2), 2 ) * histgram[j];
                }
		
                brkup2 /= (double)cnt2;
            }
		
            double class1 = (cnt1 * brkup1 + cnt2 * brkup2);
            double class2 = cnt1 * pow( (ave1 - ave), 2 ) 
                          + cnt2 * pow( (ave2 - ave), 2 );
		
            tmp = class2 / class1;
		
            if(max < tmp){
                max = tmp;
                mode = i;
            }
        }

判別分析法の問題点

判別分析法ではどのようなヒストグラムでも一意に閾値を決定出来る.
しかし判別分析法も画像中のパターン領域と背景領域の画素数に大きな差がある場合,
パターン領域と背景領域が明確に分かれない閾値が設定される場合がある.
ラプラシアンフィルタを適用することで,隣接する画素との濃度値の変化分が大きい画素を
抽出することができる.抽出された画素とその周辺の画素を利用して濃度ヒストグラム
作成すると,フィルタを適用していない濃度ヒストグラムに比べて濃度ヒストグラム
双峰化する.ヒストグラムが双峰化されていると,判別分析法で精度の高い
最適なしきい値設定が可能となり、この問題を回避する事ができる.



2値化には飽きてきたので,次はフィルタリングしようかな.