2 値化された画像の中に、非ゼロのピクセルが隣接してできた領域がある。このような非ゼロが隣接してつながった領域がオブジェクトとしてみなされる。例えば、下図では、4 つのオブジェクトが見られる。2 値画像では、情報を持つピクセルの値は 1 であり、情報を持たないピクセルの値は 0 であるので、画像に保存した際に、オブジェクトは白になる。
findContours
メソッドの使い方
OpenCV では、オブジェクトの輪郭を検出するメソッド findContours
が用意されている。このメソッドでは、画像中に含まれるすべてのオブジェクトを検出して、それぞれのオブジェクトにに有のラベル番号を振り分けている。ラベル番号は 1、2、3、・・・のようになり、オブジェクト分だけ振り分けられる。背景は、0 としてラベリングされる。このメソッドは、オブジェクトのラベリング結果とともに、輪郭線の情報およびオブジェクトの階層構造情報を返す。
label, contours, hierarchy = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
findContours
の引数
findContours
に与える RETR_LIST
は、輪郭検出モードを表す定数である。FETR_LIST
はすべての輪郭を同等に扱って検出するモードである。この他に、(入れ子構造となっている輪郭の場合)もっとも外側の輪郭だけを検出するモード RETR_EXTERNAL
、相対的な階層情報(親と子)を備えて全ての輪郭を検出するモード RETR_CCOMP
、絶対的な階層情報を備えてすべての輪郭を検出するモード RETR_TREE
がある。
findContours
に与えるもう一つの CHAIN_APPROX_NONE
は輪郭の近似方法を指定するための定数となっている。CHAIN_APPROX_NONE
は、輪郭線上のすべての点を検出する方法となっている。このほかに、CHAIN_APPROX_SIMPLE
があり、これは輪郭の冗長な点を削除して、必要最小限の点を検出する方法である。例えば、長方形のオブジェクトであれば、4 つの頂点の情報だけが保持される。
findContours
の戻り値
findContours
は 3 つの値を返す。それぞれは、画像のラベリング結果、オブジェクトの輪郭座標およびオブジェクトの改装情報となっている。
labels | 画像のラベリング結果を保持している二次元配列。配列の要素は、各ピクセルのラベル番号となっている。 | |
contours | オブジェクトの輪郭座標を保持している配列。 | |
hierarchy | オブジェクトの階層構造情報を保持している配列。 |
オブジェクト検出例
オブジェクト検出例(輪郭線)
次のサンプルは、画像中のチューリップの花部分を探して、その輪郭を白い線で囲む例である。具体的に、チューリップの花部分の 2 値画像を作成して、その 2 値画像に対して findContours
メソッドを適用し、すべてのオブジェクト(チューリップの花)を検出するという手順となっている。チューリップの花の部分の検出は、BGR 色空間から HSV 色空間に変換し、Hue の値に閾値を設けてピンク色の部分を抽出している。また、500 ピクセル未満の小さいオブジェクトは無視するように制御している。
import cv2
import numpy as np
# load image, change color spaces, and smoothing
img = cv2.imread('tulip.jpg')
img_HSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_HSV = cv2.GaussianBlur(img_HSV, (9, 9), 3)
# detect tulips
img_H, img_S, img_V = cv2.split(img_HSV)
_thre, img_flowers = cv2.threshold(img_H, 140, 255, cv2.THRESH_BINARY)
cv2.imwrite('tulips_mask.jpg', img_flowers)
# find tulips
labels, contours, hierarchy = cv2.findContours(img_flowers, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
for i in range(0, len(contours)):
if len(contours[i]) > 0:
# remove small objects
if cv2.contourArea(contours[i]) < 500:
continue
cv2.polylines(img, contours[i], True, (255, 255, 255), 5)
# save
cv2.imwrite('tulips_boundingbox.jpg', img)
オブジェクト検出例(バウンディングボックス)
次のサンプルは、同様にチューリップの花の部分を検出して、それを矩形で囲む例となっている。findContours
により検出された輪郭線を boundingRect
に代入し、その矩形外枠の座標を計算して、矩形を描く手順となっている。
import cv2
import numpy as np
# load image, change color spaces, and smoothing
img = cv2.imread('tulip.jpg')
img_HSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_HSV = cv2.GaussianBlur(img_HSV, (9, 9), 3)
# detect tulips
img_H, img_S, img_V = cv2.split(img_HSV)
_thre, img_flowers = cv2.threshold(img_H, 140, 255, cv2.THRESH_BINARY)
cv2.imwrite('tulips_mask.jpg', img_flowers)
# find tulips
labels, contours, hierarchy = cv2.findContours(img_flowers, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
for i in range(0, len(contours)):
if len(contours[i]) > 0:
# remove small objects
if cv2.contourArea(contours[i]) < 500:
continue
rect = contours[i]
x, y, w, h = cv2.boundingRect(rect)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 10)
# save
cv2.imwrite('tulips_boundingbox.jpg', img)
その他
findContours
はオブジェクトの輪郭を検出する関数であるが、上のサンプルにもあるようにオブジェクト検出にも応用できる。ただし、オブジェクト検出が目的であれば、OpenCV では効率のよい connectedComponents
および connectedComponentsWithStats
メソッドが提供されている。また、輪郭の検出だけであれば、エッジ検出に使われる Canny アルゴリズムなども使える。