連結成分分析とラベリング - 画像内オブジェクトの個別識別と計数
連結成分分析とは - オブジェクトの個別識別の基礎
連結成分分析 (Connected Component Analysis / Labeling) は、二値画像中の連結した前景ピクセルの集合を個別のオブジェクトとして識別し、一意のラベル (整数値) を割り当てる処理です。物体の計数、面積計測、位置特定など、画像解析の基本操作として広く使用されます。
基本概念: 二値画像では前景ピクセル (白=255) と背景ピクセル (黒=0) が存在します。互いに隣接する前景ピクセルの集合が 1 つの「連結成分」を形成し、それぞれに固有のラベル番号 (1, 2, 3, ...) が付与されます。背景は通常ラベル 0 です。
連結性の定義:
- 4 連結 (4-connectivity): 上下左右の 4 方向のみを隣接とみなす。斜め方向は別オブジェクトとして扱われる。
- 8 連結 (8-connectivity): 上下左右に加え斜め 4 方向も隣接とみなす。より多くのピクセルが同一オブジェクトに統合される。
4 連結と 8 連結の選択: 8 連結は斜めに接触するオブジェクトを 1 つとして扱うため、文字認識 (斜めのストロークが繋がる) や細胞計数 (接触した細胞を 1 つとして扱いたくない場合は 4 連結) など、用途に応じて選択します。一般的には 8 連結がデフォルトで使用されます。
OpenCV では cv2.connectedComponents() および cv2.connectedComponentsWithStats() で実装されており、後者はラベルに加えて各成分の統計情報 (面積、重心、バウンディングボックス) も同時に取得できます。
ラベリングアルゴリズム - Two-Pass 法と Union-Find
連結成分ラベリングの代表的なアルゴリズムを解説します。効率的な実装は大規模画像のリアルタイム処理に不可欠であり、アルゴリズムの選択が処理速度に大きく影響します。
Two-Pass アルゴリズム: 最も古典的で広く実装されているアルゴリズムです。画像を 2 回走査します。
第 1 パス (ラベル割り当て): 左上から右下へラスタスキャンし、各前景ピクセルに仮ラベルを割り当てます。隣接ピクセルにラベルがあればそれを継承し、複数の異なるラベルが隣接する場合は等価テーブルに記録します。
第 2 パス (ラベル統合): 等価テーブルを解決し、同一オブジェクトに属する仮ラベルを最終ラベルに統一します。
Union-Find (Disjoint Set) による最適化: 等価テーブルの管理に Union-Find データ構造を使用すると、ラベルの統合操作が準 O(1) (逆アッカーマン関数) で実行できます。パス圧縮とランクによる統合を組み合わせることで、大量のラベル統合が発生する複雑な画像でも高速に処理できます。
計算量: Two-Pass 法の計算量は O(N) (N: ピクセル数) で、画像サイズに線形比例します。1,920 × 1,080の二値画像で約 5-8ms (CPU) です。メモリ使用量はラベルマップ (int32) で画像サイズの 4 倍が必要です。
並列アルゴリズム: GPU 向けの並列ラベリングアルゴリズム (Block-based Union-Find) も研究されており、4K 画像で 1ms 以下の処理が可能です。OpenCV の CUDA モジュールには cv2.cuda.connectedComponents() が実装されています。
統計情報の活用 - 面積、重心、バウンディングボックス
連結成分分析で得られる統計情報は、オブジェクトのフィルタリング、分類、追跡に活用されます。OpenCV の connectedComponentsWithStats() は各成分の統計を効率的に計算します。
取得できる統計情報:
- 面積 (Area): 成分に含まれるピクセル数。
stats[label, cv2.CC_STAT_AREA] - バウンディングボックス: 成分を囲む最小矩形の左上座標 (x, y) と幅・高さ (w, h)
- 重心 (Centroid): 成分の質量中心座標 (cx, cy)。
centroids[label]
面積フィルタリング: ノイズ (小さな連結成分) や背景の大きな塊を除去する最も基本的なフィルタリングです。
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary)
min_area, max_area = 100, 10000
for i in range(1, num_labels):
if stats[i, cv2.CC_STAT_AREA] < min_area or stats[i, cv2.CC_STAT_AREA] > max_area:
labels[labels == i] = 0
アスペクト比フィルタリング: バウンディングボックスの幅/高さ比で形状を判別します。文字は縦長 (アスペクト比 0.3-0.8)、ノイズは正方形に近い (0.8-1.2) 傾向があり、この特性で分離できます。
円形度 (Circularity): 4π × Area / Perimeter² で計算される形状指標で、完全な円が 1.0、細長い形状ほど 0 に近づきます。細胞計数で円形の細胞のみを選択する場合に有効です。輪郭の周囲長は cv2.findContours() + cv2.arcLength() で取得します。
実践応用 - 細胞計数、文字分割、欠陥検出
連結成分分析の代表的な実務応用を、完全なコード例とパラメータ設計の指針とともに紹介します。
細胞計数 (Cell Counting): 顕微鏡画像から細胞数を自動計測するパイプラインです。(1) グレースケール変換。(2) 適応的二値化 (blockSize=51, C=5)。(3) モルフォロジーオープニング (5x5) でノイズ除去。(4) 連結成分分析。(5) 面積フィルタ (min=200, max=5000 ピクセル) で細胞サイズの成分のみ抽出。(6) 円形度フィルタ (>0.6) で非細胞形状を除外。この手順で手動計数との相関係数 r=0.95 以上が達成可能です。
文字分割 (Character Segmentation): OCR の前処理として、テキスト行から個々の文字を分割します。(1) 二値化。(2) 連結成分分析。(3) バウンディングボックスを x 座標でソート。(4) 隣接するバウンディングボックス間の距離が閾値以下なら同一文字として統合 (「i」の点と本体など)。(5) 各文字領域を切り出して OCR エンジンに渡す。日本語の場合、文字サイズのばらつきが大きいため、面積の中央値の 0.3-3.0 倍を有効範囲とします。
PCB 欠陥検出: 基板画像の検査では、正常画像との差分を二値化し、連結成分分析で欠陥候補を抽出します。面積 50 ピクセル以上の成分を欠陥として報告し、バウンディングボックスで位置を特定します。誤検出率を下げるため、エッジ付近 (基板外周 5%) の成分は除外するルールを追加します。
粒度分析 (Particle Analysis): 粉体や粒子の画像から粒度分布を計測します。各連結成分の面積から等価円直径 d = 2√(Area/π) を計算し、ヒストグラムで粒度分布を可視化します。接触粒子の分離には Watershed 法との組み合わせが有効です。
接触オブジェクトの分離 - Watershed と距離変換の併用
連結成分分析の最大の課題は、接触または重なり合うオブジェクトが 1 つの成分として統合されてしまうことです。この問題を解決する距離変換と Watershed アルゴリズムの併用手法を解説します。
問題の本質: 2 つの円形オブジェクトが接触している場合、二値画像上では 1 つの連結成分になります。単純な連結成分分析では 1 個とカウントされ、正確な計数ができません。
距離変換 (Distance Transform): 各前景ピクセルから最も近い背景ピクセルまでの距離を計算します。オブジェクトの中心ほど距離値が大きくなり、接触点付近は距離値が小さくなります。
dist = cv2.distanceTransform(binary, cv2.DIST_L2, 5)
Watershed による分離手順:
- 距離変換画像の局所最大値 (ピーク) を検出し、各オブジェクトのシード (種) とする
- シードに対して連結成分分析を行い、マーカーを生成
- Watershed アルゴリズムでマーカーから領域を成長させ、境界を決定
ret, markers = cv2.connectedComponents(sure_fg)
markers = cv2.watershed(img_color, markers)
パラメータ調整: 距離変換のピーク検出閾値が重要です。最大距離値の 0.5-0.7 倍を閾値とするのが一般的です。閾値が低すぎると過分割 (1 つのオブジェクトが複数に分割)、高すぎると分離不足が発生します。
実測精度: 接触細胞画像 (100 個の細胞、うち 30% が接触) に対して、単純な連結成分分析では計数精度 72% ですが、距離変換 + Watershed の併用で 94% まで向上します。処理時間は 1,920 × 1,080画像で約 25ms です。
パフォーマンス最適化と大規模画像への対応
大規模画像 (4K 以上) や大量画像のバッチ処理における連結成分分析のパフォーマンス最適化手法を解説します。
メモリ効率: ラベルマップは int32 (4 bytes/pixel) で格納されるため、4K 画像 (3,840 × 2,160) で約 33MB のメモリを消費します。成分数が 255 以下であることが保証される場合は uint8 に変換してメモリを 1/4 に削減できます。大量画像のバッチ処理ではこの最適化が重要です。
ROI (Region of Interest) による高速化: 画像全体ではなく、対象物が存在する領域のみに連結成分分析を適用することで処理時間を削減します。まず粗い解像度 (1/4 縮小) で候補領域を検出し、元解像度の該当領域のみを処理する 2 段階アプローチが有効です。
インクリメンタルラベリング: 動画処理では、フレーム間の差分が小さいことを利用し、前フレームのラベル結果を初期値として差分のみを更新するインクリメンタル手法が有効です。処理時間を 60-80% 削減できます。
OpenCV の最適化オプション:
cv2.CCL_DEFAULT: 自動選択 (通常は Grana のアルゴリズム)cv2.CCL_WU: Wu のアルゴリズム (小さな画像に最適)cv2.CCL_GRANA: Grana のアルゴリズム (大きな画像に最適)cv2.CCL_BOLELLI: Bolelli のアルゴリズム (最新、多くの場合最速)
GPU アクセラレーション: CUDA 対応の連結成分分析は 4K 画像で約 0.5ms と、CPU の 10-15 倍高速です。ただし、GPU-CPU 間のデータ転送オーバーヘッドがあるため、単一画像では恩恵が小さく、バッチ処理や動画処理で効果を発揮します。