2016. 7. 17.
石立 喬

OpenCV3.1.0とVisual C++ 2015による画像処理と認識(3)

----- グローバルしきい値とローカルしきい値を用いた二値化の比較 -----

 旧バージョンについての「OpenCVとVisual C++による画像処理と認識(2)」とほぼ同じ内容を、OpenCV3.1.0で確認した。OpenCV3.0以降に組み入れられた、グローバルしきい値法の新しいトライアングル法(THRESH_TRIANGLE)についても調べた。
 しきい値を外部から調節するトラックバーの使用は省略した。

二値化とは
 原画像を、濃度0と濃度255の二値しかない画像に変換するもので、物体の識別、文字の読み取りなどの前処理に有用である。原画像はグレイスケール画像とし、ピクセルごとにしきい値と比較して、原画像の濃度がしきい値より小さいと0に、大きいと255に設定して二値画像を作る(255と0を逆にすることもある)。
 しきい値の選び方に二種類あり、全画像に同一のしきい値を用いるグローバルしきい値法と、画像の各ピクセル位置部分に応じて最適のしきい値を使用するローカル(適応型)しきい値法がある。

グローバルしきい値法
 画像全体にわたって同一のしきい値を使うグローバルしきい値法のためにOpenCV3.1.0に用意された関数に、
  threshold(src, dst, thres, maxval, THRESH_BINARY);
がある。srcはグレイスケールの原画像、dstは結果として得られる二値画像で、しきい値thresは外部から与え、二値画像の最大濃度値maxvalは255を用いる。最適なしきい値thresを選ぶには、人が介在したり、自動的にヒストグラムを採って求めたりする必要がある。THRESH_BINARYは二値化の方法を指定するタイプの一つで、THRESH_BINARY_INVを用いると、255と0が逆転する。
 最適なしきい値thresを自動的に求めるのに、「大津の方法」と「トライアングル法」があり、これを使うには、THRESH_OTSUやTHRESH_TRIANGLEをタイプ指定に追加して、
  threshold(src, dst, 0, maxval, THRESH_BINARY | THRESH_OTSU);
などとする。thresの値は、外部から設定しても無視され、一般には0を入れておく。
 OpenCV3.0から追加されたトライアングル法は、明暗いずれかに偏ったヒストグラムを持つ画像において、ヒストグラムの頻度の小さい部分にしきい値を求めるのに適した方法であり、顕微鏡下での染色体観測に用いられた。ヒストグラムの幅の狭いピークと、ヒストグラム全体の幅を、それぞれ値1.0に正規化し、得られた二等辺直角三角形からしきい値を求めるので、トライアングル法と呼ばれる。したがって、ヒストグラムが比較的均一に分布する一般的な画像には向かない。

ローカル(適応型)しきい値法
 画像の部分部分で、最適の異なったしきい値を使うローカルしきい値法のためにOpenCV3.1.0に用意された関数に、
  adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C);
がある。
  adaptiveMethodには、ADAPTIVE_THRESH_MEAN_CとADAPTIVE_THRESH_GAUSSIAN_Cの二種類があり、ローカルしきい値を求める方法を指定する。APAPTIVE_THRESH_MEAN_Cは、注目するピクセルの周辺のピクセル値の単純平均を取り、外部から与えられる一定値C(オフセットに相当する)を引くことを意味する。ADAPTIVE_GAUSSIAN_Cは、周辺ピクセルにガウス分布に従った重みを付けて加算し、一定値を引く。
 これらの方法は、自動的ではない。周辺ピクセルをどの範囲までに選ぶかを指定するblockSIzeと、しきい値を補正するCは、外部から設定しなければいけない。これには、経験と試行錯誤が必要になる。blockSizeは奇数でないと、ビルドはできても実行時にエラーとなる。Cの値は、周辺ピクセル値の分散が小さい場合には、それに応じて小さい値を用いないと、効果が過大になる。同一濃度の部分が広範囲に存在する場合には、僅かのオフセットCで255か0かが決まってしまう。

グローバルしきい値による二値化の方法を比較する
 図1は使用したプログラムで、グローバルしきい値法3種類を実行する。具体的には、
1)原画像をグレイスケール画像として読み込む。
  imread関数の第2引数にIMREAD_GRAYSCALEを記述する。
2)グローバルしきい値法を用い、実行結果を見ながら、手動で(自動的でなく)しきい値を選んで二値化する。
  threshold関数を用い、thresを80(試行錯誤の結果)に設定する。
3)グローバルしきい値法の大津の方法で二値化する。しきい値は0にしておく(与えても無視される)。
  threshold関数で、THRESH_BINARYの他にTHRESH_OTSUを追加する。
4)グローバルしきい値法のトライアングル法で二値化する。しきい値は0にしておく(与えても無視される)。
  threshold関数で、THRESH_TRIANGLEを追加する。


図1 グローバルしきい値による二値化の方法を比較するプログラム


グローバルしきい値による二値化の比較で得られた結果
 図2は得られた結果で、これら二種類の原画像を用いた限りでは、結果を見ながら最適のグローバルしきい値を求めた、目視手動の方法が最も良い。大津の方法は、画面に占める白と黒の比率を同じにしようと努力した結果が現れ、画像としての多くの情報が失われている。
 トライアングル法は、一番目の画像では、大津の方法と大差がないが、二番目の画像では、黒く潰れてしまった。トライアングル法は、やはり、明暗いずれかに大きなヒストグラム値を持ち、他は小さい頻度値で広く分布している画像でないと、良い結果が得られない。



図2 グローバルしきい値による二値化の比較結果


ローカル(適応型)しきい値による二値化の方法を比較する
 図3は使用したプログラムで、ローカルしきい値法2種類を実行する。具体的には、
