2 値化された画像の中に、非ゼロのピクセルが連続してできた領域を connected component などと呼んだりする。この connected component がオブジェクトとみなされる。例えば、下図では、4 つのオブジェクトが見られる。2 値画像では、情報を持つピクセルの値は 1 であり、情報を持たないピクセルの値は 0 であるので、画像に保存した際に、オブジェクトは白になる。
OpenCV では、オンブジェクト(連結領域)を検出するメソッドとして connectedComponents
と connectedComponentsWithStats
が用意されている。前者は、画像中のオブジェクトを検出して返す簡単なメソッドであるのに対して、後者はオブジェクトのサイズや重心などの情報も合わせて返す仕様となっている。
connectedComponents
OpenCV の connectedComponents
は、次のように使う。このメソッドでは、画像中に含まれるすべてのオブジェクトを検出して、それぞれのオブジェクトにに固有のラベル番号を振り分けている。ラベル番号は 1、2、3、・・・のようになり、オブジェクト分だけ振り分けられる。背景は、0 としてラベリングされる。このメソッドは、オブジェクトの数とラベリング結果を返す。
nlabels, labels = cv2.connectedComponents(img)
nlabels | ラベルの数。各オブジェクトに番号がラベリングされており、nlabels はそのラベルの数を表す。ただし、画像の背景の番号は 0 とラベリングされているので、実際のオブジェクトの数は nlabels - 1 となる。 | |
labels | 画像のラベリング結果を保持している二次元配列。配列の要素は、各ピクセルのラベル番号となっている。 |
オブジェクト検出例
次のサンプルは connectedComponents
メソッドを使って、画像中のチューリップの花の部分を検出する例である。具体的に、チューリップの花部分の 2 値画像を作成して、その 2 値画像に対して メソッドを適用し、すべてのオブジェクト(チューリップの花)を検出するという手順となっている。チューリップの花の部分の検出は、BGR 色空間から HSV 色空間に変換し、Hue の値に閾値を設けてピンク色の部分を抽出している。また、検出したチューリップの部分を単色で塗り替えている。
import cv2
import numpy as np
import random
# 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
nlabels, labels = cv2.connectedComponents(img_flowers)
img = np.zeros(img.shape[0:3])
height, width = img.shape[0:2]
cols = []
# background is label=0, objects are started from 1
for i in range(1, nlabels):
cols.append(np.array([random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]))
for i in range(1, nlabels):
img[labels == i, ] = cols[i - 1]
# save
cv2.imwrite('tulips_object.jpg', img)
connectedComponentsWithStats
OpenCV の connectedComponentsWithStats
は、次のように使う。このメソッドは、オブジェクトの位置に加えて、オブジェクトのサイズや重心などの情報も合わせて返す仕様となっている。
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(img)
nlabels | ラベルの数。各オブジェクトに番号がラベリングされており、nlabels はそのラベルの数を表す。ただし、画像の背景の番号は 0 とラベリングされているので、実際のオブジェクトの数は nlabels - 1 となる。 | |
labels | 画像のラベリング結果を保持している二次元配列。配列の要素は、各ピクセルのラベル番号となっている。 | |
stats | オブジェクトのバウンディングボックス(開始点の x 座標、y 座標、幅、高さ)とオブジェクトのサイズ。 | |
centroids | オブジェクトの重心。 |
オブジェクト検出例
次のサンプルは connectedComponentsWithStats
メソッドを使って、画像中のチューリップの花の部分を検出する例である。connectedComponentsWithStats
の戻り値には面積が含まれているので、これを利用して、面積が 500 ピクセル未満の領域は無視する。
import cv2
import numpy as np
import random
# 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
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(img_flowers)
img = np.zeros(img.shape[0:3])
height, width = img.shape[0:2]
cols = []
# background is label=0, objects are started from 1
for i in range(1, nlabels):
cols.append(np.array([random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]))
for i in range(1, nlabels):
if stats[i][4] >= 500:
img[labels == i, ] = cols[i - 1]
# save
cv2.imwrite('tulips_object.jpg', img)