Pynote

Python、機械学習、画像処理について

OpenCV - connectedComponents() で連結成分のラベリング

連結成分のラベリング

連結成分のラベリングとは、2値画像を入力としたとき、繋がっている非0のピクセルごとにラベルを割り当てる処理である。
OpenCV では、connectedComponents() で行える。


# アルゴリズムを指定しない場合
retval, labels = cv.connectedComponents(image[, labels[, connectivity[, ltype]]])
# アルゴリズムを指定する場合
retval, labels = cv.connectedComponentsWithAlgorithm(image, connectivity, ltype, ccltype[, labels])

2値画像を入力として、ラベリング結果を返す。
ラベルは背景を 0 として、1, 2, \cdots と振られる。

引数

  • image: 2値画像
  • labels: ラベリング結果 (引数経由で受け取る場合)
  • connectivity: 接続していると判定する基準
    • 4: 4連結
    • 6: 8連結
  • ltype: ラベリング結果の型
  • ccltype: アルゴリズム

返り値

  • retval: ラベル数 (背景ラベル 0 を含む)
  • labels: ラベリング結果

構造情報付きで連結成分のラベリング

# アルゴリズムを指定しない場合
retval, labels, stats, centroids =cv.connectedComponentsWithStats(
    image[, labels[, stats[, centroids[, connectivity[, ltype]]]]])
# アルゴリズムを指定する場合
retval, labels, stats, centroids = cv.connectedComponentsWithStatsWithAlgorithm(
    image, connectivity, ltype, ccltype[, labels[, stats[, centroids]]])

引数

  • image: 2値画像
  • labels: ラベル (引数経由で受け取る場合)
  • stats: 各ラベルの構造情報 (引数経由で受け取る場合)
    • (NumLabels, 5) の int 型の配列
    • 各列の内容は左上の x 座標、y 座標、幅、高さ、面積
  • centroids: 重心 (引数経由で受け取る場合)
    • (NumLabels, 2) の float 型の配列
    • 各列の内容はx 座標、y 座標
  • connectivity: 接続していると判定する基準
    • 4: 4連結
    • 6: 8連結
  • ltype: ラベルの型
  • ccltype: アルゴリズム

サンプルコード

2値画像を読み込む。

今回使用する画像

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 画像をグレースケール形式で読み込む。
src = cv2.imread('binary.png', 0)

# 2値画像を表示する。
plt.imshow(src, cmap=plt.cm.gray)
plt.show()

連結成分のラベリングを行う。

# 連結成分のラベリングを行う。
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(src)

# ラベル数
print('number of labels:', retval)  # 6 (背景 + 5個のオブジェクト)

# ラベリング結果
print(labels.shape, labels.dtype)  # (362, 420) int32
plt.imshow(labels)
plt.show()


構造情報を表示する。

print(stats.shape, stats.dtype)
for i, row in enumerate(stats):
    print('label {}'.format(i))
    print('  topleft: ({}, {})'.format(row[cv2.CC_STAT_LEFT], row[cv2.CC_STAT_TOP]))
    print('  size: ({} px, {} px)'.format(row[cv2.CC_STAT_WIDTH], row[cv2.CC_STAT_HEIGHT]))
    print('  area: {}'.format(row[cv2.CC_STAT_AREA]))
(6, 5) int32
label 0
  topleft: (0, 0)
  size: (420 px, 362 px)
  area: 126715
label 1
  topleft: (51, 32)
  size: (83 px, 83 px)
  area: 3486
label 2
  topleft: (324, 40)
  size: (71 px, 71 px)
  area: 5041
label 3
  topleft: (158, 79)
  size: (118 px, 117 px)
  area: 10910
label 4
  topleft: (76, 179)
  size: (88 px, 87 px)
  area: 3872
label 5
  topleft: (175, 256)
  size: (63 px, 62 px)
  area: 2016

重心を表示する。

print(centroids.shape, centroids.dtype)
for i, row in enumerate(centroids):
    print('label {} topleft: ({}, {})'.format(i, row[0], row[1]))
(6, 2) float64
label 0 (208.99150850333425, 188.06918675768458)
label 1 (91.75301204819277, 86.66666666666667)
label 2 (359.0, 75.0)
label 3 (216.99459211732355, 137.0)
label 4 (119.5, 222.0)
label 5 (203.53968253968253, 286.5)