OpenCV (Python3) を利用して画像を 2 値化

閾値処理

1 枚の写真の中から特定の色を抽出する際に、閾値を利用して処理することができる。閾値処理は、画像全体に対して 1 つだけの閾値を決めて処理する方法(閾値処理)と、画像の各ピクセルに応じて異なる閾値を決めて処理する方法(適応閾値処理)とある。

閾値処理 threshold

OpenCV で用意されている閾値処理には、以下の種類がある。

閾値の種類処理
THRESH_BINARY閾値を超えたピクセルは最大値なり、それ以外のピクセルは 0 になる。
THRESH_BINARY_INV閾値を超えたピクセルは 0 になり、それ以外のピクセルは最大値になる。
THRESH_TRUNC閾値を超えたピクセルは閾値と同じ値になり、それ以外のピクセルは変更されない。
THRESH_TOZERO閾値を超えたピクセルは変更されず、それ以外のピクセルは 0 になる。
THRESH_TOZERO_INV閾値を超えたピクセルは 0 になり、それ以外のピクセルは変更されない。

THRESH_BINARYTHRESH_BINARY 使い方

THRESH_BINARYTHRESH_BINARY を使用して画像の 2 値化を行い、画像中の植物個体を検出する例を示す。ここでは次に示した画像をサンプルとして使う。この画像には、緑色の葉を持つ植物個体と赤色の葉を持つ植物個体の両方が含まれている合成画像である。

ミチタネツケバナ(緑と赤)の写真

緑色と赤色を検出するには L*a*b* 色空間における a* の値が適しているので、ここでは画像を読み込んでから L*a*b* 色空間に変化して、続いて a* の値に閾値を設けて緑色と赤色を検出してみる。一般に、a* < 127.5 のときに画像が緑みを帯び、a* > 127.5 のときに画像が赤みを帯びる。ここでは、この値をこの画像の特徴に応じて、少し修正してから使う。

緑色の個体を検出するのに、閾値として a* < 110 を使う。THRESH_BINARY_INV を使って、a* 値が 110 を超えたピクセルを 0(黒)にして、それ以外(つまり 110 以下)を 255(白)にする処理を行う。続いて、赤色の個体を検出するのに、THRESH_BINARY を使って、閾値として a* > 135 を使う。a* 値が 135 を超えたピクセルを 255(白)にし、それ以外のピクセルを 0(黒)にする処理を行う。

import cv2

img_BGR = cv2.imread("sample.jpg")

# L*a*b*
img_Lab = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2Lab)
img_Lab_L, img_Lab_a, img_Lab_b = cv2.split(img_Lab)


# a* < 110
_thres, img_green = cv2.threshold(img_Lab_a, 110, 255, cv2.THRESH_BINARY_INV)

# a* > 135
_thres, img_red = cv2.threshold(img_Lab_a, 135, 255, cv2.THRESH_BINARY)

# mergen two green and red plants
img_plant = cv2.add(img_green, img_red)

cv2.imwrite('opencv.output.a.jpg', img_Lab_a)
cv2.imwrite('opencv.output.a.green.jpg', img_green)
cv2.imwrite('opencv.output.a.red.jpg', img_red)
cv2.imwrite('opencv.output.a.plants.jpg', img_red)
緑色の植物個体 ミチタネツケバナ(緑と赤)の写真;緑色の個体を検出した結果
赤色の植物個体 ミチタネツケバナ(緑と赤)の写真;赤色の個体を検出した結果
植物個体

両方の画像をそのまま合わせたので、ゴミが多く見られる。この場合、画像を足し合わせる前に、面積の小さい領域を除去する処理を行うと、足し合わせ後の画像全体がきれいになる。
ミチタネツケバナ(緑と赤)の写真;すべての個体を検出した結果

適応閾値処理 adaptiveThreshold

threshold メソッドでは閾値を指定して、画像全体に対して 2 値化をしている。これに対して、adaptiveThreshold メソッドを用いれば、あるピクセルを 2 値化したい場合、そのピクセルを中心とする n×n ピクセルのデータを用いて、閾値計算を行い、2 値化を行う。adaptiveThreshold は次のように、6 つの引数を使って指定する。

cv2.adaptiveThreshold(img, maxValue, adaptiveMethod, thresholdType, blockSize, C)

img は、入力画像で、maxValue は閾値を満たすピクセルに与える値となる。続いて、adaptiveMethod は適応閾値処理の種類を指定して平均値 ADAPTIVE_THRESH_MEAN_C あるいは標準化された平均値 ADAPTIVE_THRESH_GAUSSIAN_C のどちらかを指定する。また、thresholdType には、上で示した 5 つの閾値処理の種類を指定する。blockSize は、近傍ピクセルのサイズを奇数で指定する。また、C には定数を指定する。閾値処理がすべて終えたときに得られた閾値から最終的に定数 C の値を引いて、最終結果として出力される。

import cv2

img = cv2.imread("sample.jpg")
img_Lab = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2Lab)
img_Lab_L, img_Lab_a, img_Lab_b = cv2.split(img_Lab)
img = cv.adaptiveThreshold(img_gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 9, 0)

cv.imwrite("output.jpg", img)