1)原画像をグレイスケール画像として読み込む。
  imread関数の第2引数にIMREAD_GRAYSCALEを記述する。
2)比較のために、グローバルしきい値法(目視手動)を実行して表示する。
  threshold関数を用い、thresを80に設定する。
3)ローカルしきい値法の平均(mean)の方法で二値化する。
  adaptiveThreshold関数を用い、試行錯誤の結果、blockSize = 41, C = 9とし、adaptiveMethodをADAPTIVE_THRESH_MEAN_Cとする。
4)ローカルしきい値法のガウス(gaussian)の方法で二値化する
  adaptiveThreshold関数を用い、同様にblockSize = 41, C = 9とし、adaptiveMethodをADAPTIVE_THRESH_GAUSSIAN_Cとする。


図3 ローカルしきい値法のプログラム


ローカルしきい値による二値化の比較で得られた結果
 図4は得られた結果で、これらの原画像を用いた限りでは、結果を見ながら最適のグローバルしきい値を求めた、目視手動の方法が最も良い。自動的にしきい値を決める方式のグローバル二値化に比べると、平均法もガウス法も、共に潰れることがなく、良好な結果が得られた。blockSizeを同じに選ぶと、ガウス法の方が、ローカル性が強いことが分かる。



図4 ローカルしきい値法の結果


ローカル(適応型)しきい値法で、ブロックサイズの影響を調べる
 ローカルしきい値法のadaptiveThreshold関数で、blockSizeを変えるとどのように結果が変わるか(Cは最適値に固定)を確認した。プログラムについては省略する。
 図5はその結果で、、タイトルに21, 8などとあるのは、blocklSize = 21でC = 8の意味である。blockSizeが小さいと、狭い範囲での部分的な白黒比が一定になる(Cによって白を多くしてあるが)ので、線画風になり、画像全体としての明部、暗部の差がなくなる。
 ADAPTIVE_GAUSSIAN_Cタイプを使うと、周辺ピクセルにガウス分布に従った重みを掛けて加算するので、ローカル性が出やすい。一般的には、MEANタイプの方が、blockSizeが小さくても効果が出やすいので、高速であり、利用価値が高い。図5では、平均法(MEAN)の方が、blockSizeを小さめにし、Cを大きく取ってある。ガウス法(GAUSSIAN)で同等の効果を得るには、blockSizeを大きく、Cを下げる必要があった。




図5 グローバルしきい値法で、ブロックサイズを変えて二値化した結果(上:平均法、下:ガウス法)


認識を目的とした画像で比較する
 車のナンバープレートや製品の製造番号を読み取るには、文字の二値化が欠かせない。光源にむらがあったり、日照の条件が悪い場合などの画像サンプルを用いて二値化を試みた結果を、図6と図7に示す。使用プログラムは省略する。
 各図で、左上は原画像をグレイスケール化したもの、右上はグローバルしきい値法(大津の方法)を用いたものを参考として示し、左下はローカルしきい値法のMEANを用いたもの、右下は同じくGAUSSIANを用いたものを、それぞれ試行錯誤により、ブロックサイズとオフセットの最適条件を求めて表示したものである。いずれの場合も、グローバルしきい値法を用いる必要があり、、その中では、MEANの方が、ブロックサイズを小さくすることができ、速度的に優位と思われる。
 図6は、対象とする文字が小さいので、ブロックサイズを小さくした。MEANとGAUSSIANの比較では、ガウス法の方が、一般的に大きめのブロックサイズを必要とした。図7では、文字を黒で表示するために、白黒を反転させてある。


図6 書籍の一部を二値化した結果



図7 電柱の表示を二値化した結果


結 論
 二値化の目的が何であるかによって、選ぶべき二値化手法が変わる。原画像全体の濃淡を表示させたい自然画像のような場合には、グローバルしきい値法が良く、しきい値を完全自動で決めたいなら大津の方法とトライアングル法があり、後者は、大部分が白地で、黒みがかった対象物を明確化したい場合に適し、OpenCV3.0から使用可能になった。しかし、面倒ではあるが、眼で見ながらしきい値を調節するのが最も良い結果が得られた。
 照明むらなどがあっても、そこにある物体、文字などをはっきり読み取りたい画像認識の場合には、ローカル(適応型)しきい値法がすぐれている。ローカルしきい値法には、周辺のピクセル値の単純平均を使用する平均法(THRESH_MEAN_C)とガウス分布による重みづけ加算を使うガウス法(THRESH_GAUSSIAN_C)があるが、いづれも、周辺ピクセルの範囲を広くすると、実行時間が遅くなる。平均法の方が、ブロックサイズが小さくても効果が得られるので、一般的には有用である。
 ローカルしきい値法は、完全自動とは言えない。周辺ピクセルのサイズは、対象とする図形などのサイズに合せて最適化する必要があり、オフセットも目で見ながら調節する方が、良い結果が得られる。
 クルマのナンバープレートの読み取りのような、屋外での応用には、ローカルしきい値法もやむを得ないが、工場内での製品検査などでは、光源を均一化し、固定しきい値でのグローバルしきい値法を使うのが高速化できて良いと思われる。

参考文献
大津の方法
 Nobuyoshi Otsu, "A THreshold Selection Method from Gray-Level Histofram" (1979)
 https://engineering.purdue.edu/kak/computervision/ECE661.08/OTSU_paper.pdf
トライアングル法
 G. W. Zack, W. E. Rogers and S. A. Latt, "Automatic Measurement of Sister Chromatid Exchange Frequency" (1977)
 http://e-archivo.uc3m.es/bitstream/handle/10016/14764/automatic_?sequence=1




「Visual C++の勉強部屋」(目次)へ