Pynote

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

OpenCV - connectedComponents で連結成分のラベリングを行う方法

連結成分のラベリング

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


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

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

引数

  • image: 2値画像
  • connectivity: 接続していると判定する基準
    • 4: 4連結
    • 6: 8連結
  • ltype: ラベリング結果の型
  • ccltype: アルゴリズム
    • cv2.CCL_WU: 4, 8連結の場合は SAUF アルゴリズムを使用する。
    • cv2.CCL_DEFAULT: 8連結の場合は BBDT アルゴリズム、4連結の場合は SAUF アルゴリズムを使用する。
    • cv2.CCL_GRANA: 8連結の場合は BBDT アルゴリズム、4連結の場合は SAUF アルゴリズムを使用する。

返り値

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

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

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

引数

  • image: 2値画像
    • (NumLabels, 5) の int 型の配列
    • 各列の内容は左上の x 座標、y 座標、幅、高さ、面積
    • (NumLabels, 2) の float 型の配列
    • 各列の内容はx 座標、y 座標
  • connectivity: 接続していると判定する基準
    • 4: 4連結
    • 6: 8連結
  • ltype: ラベルの型
  • ccltype: アルゴリズム
    • cv2.CCL_WU: 4, 8連結の場合は SAUF アルゴリズムを使用する。
    • cv2.CCL_DEFAULT: 8連結の場合は BBDT アルゴリズム、4連結の場合は SAUF アルゴリズムを使用する。
    • cv2.CCL_GRANA: 8連結の場合は BBDT アルゴリズム、4連結の場合は SAUF アルゴリズムを使用する。

返り値

  • retval: ラベル数 (背景ラベル 0 を含む)
  • labels: ラベリング結果
  • stats: 各ラベルの構造情報
  • centroids: 重心

サンプルコード

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

入力画像

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

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

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

# ラベル数
print("number of labels:", retval)  # 6 (背景 + 5個のオブジェクト)
# ラベリング結果
print(labels.shape, labels.dtype)  # (362, 420) int32

fig, ax = plt.subplots(facecolor="w")
ax.imshow(labels)

plt.show()

出力画像

構造情報を表示する。

print(stats.shape, stats.dtype)
for i, row in enumerate(stats):
    print(f"label {i}")
    print(f"  topleft: ({row[cv2.CC_STAT_LEFT]}, {row[cv2.CC_STAT_TOP]})")
    print(f"  size: ({row[cv2.CC_STAT_WIDTH]} px, {row[cv2.CC_STAT_HEIGHT]} px)")
    print(f"  area: {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(f"label {i} topleft: ({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)