自動でしきい値を決めて二値化してくれる画像処理と言えば、大津の二値化ですが、OpenCVにはadaptiveThreshold(適応的しきい値処理)という良さげな処理があります。
この adaptiveThreshold は画像全体に影や照明のムラがある場合に、効果を発揮します。
以下に大津の二値化とadaptiveThreshold の処理例を示します。
使用したプログラム
import cv2
img = cv2.imread("image.jpg")
# カラー→モノクロ変換
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 元画像の表示
cv2.imshow("src image", img)
# 大津の二値化
_, dst1 = cv2.threshold(
img, 0, 255, cv2.THRESH_OTSU)
cv2.imshow("THRESH_OTSU", dst1)
# 適応的しきい値処理
dst2 = cv2.adaptiveThreshold(
img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 51, 20)
cv2.imshow("adaptiveThreshold", dst2)
cv2.waitKey(0)
元画像
大津の二値化
adaptiveThreshold
元画像の左側に影のある例を示しています。
今回の画像は、文字の部分を黒く、それ以外の部分を白く二値化することを想定しているのですが、大津の二値化では、自動でしきい値は決めてくれるものの、画像全体に輝度値のムラがある場合は、うまく二値化してくれません。それに比べて adaptiveThreshold ではある程度狙った通りに二値化されています。
Pythonですが、この関数定義は以下のようになっています。
adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
src | 入力画像 |
maxValue | 二値化後の輝度値 |
adaptiveMethod | 適応的しきい値処理で使用するアルゴリズム
cv2.ADAPTIVE_THRESH_MEAN_C
もしくは cv2.ADAPTIVE_THRESH_GAUSSIAN_C
|
thresholdType | 二値化の種類 cv2.THRESH_BINARY もしくは cv2.THRESH_BINARY_INV
|
blockSize | しきい値計算のための近傍サイズ |
C | 平均あるいは加重平均から引かれる値 |
戻り値 | 処理後画像 |
(参考)
http://opencv.jp/opencv-2svn/c/imgproc_miscellaneous_image_transformations.html
だいたい上記のような説明されている場合が多いのですが、よく分からないですよね?!
ただ、やっている事自体は意外と簡単です。
実際にOpenCV内部で行われている処理と異なると思いますが、処理の意味合い的には以下の通りになります。
1.adaptiveMethodの設定に従って、平均化(blur)もしくはガウシアンフィルタ(GaussianBlur)で入力画像をぼかします。この時のカーネルのサイズが blockSize x blockSize となります。
2.元画像とぼかした画像の差分処理を行います。
3.差分画像を指定したしきい値( C ) で二値化し、白黒反転します。
すると、adaptiveThreshold で処理した二値化画像が取得できます。
重要なのは、処理の途中に平均化 もしくは ガウシアンフィルタで二値化したい部分をぼかしている部分です。そのため、二値化したい部分の大きさ(今回の例では文字の線幅)に対して十分大きな blockSize を指定する必要があります。
blockSizeを変えながら処理をすると、
blockSize = 5 のとき
blockSize = 21 のとき
blockSize = 51 のとき
このようにblockSizeが小さいと、文字の輪郭が二値化され、blockSizeを大きくすると、太い文字も文字全体が二値化されます。
処理の目的的にはトップハット、ボトムハットに似ています。
(参考)
実際の用途的には、画像にムラがあるときに、小さなゴミやキズなどの検出に用いられます。
逆に大きな領域を二値化する場合には adaptiveThreshold は不向きなのでご注意下さい。
ちなみに買ったチョコビ