もどくんちゃんねる ガジェット部

自転車、ガジェット、映像制作、CG、Blender など

画像処理 C言語 Pタイル法を使って2値化する方法 p-タイル

f:id:KANSONINGEN:20210117213027p:plain

pタイル法の課題が出て難しい解説ばかりだったので今日は簡単に思えるように説明していきます。

 

 

pタイル法とは

f:id:KANSONINGEN:20210117212633p:plain

pタイル法は簡単に説明すると上のような画像の画素値分布のグラフがあったときに、グラフを見ていい感じのところを閾値に設定するという方法です。

この場合だと大体90くらいに設定するのが良いと思います。

 

このようにグラフを見れば簡単な方法ですが、プログラムするとなると少しむずかしいですよね。

その手順を解説します。

 

実装手順

まず関数の入力は「入力画像」と「2値化の基準とする値の画素全体に対する割合」

出力は出力画像となります。

そして求めたいのが、「2値化の基準とする値の画素全体に対する割合」のときの画素値です。

①入力画像に対して、それぞれの画素値に対して何個の画素があるか計算する。

とてもわかりにくいこと言ってますが、

画素値が0の画素は15個

画素値が1の画素は30個

画素値が2の画素は56個

・・・

画素値が255の画素は4個

というように計算していきます。

プログラムではこの部分に当たります。

for(y=0;y<img1->s.y;y++){
  for(x=0;x<img1->s.x;x++){
      for(i=0;i<=255;i++){ //輝度のループ
             if(img1->val[x][y]==i){ //輝度のカウント
                   kido[i]=kido[i]+1;
             }
      }
   }
} 

できた値をprintfでもして書き出してエクセルなどでグラフにするとこのようになります。

グラフを見れば閾値にするべき値は簡単にわかりますが、プログラムですのでそう簡単にはいきません。

f:id:KANSONINGEN:20210117215055p:plain

kido[0]からkido[255]のグラフ


 

 

②画素の総和の計算をする

次に画素の総和の計算をします。

 for(i=0;i<=255;i++){ //すべての画素値の合計の計算,画素値は255までだからそこまでループ
  printf("画素値\t%d\t数\t%d\t総和\t%d\n",i,kido[i],allData); //デバッグ
  allData =allData + kido[i];
}

 この部分ですね。alldataをループの度にprintfすればその値でこのようなグラフがかけます。

f:id:KANSONINGEN:20210117215845p:plain

総和のグラフ

このようにかけます。

プログラムではこのグラフを見て、画素値が90の場所の割合を見てそれを入力値とします。

ただし、この90というのは結果論なのでもともとはわかっていないことに注意してください。(自分でも何言ってるかわからなくなってきた)

 

つまりその割合がrateの値になります。

まあグラフをみてもらえばわかるでしょう。

 

③画素値が(今回の場合だと)90以下の画素数を記録する。

画素値が90以下のものを記録します。このときに入力値であるrateつまり90あたりの割合が役に立ちます。

 th1 =allData*rate; //rateまでの合計

 これで計算できます。

 

④th1になるときの画素値を記録する。

allData=0;
/*th1より大きくなるときの画素値を閾値にする*/
for(i=0;i<=255;i++){
 allData =allData + i*kido[i];

 if(allData > th1){ //th1より大きくなったら、その時の画素値をth2に記録し、ループから抜ける。
  th2 = i;
  break;
 }
}

こんな漢字にします。

こうすることでやっと割合から閾値にしたい画素値を求めることができました。

 

⑤求めた閾値で2値化

後はいつもどおり2値化するだけです。

 

最後にコードを載せておきます。

 

実際のコード

/*img1は入力画像、img2は出力画像、rateは2値化の基準とする割合*/
void p_tyle(Image *img1,Image *img2,double rate){ 
    int x,y;
    int i;
    int kido[256]; //輝度の数を格納する配列、例えばkido[1]=3の場合 輝度が1の画素が3つあるという意味である。
    int allData=0//すべての画素値の合計値の格納
    int th1//rateの分け目
    int th2//求めたい閾値
    for(i=0;i<=255;i++){ //配列の初期化
      kido[i]=0;
    }

    /*それぞれの輝度がいくつあるか調査する*/
    for(y=0;y<img1->s.y;y++){
      for(x=0;x<img1->s.x;x++){
        for(i=0;i<=255;i++){ //輝度のループ
          if(img1->val[x][y]==i){ //輝度のカウント
            kido[i]=kido[i]+1
          }
        }
      }
    }
    
    for(i=0;i<=255;i++){ //すべての画素値の合計の計算,画素値は255までだからそこまでループ
        printf("画素値\t%d\t\t%d\t総和\t%d\n",i,kido[i],allData); //デバッグ
        allData =allData + kido[i];
    }

    th1 =allData*rate;  //rateまでの合計

    allData=0
    /*th1より大きくなるときの画素値を閾値にする*/
    for(i=0;i<=255;i++){
        allData =allData + i*kido[i];

        if(allData > th1){ //th1より大きくなったら、その時の画素値をth2に記録し、ループから抜ける。
          th2 = i;
          break;
        }
    }

    /*後は求めたth2を閾値に2値化する*/
    fory=0y<img1->s.yy++ ){
      forx=0x<img1->s.xx++ ){
        ifimg1->val[x][y] >= th2 ){
          img2->val[x][y] = 1;
        } 
        else{
          img2->val[x][y] = 0;
        }                       
      }
    }

}

 

以上です。

お役に立つと嬉しいです。