オブジェクト輪郭検出

2 値化された画像の中に、非ゼロのピクセルが隣接してできた領域がある。このような非ゼロが隣接してつながった領域がオブジェクトとしてみなされる。例えば、下図では、4 つのオブジェクトが見られる。2 値画像では、情報を持つピクセルの値は 1 であり、情報を持たないピクセルの値は 0 であるので、画像に保存した際に、オブジェクトは白になる。

2 値化画像の連結領域

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 値画像を作成して、その 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)
チューリップの画像 OpenCV を利用してオブジェクトの輪郭線を検出する方法

オブジェクト検出例(バウンディングボックス)

次のサンプルは、同様にチューリップの花の部分を検出して、それを矩形で囲む例となっている。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)
チューリップの画像 OpenCV を利用してオブジェクトの輪郭を検出し、輪郭からバウンディングボックスを検出し、矩形で囲む方法

その他

findContours はオブジェクトの輪郭を検出する関数であるが、上のサンプルにもあるようにオブジェクト検出にも応用できる。ただし、オブジェクト検出が目的であれば、OpenCV では効率のよい connectedComponents および connectedComponentsWithStats メソッドが提供されている。また、輪郭の検出だけであれば、エッジ検出に使われる Canny アルゴリズムなども使える。