今回も画像の前処理についてだな。
「閾値処理」ってなんです?
簡潔にまとめると、白黒画像にして白黒はっきりさせることだ。
????
まあ順に説明していこうか。
閾値処理(2値化)のメリット
そもそも、白黒画像にして何かメリットってあるんですか?
まず、カラー画像より白黒画像のほうが情報量が少ない。したがって、処理速度が速くなる。これが最大のメリットだ。
また、情報量を落とすことで対象物がよりはっきりと映るようになる。はっきりした対象物は解析が容易になるため、精度面でもメリットがある。
確かに、これまでも処理速度は大事って言ってましたもんね。
これらのメリットがあるため、前処理として2値化は積極的に検討していいと思う。
これって、デメリットはないんですか?
2値化する際の閾値(境界)をどこにするかで精度に影響する。閾値を決めるのが容易ではないのがデメリットといったところだろうか。
事前準備
まずは以下の画像を見てほしい。数字が書かれているが、何個書かれているかね。

え~っと、30、50、80、100、120、150、180、200、255の9つですか??
フフ、、いいだろう。
なんです、不敵な笑みですね。
今回は最初に答え合わせをしておこう。以下は、先ほどの画像に閾値処理を加えたものだ。

あ、あれ、5と10もあったんですか!
かなり暗いが実は潜んでいたんだよ。他の数字も白色が際立っているだろう。
これが閾値処理をする効果で、対象物をより鮮明にすることができる。
これだけはっきりしていると、文字の読み取りとかもできそうですね。
うむ。閾値処理にもいくつか手法がある。今回はその手法も含めてみていくことにしよう。
閾値処理(cv2.threshold())
OpenCVの閾値処理にはcv2.threshold()を用いる。
cv2.threshold(src, thresh, maxval, type[, dst])
引数
引数 | 説明 |
---|---|
thresh | 閾値 |
maxval | THRESH_BINARYまたはTHRESH_BINARY_INV を使用した際の最大値 |
type | 閾値タイプ |
基本的なルらルとして、threshに指定した値を基準に閾値判定が行われる。
判定後、ルールに沿って各ピクセルの値が書き換えられる。
ルールって、どんなのがあるんです?
ルールは引数typeで定義する。いくつか種類があるが、閾値に満たないピクセルの値を0にしたり、その逆(閾値以上のピクセルを0にする)もある。
また、typeにTHRESH_BINARYやTHRESH_BINARY_INV を指定すると、閾値を基準に0またはmaxvalで指定した値に書き換えれる。
ほー、いろいろ種類があるんですね。
閾値処理の種類
先ほども少し触れた引数typeについてもう少し詳細に触れる。
typeの種類には以下のようなものがある。
- cv.THRESH_BINARY
- cv.THRESH_BINARY_INV
- cv.THRESH_TRUNC
- cv.THRESH_TOZERO
- cv.THRESH_TOZERO_INV
- cv.THRESH_MASK
- cv.THRESH_OTSU
- cv.THRESH_TRIANGLE
- cv.THRESH_DRYRUN
9種類も、結構多いですね。
今回は、そのうちの cv.THRESH_TOZERO_INVまでの5つを紹介する。
cv.THRESH_BINARY
バイナリ閾値は閾値処理のなかで最もスタンダードな手法である。threshで指定した値以上のピクセルはmaxValueに、それ以外は0に変換される。
th, dst = cv2.threshold(src, 3, 255, cv2.THRESH_BINARY)
cv2.imshow("opencv-threshold-example.jpg", dst)

cv2.THRESH_BINARY_INV
cv2.THRESH_BINARY_INVは、 cv2.THRESH_BINARY の逆だ。
逆だ。ということは?
煽るんじゃない。つまり、threshの値以下のピクセルはmaxValueの値に変換され、それ以外(thresh以上)は0に変換される。
逆ですね。
言っただろう。
th, dst = cv2.threshold(src,115,255, cv2.THRESH_BINARY_INV)
cv2.imshow("opencv-thresh-binary-inv.jpg", dst)

逆になると色も反転するんですね。
cv2.THRESH_TRUNC
この方法では、threshの値以上のピクセルはthreshの値に変換され、thresh以下の値のピクセルは元の値のままになる。
ちなみに、cv2.THRESH_TRUNCを指定したうえでmaxValueを定義しても意味がないから気を付けてほしい。
threshの値に置き換わる手法ってことですね。
th, dst = cv2.threshold(src,120,255, cv2.THRESH_TRUNC)
cv2.imshow("opencv-thresh-trunc.jpg", dst)

cv2.THRESH_TOZERO
この方法ではthreshの値以上のピクセルはもとの値のまま、それ以外は0に変換する。ここも、maxValueは指定しても無視される。
th, dst = cv2.threshold(src,120,255, cv2.THRESH_TOZERO)
cv2.imshow("opencv-thresh-tozero.jpg", dst)

120以下の数字が消えてますね。
cv2.THRESH_TOZERO_INV
最後にcv2.THRESH_TOZERO_INVだが、cv2.THRESH_TOZEROの逆で、 threshの値以下のピクセルはもとの値のまま、それ以外は0に変換される。
th, dst = cv2.threshold(src,120,255, cv2.THRESH_TOZERO_INV);
cv2.imshow("opencv-thresh-to-zero-inv.jpg", dst)

cv2.THRESH_TOZERO と逆で、120以下の数字が残ってますね。
まとめ
OpenCVの閾値処理について取り上げたが、情報量を極力落として正確な画像解析を行うにはかなり有効な手法だから、是非覚えておいてほしい。
Pythonではcv2.threshold()を使うんでしたよね。typeは多いから覚えられないけど、何とかなると思います。
(その根拠の無い自信、どこから湧いてくるんだ、、)
まあいい、今回はここまでにしよう。
ありがとうございました~~♪
【ポイント】
・閾値処理をすることで情報量を落とし、処理速度を高められる。
・閾値処理をすることで対象物がはっきり映るようになる。
・閾値の設定は難易度がやや高い
・Pythonでの閾値処理はcv2.threshold()
・閾値処理の種類は引数typeで指定する。