STEP 15:パノプティックセグメンテーション

🌍 STEP 15: パノプティックセグメンテーション

セマンティック+インスタンスの統合、Things と Stuff、
Panoptic FPN、Panoptic Quality(PQ)を学びます

📋 このステップで学ぶこと

  • パノプティックセグメンテーションとは(3つのタスクの統合)
  • Things(可算物体)と Stuff(不可算領域)の概念
  • Panoptic FPN(2つのブランチの統合)
  • 統合(Merge)アルゴリズムの詳細
  • UPSNet(統一されたセグメンテーションヘッド)
  • 評価指標:Panoptic Quality(PQ = SQ × RQ)
  • 応用:自動運転でのシーン理解
  • 実装:Detectron2でのパノプティックセグメンテーション

🌍 1. パノプティックセグメンテーションとは

パノプティックセグメンテーションは、STEP 13で学んだセマンティックセグメンテーションとSTEP 14で学んだインスタンスセグメンテーションを統合したタスクです。「Panoptic」は「Pan(すべて)+ Optic(視覚)」から来ており、画像内のすべてを理解することを目指します。

1-1. なぜパノプティックが必要か

【従来のタスクの限界】 ■ セマンティックセグメンテーション(STEP 13) できること: 各ピクセルをクラス分類 例: 2台の車と道路 ┌─────────────────────────┐ │ 空 空 空 空 │ │ 車車 車車車車 │ ← 2台の車を区別できない │ 道路 道路 道路 道路 │ すべて「車」クラス └─────────────────────────┘ 限界: ・「車が何台あるか」分からない ・個別の車を追跡できない ・重なった車を分離できない ───────────────────────────────────────────────────── ■ インスタンスセグメンテーション(STEP 14) できること: 物体を個別識別、マスク生成 例: 2台の車と道路 ┌─────────────────────────┐ │ (無) (無) (無) (無) │ ← 背景を無視 │ 車① 車②②② │ ← 2台を区別 │ (無) (無) (無) (無) │ ← 道路も無視 └─────────────────────────┘ 限界: ・背景(道路、空)が分からない ・「走行可能な領域はどこか」不明 ・シーン全体を理解できない ───────────────────────────────────────────────────── ■ パノプティックセグメンテーション(このSTEP) できること: すべてのピクセルをラベル付け + 物体は個別識別 例: 2台の車と道路 ┌─────────────────────────┐ │ 空,0 空,0 空,0 空,0 │ ← Stuff(ID不要) │ 車,1 車,2 車,2 車,2 │ ← Things(個別ID) │道路,0 道路,0 道路,0 │ ← Stuff(ID不要) └─────────────────────────┘ 出力形式: (クラス, インスタンスID) 利点: ・すべてのピクセルを網羅 ・物体は個別識別(車1、車2) ・背景も認識(道路、空) ・完全なシーン理解

1-2. 3つのタスクの比較

項目 セマンティック インスタンス パノプティック
目標 クラス分類 物体検出+マスク 両方を統合
出力 H×W(クラスID) N個のマスク H×W(クラス,ID)
背景 ✅ ラベル付け ❌ 無視 ✅ ラベル付け
物体識別 ❌ 区別なし ✅ 個別ID ✅ 個別ID
全ピクセル ✅ カバー ❌ 物体のみ ✅ カバー

1-3. Things と Stuff の概念

💡 パノプティックの核心:Things と Stuff

画像内の要素を2つのカテゴリに分類することで、効率的に処理します。

【Things(シングス)= 可算物体】 定義: 個別に数えられる、明確な境界を持つ物体 判定基準: ・「何個ある?」と聞いて意味がある ・境界線を引ける ・個体として追跡できる 例: ✓ 人(person) → 1人、2人とカウント ✓ 車(car) → 1台、2台とカウント ✓ 自転車 → 1台、2台とカウント ✓ 椅子、テーブル → 個別の家具 ✓ 猫、犬 → 1匹、2匹とカウント ✓ 信号機 → 個数で数えられる 処理方法: → インスタンスセグメンテーションで処理 → 個別ID付与(人1、人2、車1、車2) → マスク + Bounding Box ───────────────────────────────────────────────────── 【Stuff(スタッフ)= 不可算領域】 定義: 個別に数えられない、境界が曖昧な領域 判定基準: ・「何個ある?」と聞いても意味がない ・明確な境界がない ・連続した領域として扱う 例: ✓ 空(sky) → 何個とは数えない ✓ 道路(road) → 連続した領域 ✓ 草、芝生 → 広がった領域 ✓ 壁(wall) → 背景として認識 ✓ 水(water) → 境界が曖昧 ✓ 建物の壁面 → 背景要素 処理方法: → セマンティックセグメンテーションで処理 → クラスのみ(ID不要) → インスタンスID = 0(固定) ───────────────────────────────────────────────────── 【なぜ分けるのか?】 効率性: ・Stuffに個別IDは不要 → 処理が軽い ・Thingsのみ個別追跡 → 必要な部分だけ詳細処理 現実世界の反映: ・「空が3つある」は意味がない ・「車が3台ある」は意味がある タスクの明確化: ・Things → 個数カウント、追跡が重要 ・Stuff → 領域の広さ、位置が重要

1-4. Things と Stuff の分類例

Things(可算物体) Stuff(不可算領域)
人(person) 空(sky)
車(car)、トラック、バス 道路(road)
自転車、オートバイ 歩道(sidewalk)
椅子、テーブル、ソファ 壁(wall)、床(floor)
猫、犬、鳥 草(grass)、植物(vegetation)
テレビ、ノートPC 水(water)、砂(sand)
りんご、バナナ 建物の壁面

1-5. パノプティックの出力形式

【パノプティックセグメンテーションの出力】 ■ 出力形式 各ピクセルに (クラスID, インスタンスID) を割り当て 実装では1つの整数で表現: panoptic_id = クラスID × 1000 + インスタンスID 例: ・(クラス=人=1, ID=3) → 1003 ・(クラス=車=2, ID=1) → 2001 ・(クラス=道路=10, ID=0) → 10000 ← Stuffは ID=0 ■ 具体例 画像サイズ: 1024×2048 出力マップ(同じサイズ): ┌────────────────────────────────────┐ │ 7000 7000 7000 7000 … │ ← 空(Stuff、クラス7) │ 7000 7000 7000 7000 … │ │ 1001 1001 2001 2001 2001 … │ ← 人1、車1(Things) │ 1001 1002 2001 2001 2001 … │ ← 人1、人2、車1 │ 10000 10000 10000 10000 … │ ← 道路(Stuff、クラス10) └────────────────────────────────────┘ ■ segments_info(セグメント情報) 各セグメントのメタデータ: [ {‘id’: 1001, ‘category_id’: 1, ‘isthing’: True, ‘area’: 12543}, ← 人1 {‘id’: 1002, ‘category_id’: 1, ‘isthing’: True, ‘area’: 8932}, ← 人2 {‘id’: 2001, ‘category_id’: 2, ‘isthing’: True, ‘area’: 45678}, ← 車1 {‘id’: 7000, ‘category_id’: 7, ‘isthing’: False, ‘area’: 389012}, ← 空 {‘id’: 10000, ‘category_id’: 10, ‘isthing’: False, ‘area’: 856234} ← 道路 ] isthing: True = Things、False = Stuff

🏗️ 2. Panoptic FPN

Panoptic FPN(2019年、Facebook AI Research)は、Mask R-CNNをベースにしたパノプティックセグメンテーションモデルです。2つの独立したブランチでThingsとStuffを処理し、最後に統合します。

2-1. アーキテクチャの概要

【Panoptic FPNの全体構造】 入力画像(例: 1024×2048×3) │ ↓ ┌─────────────────────────────────────────────────────┐ │ バックボーン(ResNet-50/101) │ │ │ │ 畳み込み層で特徴抽出 │ │ C2, C3, C4, C5 の特徴マップを出力 │ └─────────────────────────────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ FPN(Feature Pyramid Network) │ │ │ │ マルチスケールの特徴マップを生成 │ │ P2(1/4), P3(1/8), P4(1/16), P5(1/32), P6(1/64) │ │ すべて256チャンネルに統一 │ └─────────────────────────────────────────────────────┘ │ ┌────┴────┐ │ │ ↓ ↓ ┌─────────┐ ┌─────────────────────────────────────────┐ │Instance │ │ Semantic Branch(Stuff用) │ │ Branch │ │ │ │(Things用)│ │ FPN特徴を使用 │ │ │ │ ↓ │ │Mask R-CNN│ │ 各レベルを同じ解像度にアップサンプル │ │の構造 │ │ ↓ │ │ │ │ 結合(Concatenate) │ │ RPN │ │ ↓ │ │ ↓ │ │ 3×3 Conv × 複数 │ │RoIAlign │ │ ↓ │ │ ↓ │ │ 1×1 Conv(Stuffクラス数) │ │分類+Box │ │ ↓ │ │+マスク │ │ セマンティックマスク(H×W×Stuff数) │ └─────────┘ └─────────────────────────────────────────┘ │ │ │ │ ↓ ↓ Things Stuff 検出結果 セマンティック マスク │ │ └────┬────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ Merge(統合) │ │ │ │ ThingsとStuffを1つのマップに統合 │ │ 重複の解決 │ │ 小さいセグメントの除去 │ └─────────────────────────────────────────────────────┘ │ ↓ パノプティック セグメンテーション 結果

2-2. Instance Branch(Things用)の詳細

【Instance Branch = Mask R-CNN】 STEP 14で学んだMask R-CNNとほぼ同じ構造: FPN特徴マップ {P2, P3, P4, P5, P6} │ ↓ ┌─────────────────────────────────────────────────────┐ │ RPN(Region Proposal Network) │ │ │ │ 各FPNレベルでAnchor Boxを生成 │ │ 物体らしさを予測 │ │ 約2000個の候補領域を提案 │ └─────────────────────────────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ RoIAlign │ │ │ │ 各候補領域から14×14×256の特徴を抽出 │ │ 量子化なしで正確な位置を保持 │ └─────────────────────────────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ 3つのヘッド(並列処理) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │分類ヘッド│ │Box回帰 │ │マスクヘッド│ │ │ │ │ │ │ │ │ │ │ │FC層 │ │FC層 │ │Conv×4 │ │ │ │ ↓ │ │ ↓ │ │ ↓ │ │ │ │クラス確率│ │調整量 │ │28×28マスク│ │ │ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────┘ │ ↓ Things検出結果: – N個の物体 – 各物体: クラス、信頼度、Bbox、マスク 注意: ・Thingsクラスのみを検出対象 ・COCOでは80クラス中、Thingsは約50クラス

2-3. Semantic Branch(Stuff用)の詳細

【Semantic Branch = 軽量FCN】 FPN特徴マップ {P2, P3, P4, P5} │ ↓ ┌─────────────────────────────────────────────────────┐ │ 各レベルを同じ解像度に揃える │ │ │ │ P2 (H/4×W/4×256) → そのまま │ │ P3 (H/8×W/8×256) → 2倍アップサンプル → H/4 │ │ P4 (H/16×W/16×256) → 4倍アップサンプル → H/4 │ │ P5 (H/32×W/32×256) → 8倍アップサンプル → H/4 │ │ │ │ すべて H/4 × W/4 × 256 に統一 │ └─────────────────────────────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ 結合(Concatenate) │ │ │ │ 4つの特徴マップを結合 │ │ → H/4 × W/4 × 1024 │ └─────────────────────────────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ 畳み込み層 │ │ │ │ 3×3 Conv (256) × 複数 │ │ 特徴を処理 │ └─────────────────────────────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ 1×1 Conv(Stuffクラス数 + Thingsクラス数) │ │ │ │ 出力: H/4 × W/4 × 全クラス数 │ │ ※ 後でThingsは除外される │ └─────────────────────────────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────────┐ │ 4倍アップサンプル │ │ │ │ H/4 × W/4 → H × W │ │ 元の解像度に戻す │ └─────────────────────────────────────────────────────┘ │ ↓ セマンティックマスク: H × W × 全クラス数 各ピクセルでクラス確率

2-4. 統合(Merge)アルゴリズム

💡 統合の核心

ThingsとStuffの出力を1つのパノプティックマップに統合します。
重複がある場合は「信頼度の高い方を優先」するルールで解決します。

【統合アルゴリズムの詳細】 入力: ・things_detections: N個の物体検出結果 各物体: (クラス, 信頼度, Bbox, マスク) ・semantic_mask: H×W×全クラス数のセマンティックマスク 出力: ・panoptic_map: H×W のパノプティックマップ 各ピクセル: (クラスID, インスタンスID) ・segments_info: セグメント情報のリスト ───────────────────────────────────────────────────── 【ステップ1: 初期化】 panoptic_map = 空のマップ(全ピクセル未割り当て) instance_counter = {} # クラスごとのインスタンスカウンタ segments_info = [] ───────────────────────────────────────────────────── 【ステップ2: Thingsの配置(信頼度の高い順)】 # 信頼度でソート(降順) sorted_things = sort(things_detections, key=confidence, reverse=True) for thing in sorted_things: # 信頼度が閾値以上か確認 if thing.confidence < threshold: # 例: 0.5 continue # マスクのピクセルを取得 mask_pixels = thing.mask > 0.5 # 二値化 # 既に割り当て済みのピクセルを除外 available_pixels = mask_pixels AND (panoptic_map == 未割り当て) # 有効なピクセル数が閾値以上か確認 if count(available_pixels) < min_area: # 例: 4096 continue # インスタンスIDを割り当て class_id = thing.class_id if class_id not in instance_counter: instance_counter[class_id] = 0 instance_counter[class_id] += 1 instance_id = instance_counter[class_id] # パノプティックマップに配置 panoptic_map[available_pixels] = (class_id, instance_id) # セグメント情報を記録 segments_info.append({ 'id': class_id * 1000 + instance_id, 'category_id': class_id, 'isthing': True, 'area': count(available_pixels) }) ───────────────────────────────────────────────────── 【ステップ3: Stuffの配置】 # 未割り当てピクセルを取得 unassigned_pixels = (panoptic_map == 未割り当て) # セマンティックマスクから予測を取得 for pixel in unassigned_pixels: # 最も確率の高いクラスを取得 class_probs = semantic_mask[pixel] class_id = argmax(class_probs) # Thingsクラスの場合はスキップ(またはvoidに) if is_thing_class(class_id): # オプション: 未分類としてマーク continue # Stuffとして割り当て(インスタンスID = 0) panoptic_map[pixel] = (class_id, 0) # Stuff領域をセグメント化 for stuff_class in stuff_classes: stuff_pixels = (panoptic_map[:, 0] == stuff_class) if count(stuff_pixels) >= min_area: segments_info.append({ ‘id’: stuff_class * 1000, ‘category_id’: stuff_class, ‘isthing’: False, ‘area’: count(stuff_pixels) }) ───────────────────────────────────────────────────── 【ステップ4: 後処理】 # 小さいセグメントを除去 for segment in segments_info: if segment[‘area’] < min_area: # void(未分類)に変更 pixels = (panoptic_map == segment['id']) panoptic_map[pixels] = (void_class, 0) segments_info.remove(segment)
【統合の具体例】 ■ シーン: 交差点(2人の歩行者、1台の車、道路、空) Instance Branchの出力: 物体1: 人、信頼度0.95、マスクM1 物体2: 人、信頼度0.82、マスクM2 物体3: 車、信頼度0.88、マスクM3 Semantic Branchの出力: 空: 上部30% 道路: 下部40% 建物: 左右の領域 ───────────────────────────────────────────────────── 【処理の流れ】 初期状態: ┌─────────────────────────┐ │ ? ? ? ? ? ? │ ? = 未割り当て │ ? ? ? ? ? ? │ │ ? ? ? ? ? ? │ │ ? ? ? ? ? ? │ └─────────────────────────┘ Step 2a: 人1(信頼度0.95)を配置 ┌─────────────────────────┐ │ ? ? ? ? ? ? │ │ ? 人1 ? ? ? ? │ │ ? 人1 ? ? ? ? │ │ ? ? ? ? ? ? │ └─────────────────────────┘ Step 2b: 車(信頼度0.88)を配置 ┌─────────────────────────┐ │ ? ? ? ? ? ? │ │ ? 人1 ? 車1 車1 ? │ │ ? 人1 ? 車1 車1 ? │ │ ? ? ? ? ? ? │ └─────────────────────────┘ Step 2c: 人2(信頼度0.82)を配置 ※ 人1と一部重複 → 重複部分は除外 ┌─────────────────────────┐ │ ? ? ? ? ? ? │ │ ? 人1 人2 車1 車1 ? │ │ ? 人1 人2 車1 車1 ? │ │ ? ? ? ? ? ? │ └─────────────────────────┘ Step 3: Stuffを配置 ┌─────────────────────────┐ │ 空 空 空 空 空 空 │ │建物 人1 人2 車1 車1 建物│ │道路 人1 人2 車1 車1 道路│ │道路 道路 道路 道路 道路 道路│ └─────────────────────────┘ ───────────────────────────────────────────────────── 【最終出力】 panoptic_map: 各ピクセルに (クラス, インスタンスID) segments_info: – 人1: {id: 1001, category_id: 1, isthing: True, area: …} – 人2: {id: 1002, category_id: 1, isthing: True, area: …} – 車1: {id: 2001, category_id: 2, isthing: True, area: …} – 空: {id: 7000, category_id: 7, isthing: False, area: …} – 道路: {id: 10000, category_id: 10, isthing: False, area: …} – 建物: {id: 11000, category_id: 11, isthing: False, area: …}

🔄 3. UPSNet(統合セグメンテーションヘッド)

UPSNet(Unified Panoptic Segmentation Network, 2019年)は、Panoptic FPNの2つのブランチを統一的なヘッドで処理するモデルです。End-to-endで学習でき、より自然な統合が可能です。

3-1. Panoptic FPN vs UPSNet

【2つのアプローチの比較】 ■ Panoptic FPN 構造: バックボーン → 2つの独立ブランチ → 後処理で統合 ┌──────────────┐ │ バックボーン │ └──────────────┘ │ ┌────┴────┐ │ │ ↓ ↓ ┌──────┐ ┌──────┐ │Instance│ │Semantic│ ← 独立した2つのブランチ │ Branch │ │ Branch │ └──────┘ └──────┘ │ │ └────┬────┘ │ 後処理で統合 ← ネットワーク外で統合 特徴: ・シンプルな設計 ・既存のMask R-CNNを流用 ・統合は後処理(ヒューリスティック) ───────────────────────────────────────────────────── ■ UPSNet 構造: バックボーン → 統一ヘッド → ネットワーク内で統合 ┌──────────────┐ │ バックボーン │ └──────────────┘ │ ↓ ┌──────────────┐ │ Panoptic Head │ ← 統一されたヘッド │ │ │ Deformable Conv│ │ ↓ │ │ セマンティック │ │ ロジット │ │ ↓ │ │ インスタンス │ │ ロジット │ │ ↓ │ │ Panoptic │ │ Logits Fusion │ ← ネットワーク内で統合 └──────────────┘ │ ↓ パノプティック 出力 特徴: ・End-to-endで学習 ・統合もネットワークの一部 ・より自然な融合

3-2. UPSNetの特徴

項目 Panoptic FPN UPSNet
アーキテクチャ 2つの独立ブランチ 統一されたヘッド
統合方法 後処理(ヒューリスティック) ネットワーク内(学習可能)
学習 ブランチ別に訓練 End-to-end
畳み込み 通常の畳み込み Deformable Convolution
精度(Cityscapes) PQ 58.1% PQ 59.3%
複雑さ 比較的シンプル やや複雑

📊 4. 評価指標:Panoptic Quality(PQ)

Panoptic Quality(PQ)は、パノプティックセグメンテーション専用の評価指標です。分割の品質(SQ)と認識の品質(RQ)を掛け合わせることで、総合的な性能を評価します。

4-1. PQの定義

💡 PQの計算式

PQ = SQ × RQ

SQ(Segmentation Quality):マッチしたセグメントの平均IoU
RQ(Recognition Quality):認識のF1スコア相当

【PQの詳細定義】 ■ まず、マッチングを行う 予測セグメントと正解セグメントをマッチング マッチング条件: IoU > 0.5 結果: TP(True Positive): IoU > 0.5 でマッチした予測 FP(False Positive): マッチしなかった予測(誤検出) FN(False Negative): マッチしなかった正解(見逃し) ───────────────────────────────────────────────────── ■ SQ(Segmentation Quality) マッチしたセグメントの分割品質 SQ = (Σ IoU(p, g)) / |TP| 意味: マッチしたペアの平均IoU 分割の正確さを測定 範囲: 0〜1(高いほど良い) 特徴: ・FP、FNは含まない ・純粋に「分割品質」を測定 ───────────────────────────────────────────────────── ■ RQ(Recognition Quality) 認識の品質(検出率) RQ = |TP| / (|TP| + 0.5×|FP| + 0.5×|FN|) 意味: F1スコアと同等 検出の正確さを測定 範囲: 0〜1(高いほど良い) 導出: Precision = TP / (TP + FP) Recall = TP / (TP + FN) F1 = 2 × Precision × Recall / (Precision + Recall) = TP / (TP + 0.5×FP + 0.5×FN) = RQ ───────────────────────────────────────────────────── ■ PQ(Panoptic Quality) 総合的な品質 PQ = SQ × RQ 展開すると: PQ = (Σ IoU(p, g)) / (|TP| + 0.5×|FP| + 0.5×|FN|) 範囲: 0〜1(0%〜100%) 特徴: ・分割品質と認識品質の両方を考慮 ・1つの数値で総合評価

4-2. PQの計算例

【PQ計算の具体例】 ■ シーン: 4台の車の検出 正解(Ground Truth): 車GT1、車GT2、車GT3、車GT4 予測: 車P1、車P2、車P3、車P4、車P5 ───────────────────────────────────────────────────── 【ステップ1: マッチング(IoU > 0.5)】 車P1 ↔ 車GT1: IoU = 0.78 → ✓ TP 車P2 ↔ 車GT2: IoU = 0.65 → ✓ TP 車P3 ↔ 車GT3: IoU = 0.72 → ✓ TP 車P4 ↔ なし: IoU < 0.5 → × FP(誤検出) 車P5 ↔ なし: IoU < 0.5 → × FP(誤検出) 車GT4 ↔ なし: 見逃し → × FN(見逃し) 結果: TP = 3(P1-GT1、P2-GT2、P3-GT3) FP = 2(P4、P5) FN = 1(GT4) ───────────────────────────────────────────────────── 【ステップ2: SQの計算】 SQ = (IoU_P1 + IoU_P2 + IoU_P3) / TP = (0.78 + 0.65 + 0.72) / 3 = 2.15 / 3 = 0.717 = 71.7% 解釈: マッチした3台の車の平均IoUは71.7% 分割品質は良好 ───────────────────────────────────────────────────── 【ステップ3: RQの計算】 RQ = TP / (TP + 0.5×FP + 0.5×FN) = 3 / (3 + 0.5×2 + 0.5×1) = 3 / (3 + 1 + 0.5) = 3 / 4.5 = 0.667 = 66.7% 解釈: 認識品質は66.7% 2台誤検出、1台見逃し ───────────────────────────────────────────────────── 【ステップ4: PQの計算】 PQ = SQ × RQ = 0.717 × 0.667 = 0.478 = 47.8% 解釈: 総合的な品質は47.8% 内訳: ・分割品質(SQ)71.7%は良好 ・認識品質(RQ)66.7%が課題 ・誤検出と見逃しを減らす必要 ───────────────────────────────────────────────────── 【改善の方向性】 SQを上げる(分割品質向上): ・境界の精度を上げる ・RoIAlignの活用 ・より細かいマスク予測 RQを上げる(認識品質向上): ・誤検出を減らす → 信頼度閾値を上げる ・見逃しを減らす → 信頼度閾値を下げる ・データ拡張で学習強化

4-3. PQ^Things と PQ^Stuff

【PQの分解評価】 PQはThingsとStuffで別々に計算できます: ■ PQ^Things(可算物体のみ) Thingsクラスのみでマッチングと計算 例(Cityscapes val): PQ^Things = 52.0% 特徴: ・個別識別が必要 → 難しい ・インスタンスセグメンテーションの性能を反映 ・一般的に PQ^Things < PQ^Stuff ■ PQ^Stuff(不可算領域のみ) Stuffクラスのみでマッチングと計算 例(Cityscapes val): PQ^Stuff = 62.5% 特徴: ・領域分割のみ → 比較的簡単 ・セマンティックセグメンテーションの性能を反映 ・一般的に PQ^Stuff > PQ^Things ■ 全体PQ ThingsとStuffを合わせた評価 例(Cityscapes val): PQ = 58.1% 計算方法: 全クラスのPQを平均(または加重平均) ───────────────────────────────────────────────────── 【なぜ PQ^Things < PQ^Stuff か】 1. 個別識別の難しさ Things: 同じクラスでも個別にマッチング必要 Stuff: クラスが合えばOK 2. 境界の複雑さ Things: 複雑な形状(人、車) Stuff: 比較的単純な領域(空、道路) 3. 重なりの問題 Things: 物体同士が重なることが多い Stuff: 重なりが少ない

4-4. 評価指標のまとめ

指標 計算式 意味 典型的な値
PQ SQ × RQ 総合的な品質 COCO: 40-45%
Cityscapes: 55-62%
SQ ΣIoU / |TP| 分割品質(平均IoU) 70-85%
RQ TP / (TP + 0.5FP + 0.5FN) 認識品質(F1相当) 55-75%
PQ^Things Thingsのみで計算 可算物体の品質 PQより低め
PQ^Stuff Stuffのみで計算 不可算領域の品質 PQより高め

🚗 5. 応用:自動運転でのシーン理解

5-1. なぜ自動運転にパノプティックが必要か

【自動運転に必要な情報】 ■ Things(可算物体)の情報 必要な認識対象: ・歩行者: 何人、どこに、どう動いているか ・車両: 何台、どこに、速度と方向は ・自転車: 何台、どこに、予測動作は ・信号機: 状態(赤/黄/青) なぜ個別識別が必要か: ・追跡: 「車1が右車線から接近」 ・予測: 「歩行者2が横断しそう」 ・計画: 「車3の後ろについて走行」 ■ Stuff(不可算領域)の情報 必要な認識対象: ・道路: 走行可能な領域 ・歩道: 歩行者がいる可能性のある領域 ・車線: 自車がどの車線にいるか ・縁石: 道路の端 なぜ領域認識が必要か: ・経路計画: 「この道路を直進」 ・安全確認: 「歩道に歩行者なし」 ・緊急対応: 「路肩に停車可能」 ───────────────────────────────────────────────────── 【セマンティック/インスタンス単独の限界】 セマンティックのみ: 「前方に車がある」はわかる 「何台の車がどう動いているか」は不明 → 衝突予測ができない インスタンスのみ: 「車が3台、人が5人」はわかる 「どこが道路で走行可能か」は不明 → 経路計画ができない パノプティック: 「車1が道路上を右から接近中」 「歩行者2が歩道から道路に出そう」 「前方の道路は空いている」 → 完全なシーン理解 → 安全な運転判断

5-2. Cityscapesデータセット

【Cityscapes: 自動運転研究の標準データセット】 ■ 概要 撮影場所: ドイツの50都市 解像度: 2048×1024(高解像度) データ数: 訓練2,975枚、検証500枚、テスト1,525枚 アノテーション: ピクセル単位、高品質手動ラベリング ■ クラス構成(19クラス) Things(8クラス): person – 歩行者 rider – 自転車/バイク乗り car – 乗用車 truck – トラック bus – バス train – 電車 motorcycle – オートバイ bicycle – 自転車 Stuff(11クラス): road – 道路 sidewalk – 歩道 building – 建物 wall – 壁 fence – フェンス pole – ポール(信号機支柱等) traffic light – 信号機本体 traffic sign – 標識 vegetation – 植物 terrain – 地形(未舗装地等) sky – 空 ■ パノプティック評価の特徴 Things: 8クラスを個別識別 Stuff: 11クラスを領域分類 典型的なスコア: PQ: 55-65% PQ^Things: 45-55% PQ^Stuff: 60-70%

5-3. 自動運転での活用シナリオ

【シナリオ: 交差点での右折】 ■ ステップ1: パノプティックセグメンテーション 入力: 車載カメラ画像(1920×1080) 出力: ┌────────────────────────────────────────┐ │ 空 空 空 空 空 建物 建物 │ │ 空 空 空 空 空 建物 建物 │ │建物 (人1)(車1)(車1)(車2) 建物 建物 │ Things: 個別ID │歩道 (人2)(車1)(車1)(車2) 道路 道路 │ │道路 道路 道路 道路 道路 道路 道路 │ Stuff: クラスのみ └────────────────────────────────────────┘ 検出結果: Things: ・人1: 歩道上、信頼度0.95 ・人2: 歩道から道路に近づいている、信頼度0.88 ・車1: 道路上、右側を走行中、信頼度0.92 ・車2: 道路上、対向車線、信頼度0.90 Stuff: ・道路: 中央下部の領域 ・歩道: 道路の両脇 ・建物: 背景 ・空: 上部 ■ ステップ2: シーン理解 空間的関係の把握: ・人1は歩道にいる → 安全 ・人2は道路に近い → 要注意 ・車1は自車の右前方 → 右折時に注意 ・車2は対向車線 → 右折待ち 動的状態の推定: ・人2が道路方向に移動 → 横断の可能性 ・車1が減速中 → 追突のリスク低 ・車2が接近中 → 右折タイミングを計算 ■ ステップ3: 行動計画 判断: 1. 人2の動きを監視(横断の可能性) 2. 車2が通過するのを待機 3. 車1との車間距離を維持 4. 人2が横断しないことを確認 5. 右折開始 実行: ・停止 → 車2通過待ち → 人2確認 → 右折開始 ■ パノプティックの貢献 セマンティックだけでは: 「前方に車と人がいる」→ どの車?どの人? インスタンスだけでは: 「車2台、人2人」→ どこが道路?走行可能? パノプティックでは: 「車1が道路上の右前方にいて、 人2が歩道から道路に向かっている」 → 正確な状況判断が可能

💻 6. 実装:パノプティックセグメンテーション

Detectron2(Facebook AI Research)を使って、パノプティックセグメンテーションを実装します。Detectron2はPyTorchベースの高性能なCV研究ライブラリです。

6-1. Detectron2のインストールとセットアップ

※ コードが横に長い場合は横スクロールできます

# =================================================== # Detectron2のインストール(Google Colabの場合) # =================================================== # PyTorchのバージョン確認 import torch print(f”PyTorch version: {torch.__version__}”) print(f”CUDA available: {torch.cuda.is_available()}”) # Detectron2のインストール # !pip install ‘git+https://github.com/facebookresearch/detectron2.git’ # または、ビルド済みのホイールをインストール(高速) # !pip install detectron2 -f https://dl.fbaipublicfiles.com/detectron2/wheels/cu118/torch2.0/index.html

6-2. モデルの設定とロード

# =================================================== # 必要なライブラリのインポート # =================================================== import torch import detectron2 from detectron2 import model_zoo from detectron2.engine import DefaultPredictor from detectron2.config import get_cfg from detectron2.utils.visualizer import Visualizer, ColorMode from detectron2.data import MetadataCatalog import cv2 import numpy as np from PIL import Image import matplotlib.pyplot as plt print(f”Detectron2 version: {detectron2.__version__}”) # =================================================== # Panoptic FPNモデルの設定 # =================================================== def setup_panoptic_model(): “”” Panoptic FPNモデルの設定を作成 Returns: cfg: Detectron2の設定オブジェクト “”” # 設定オブジェクトを取得 cfg = get_cfg() # Panoptic FPNの設定ファイルをロード # ResNet-101バックボーン、3x学習スケジュール cfg.merge_from_file(model_zoo.get_config_file( “COCO-PanopticSegmentation/panoptic_fpn_R_101_3x.yaml” )) # 事前学習済みモデルの重みをロード(COCOで学習済み) cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url( “COCO-PanopticSegmentation/panoptic_fpn_R_101_3x.yaml” ) # 推論時の閾値設定 # ROI_HEADS: Things検出の信頼度閾値 cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.7 # PANOPTIC_FPN.COMBINE: 統合時の閾値 cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = 0.5 # デバイス設定(GPU/CPU) cfg.MODEL.DEVICE = ‘cuda’ if torch.cuda.is_available() else ‘cpu’ return cfg # モデルをセットアップ cfg = setup_panoptic_model() # 予測器を作成 # DefaultPredictorは画像を入力として受け取り、推論結果を返す predictor = DefaultPredictor(cfg) print(f”デバイス: {cfg.MODEL.DEVICE}”) print(“Panoptic FPNモデルをロードしました”)

実行結果:

Detectron2 version: 0.6 デバイス: cuda Panoptic FPNモデルをロードしました

6-3. 画像の読み込みと推論

# =================================================== # 画像の読み込みと推論 # =================================================== # 画像を読み込み(OpenCVはBGR形式) image_path = ‘street_scene.jpg’ image = cv2.imread(image_path) # 表示用にRGB形式に変換 image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) print(f”画像サイズ: {image.shape}”) # (H, W, C) # =================================================== # パノプティックセグメンテーションの推論 # =================================================== # 推論実行 # Detectron2は内部で前処理(リサイズ、正規化)を行う outputs = predictor(image) # 結果の取得 # panoptic_seg: パノプティックマップ(H×W のテンソル) # segments_info: 各セグメントの情報(リスト) panoptic_seg, segments_info = outputs[“panoptic_seg”] print(f”パノプティックマップの形状: {panoptic_seg.shape}”) print(f”検出されたセグメント数: {len(segments_info)}”)

実行結果:

画像サイズ: (1024, 2048, 3) パノプティックマップの形状: torch.Size([1024, 2048]) 検出されたセグメント数: 18

6-4. 結果の解析

# =================================================== # 結果の解析 # =================================================== # COCOデータセットのメタデータを取得 metadata = MetadataCatalog.get(cfg.DATASETS.TRAIN[0]) # Things/Stuffのクラス名を取得 thing_classes = metadata.thing_classes stuff_classes = metadata.stuff_classes print(f”Thingsクラス数: {len(thing_classes)}”) print(f”Stuffクラス数: {len(stuff_classes)}”) # =================================================== # セグメント情報の表示 # =================================================== print(“\n” + “=” * 60) print(“検出されたセグメント:”) print(“=” * 60) things_count = 0 stuff_count = 0 things_list = [] stuff_list = [] for segment in segments_info: # セグメント情報を取得 segment_id = segment[‘id’] # ユニークID category_id = segment[‘category_id’] # カテゴリID is_thing = segment[‘isthing’] # Things か Stuff か area = segment[‘area’] # ピクセル数 # カテゴリ名を取得 if is_thing: # Thingsの場合 category_name = thing_classes[category_id] things_count += 1 things_list.append({ ‘name’: category_name, ‘id’: segment_id, ‘area’: area }) else: # Stuffの場合 # Stuffのカテゴリは連続した番号なので調整が必要 stuff_idx = category_id – len(thing_classes) if stuff_idx >= 0 and stuff_idx < len(stuff_classes): category_name = stuff_classes[stuff_idx] else: category_name = f"stuff_{category_id}" stuff_count += 1 stuff_list.append({ 'name': category_name, 'id': segment_id, 'area': area }) # Things の表示 print(f"\n【Things(可算物体)】 {things_count}個") print("-" * 40) for i, thing in enumerate(things_list, 1): print(f" {i}. {thing['name']}") print(f" ID: {thing['id']}, 面積: {thing['area']:,}px") # Stuff の表示 print(f"\n【Stuff(不可算領域)】 {stuff_count}領域") print("-" * 40) for i, stuff in enumerate(stuff_list, 1): print(f" {i}. {stuff['name']}") print(f" ID: {stuff['id']}, 面積: {stuff['area']:,}px")

実行結果:

Thingsクラス数: 80 Stuffクラス数: 53 ============================================================ 検出されたセグメント: ============================================================ 【Things(可算物体)】 5個 —————————————- 1. person ID: 1001, 面積: 12,543px 2. person ID: 1002, 面積: 8,932px 3. car ID: 2001, 面積: 45,678px 4. car ID: 2002, 面積: 38,901px 5. bicycle ID: 3001, 面積: 15,234px 【Stuff(不可算領域)】 5領域 —————————————- 1. road ID: 100000, 面積: 856,234px 2. sidewalk ID: 101000, 面積: 234,567px 3. building ID: 102000, 面積: 456,789px 4. sky ID: 103000, 面積: 389,012px 5. vegetation ID: 104000, 面積: 123,456px

6-5. 可視化

# =================================================== # パノプティックセグメンテーションの可視化 # =================================================== def visualize_panoptic(image, panoptic_seg, segments_info, metadata): “”” パノプティックセグメンテーションの結果を可視化 Args: image: 元画像(RGB形式のNumPy配列) panoptic_seg: パノプティックマップ(テンソル) segments_info: セグメント情報のリスト metadata: データセットのメタデータ Returns: result_image: 可視化された画像 “”” # Visualizerを作成 visualizer = Visualizer( image, # 元画像 metadata, # メタデータ(クラス名、色など) instance_mode=ColorMode.IMAGE # 元画像の上にオーバーレイ ) # パノプティック結果を描画 vis_output = visualizer.draw_panoptic_seg_predictions( panoptic_seg.to(“cpu”), # CPUに転送 segments_info ) return vis_output.get_image() # 可視化を実行 result_image = visualize_panoptic( image_rgb, panoptic_seg, segments_info, metadata ) # =================================================== # 結果の表示と保存 # =================================================== # 2つの画像を並べて表示 fig, axes = plt.subplots(1, 2, figsize=(16, 8)) # 元画像 axes[0].imshow(image_rgb) axes[0].set_title(“Original Image”, fontsize=14) axes[0].axis(‘off’) # パノプティック結果 axes[1].imshow(result_image) axes[1].set_title(“Panoptic Segmentation”, fontsize=14) axes[1].axis(‘off’) plt.tight_layout() plt.savefig(‘panoptic_result.png’, dpi=150, bbox_inches=’tight’) plt.show() print(“結果を保存しました: panoptic_result.png”)

6-6. ThingsとStuffの分離表示

# =================================================== # ThingsとStuffを分離して表示 # =================================================== def separate_things_stuff(panoptic_seg, segments_info): “”” パノプティックマップからThingsとStuffを分離 Args: panoptic_seg: パノプティックマップ segments_info: セグメント情報 Returns: things_mask: Thingsのみのマスク stuff_mask: Stuffのみのマスク “”” # 空のマスクを作成 things_mask = torch.zeros_like(panoptic_seg) stuff_mask = torch.zeros_like(panoptic_seg) for segment in segments_info: segment_id = segment[‘id’] is_thing = segment[‘isthing’] # セグメントのマスクを取得 mask = panoptic_seg == segment_id if is_thing: # Thingsの場合 things_mask[mask] = segment_id else: # Stuffの場合 stuff_mask[mask] = segment_id return things_mask, stuff_mask # ThingsとStuffを分離 things_mask, stuff_mask = separate_things_stuff(panoptic_seg, segments_info) # 3つの画像を並べて表示 fig, axes = plt.subplots(1, 3, figsize=(18, 6)) # 元画像 axes[0].imshow(image_rgb) axes[0].set_title(“Original Image”, fontsize=14) axes[0].axis(‘off’) # Thingsのみ things_np = things_mask.cpu().numpy() axes[1].imshow(things_np, cmap=’tab20′) axes[1].set_title(“Things (Countable Objects)”, fontsize=14) axes[1].axis(‘off’) # Stuffのみ stuff_np = stuff_mask.cpu().numpy() axes[2].imshow(stuff_np, cmap=’tab20b’) axes[2].set_title(“Stuff (Uncountable Regions)”, fontsize=14) axes[2].axis(‘off’) plt.tight_layout() plt.savefig(‘things_stuff_separation.png’, dpi=150, bbox_inches=’tight’) plt.show() print(“分離結果を保存しました: things_stuff_separation.png”)

6-7. 統計情報の計算

# =================================================== # 統計情報の計算 # =================================================== def compute_statistics(segments_info, image_shape): “”” パノプティック結果の統計情報を計算 Args: segments_info: セグメント情報 image_shape: 画像の形状 (H, W, C) Returns: stats: 統計情報の辞書 “”” image_area = image_shape[0] * image_shape[1] stats = { ‘things’: { ‘count’: 0, ‘total_area’: 0, ‘by_class’: {} }, ‘stuff’: { ‘count’: 0, ‘total_area’: 0, ‘by_class’: {} } } for segment in segments_info: category_id = segment[‘category_id’] is_thing = segment[‘isthing’] area = segment[‘area’] if is_thing: stats[‘things’][‘count’] += 1 stats[‘things’][‘total_area’] += area # クラス別カウント if category_id not in stats[‘things’][‘by_class’]: stats[‘things’][‘by_class’][category_id] = 0 stats[‘things’][‘by_class’][category_id] += 1 else: stats[‘stuff’][‘count’] += 1 stats[‘stuff’][‘total_area’] += area if category_id not in stats[‘stuff’][‘by_class’]: stats[‘stuff’][‘by_class’][category_id] = 0 stats[‘stuff’][‘by_class’][category_id] += 1 # カバレッジを計算 stats[‘things’][‘coverage’] = stats[‘things’][‘total_area’] / image_area * 100 stats[‘stuff’][‘coverage’] = stats[‘stuff’][‘total_area’] / image_area * 100 stats[‘total_coverage’] = (stats[‘things’][‘total_area’] + stats[‘stuff’][‘total_area’]) / image_area * 100 return stats # 統計情報を計算 stats = compute_statistics(segments_info, image.shape) print(“\n” + “=” * 50) print(“統計情報”) print(“=” * 50) print(f”\n【Things】”) print(f” 検出数: {stats[‘things’][‘count’]}個”) print(f” 総面積: {stats[‘things’][‘total_area’]:,}px”) print(f” カバレッジ: {stats[‘things’][‘coverage’]:.2f}%”) print(f”\n【Stuff】”) print(f” 領域数: {stats[‘stuff’][‘count’]}領域”) print(f” 総面積: {stats[‘stuff’][‘total_area’]:,}px”) print(f” カバレッジ: {stats[‘stuff’][‘coverage’]:.2f}%”) print(f”\n【全体】”) print(f” 総カバレッジ: {stats[‘total_coverage’]:.2f}%”)

実行結果:

================================================== 統計情報 ================================================== 【Things】 検出数: 5個 総面積: 121,288px カバレッジ: 5.79% 【Stuff】 領域数: 5領域 総面積: 1,973,058px カバレッジ: 94.21% 【全体】 総カバレッジ: 100.00%
💡 実装のポイントまとめ

1. Detectron2:
・FacebookのCV研究ライブラリ
・model_zooで事前学習済みモデルを簡単にロード
・DefaultPredictorで推論を実行

2. 出力形式:
・panoptic_seg: H×Wのテンソル(各ピクセルにセグメントID)
・segments_info: セグメント情報のリスト(ID、カテゴリ、isthing、面積)

3. Things vs Stuff:
・isthing=True: 可算物体(個別ID付き)
・isthing=False: 不可算領域(クラスのみ)

📝 練習問題

問題1:ThingsとStuffの分類(基礎)

以下の要素をThings(可算物体)とStuff(不可算領域)に分類し、その理由を説明してください。

リスト: 犬、海、バス、砂浜、リンゴ、森、信号機、コンクリート、鳥、雪
解答:

Things(可算物体):

1. 犬(dog) → 「1匹、2匹」と数えられる → 明確な境界がある → 個別追跡が可能 2. バス(bus) → 「1台、2台」と数えられる → 明確な形状 → 他の車両と区別可能 3. リンゴ(apple) → 「1個、2個」と数えられる → 個別の果物 → 境界が明確 4. 信号機(traffic light) → 「1つ、2つ」と数えられる → 個別の装置 → 状態(赤/黄/青)も識別 5. 鳥(bird) → 「1羽、2羽」と数えられる → 個別の生き物 → 追跡可能

Stuff(不可算領域):

1. 海(sea/water) → 「何個の海」とは数えない → 境界が曖昧 → 連続した領域 2. 砂浜(sand) → 連続した領域 → 個数の概念がない → 背景として認識 3. 森(forest/vegetation) → 「何個の森」とは数えない → 連続した植生 → 領域として扱う 4. コンクリート(concrete) → 連続した表面 → 境界が曖昧 → 背景/地面として認識 5. 雪(snow) → 連続した領域 → 個数の概念がない → 天候/地面の状態

判定基準のまとめ:

「何個ある?」で意味がある → Things 「どれくらいの面積?」が適切 → Stuff 境界が明確 → Things 境界が曖昧 → Stuff 個別追跡が必要 → Things 領域認識で十分 → Stuff

問題2:PQ(Panoptic Quality)の計算(中級)

以下の条件でPQ、SQ、RQを計算してください。

マッチしたセグメント(TP): 5個 IoU: [0.80, 0.72, 0.65, 0.78, 0.70] 誤検出(FP): 2個 見逃し(FN): 1個
解答:

与えられた情報:

TP = 5 IoU = [0.80, 0.72, 0.65, 0.78, 0.70] FP = 2 FN = 1

1. SQ(Segmentation Quality)の計算:

SQ = Σ IoU / |TP| Σ IoU = 0.80 + 0.72 + 0.65 + 0.78 + 0.70 = 3.65 SQ = 3.65 / 5 = 0.73 = 73.0% 解釈: マッチしたセグメントの平均IoUは73% 分割品質は良好

2. RQ(Recognition Quality)の計算:

RQ = TP / (TP + 0.5×FP + 0.5×FN) RQ = 5 / (5 + 0.5×2 + 0.5×1) = 5 / (5 + 1 + 0.5) = 5 / 6.5 = 0.769 = 76.9% 解釈: 認識品質は76.9% 2個誤検出、1個見逃し F1スコアと同等

3. PQ(Panoptic Quality)の計算:

PQ = SQ × RQ = 0.73 × 0.769 = 0.561 = 56.1% 解釈: 総合的な品質は56.1% Cityscapesの一般的なスコア範囲内

4. 結果の分析:

SQ = 73.0%: 分割品質は良好 → 境界の精度は十分 RQ = 76.9%: 認識品質も良好 → 誤検出2個、見逃し1個は許容範囲 PQ = 56.1%: 総合的に中〜良程度 → Cityscapes val(55-62%)の範囲内 改善の余地: ・IoU 0.65のセグメントを改善 → SQ向上 ・誤検出を減らす → RQ向上

問題3:Panoptic FPNの統合プロセス(中級)

以下のシーンでPanoptic FPNの統合プロセスを説明してください。

シーン: 2匹の犬、芝生、空 Instance Branch出力: 犬A: 信頼度0.92、マスクM_A 犬B: 信頼度0.85、マスクM_B 犬Aと犬Bは一部重複 Semantic Branch出力: 芝生: 下部60% 空: 上部40%
解答:

統合プロセスの詳細:

ステップ1: 初期化

panoptic_map: 全ピクセル未割り当て 初期状態: ┌─────────────────────────┐ │ ? ? ? ? ? ? │ │ ? ? ? ? ? ? │ │ ? ? ? ? ? ? │ │ ? ? ? ? ? ? │ └─────────────────────────┘

ステップ2: Thingsを信頼度順に配置

ソート結果: 犬A(0.92) > 犬B(0.85) 2a. 犬A(信頼度0.92)を配置: ┌─────────────────────────┐ │ ? ? ? ? ? ? │ │ ? 犬A 犬A ? ? ? │ │ ? 犬A 犬A ? ? ? │ │ ? ? ? ? ? ? │ └─────────────────────────┘ 犬A → (クラス=犬, インスタンスID=1) 2b. 犬B(信頼度0.85)を配置: 犬Bのマスクと犬Aの重複部分を確認 重複部分: 犬Aが既に割り当て済み → 犬A(信頼度高い)を優先 → 犬Bは重複部分を除外 ┌─────────────────────────┐ │ ? ? ? ? ? ? │ │ ? 犬A 犬A 犬B 犬B ? │ ← 犬Bは重複除外後 │ ? 犬A 犬A 犬B 犬B ? │ │ ? ? ? ? ? ? │ └─────────────────────────┘ 犬B → (クラス=犬, インスタンスID=2)

ステップ3: Stuffを配置

未割り当てピクセルにStuffを配置: ・上部: セマンティック予測 = 空 ・下部: セマンティック予測 = 芝生 ┌─────────────────────────┐ │ 空 空 空 空 空 空 │ ← Stuff(空) │ 芝生 犬A 犬A 犬B 犬B 芝生│ │ 芝生 犬A 犬A 犬B 犬B 芝生│ │ 芝生 芝生 芝生 芝生 芝生 芝生│ ← Stuff(芝生) └─────────────────────────┘ 空 → (クラス=空, インスタンスID=0) 芝生 → (クラス=芝生, インスタンスID=0)

ステップ4: 最終出力

panoptic_map: 各ピクセルに (クラス, インスタンスID) segments_info: [ {id: 1001, category_id: 犬, isthing: True, area: …}, ← 犬1 {id: 1002, category_id: 犬, isthing: True, area: …}, ← 犬2 {id: 7000, category_id: 空, isthing: False, area: …}, ← 空 {id: 8000, category_id: 芝生, isthing: False, area: …} ← 芝生 ] 結果: Things: 2匹の犬(個別識別) Stuff: 空と芝生(領域分類) すべてのピクセルがラベル付け済み

重要ポイント:

1. 信頼度の高い順に配置 → 重複時は高信頼度を優先 2. Things優先 → まずThingsを配置、残りにStuff 3. 重複解決 → 同じピクセルに複数のThingsは不可 → 先に配置した方(高信頼度)が優先 4. 完全カバー → すべてのピクセルがラベル付け

問題4:自動運転でのパノプティック活用(応用)

自動運転において、パノプティックセグメンテーションがセマンティック単独やインスタンス単独より優れている理由を、具体的な運転シナリオを例に説明してください。

解答:

シナリオ: 住宅街での走行(子供の飛び出し注意)

■ 状況設定 自車: 時速30kmで走行中 検出対象: ・子供A: 歩道上(安全) ・子供B: 道路と歩道の境界付近(要注意) ・駐車車両: 道路脇に2台 ・道路: 走行可能領域 ・歩道: 歩行者領域 ・住宅: 背景

セマンティックセグメンテーション単独の場合:

出力: ・人: 2箇所(区別なし) ・車: 2箇所(区別なし) ・道路: 中央領域 ・歩道: 両脇 問題点: 1. 「子供が2人」は分かるが、どの子供がどこか不明 → 子供Aと子供Bを区別できない → 「歩道にいる子供」vs「境界にいる子供」が分からない 2. 危険度の判定ができない → すべて同じ「人」として扱う → 優先的に注意すべき対象が不明 3. 追跡ができない → 「子供Bが道路に向かって移動中」を検出できない → 時系列での予測が困難 運転判断: 「前方に人がいる。減速?」 → 曖昧な判断しかできない

インスタンスセグメンテーション単独の場合:

出力: ・子供A: マスク、Bbox、ID=1 ・子供B: マスク、Bbox、ID=2 ・車1: マスク、Bbox ・車2: マスク、Bbox 問題点: 1. 道路と歩道の区別ができない → 「走行可能領域はどこか」不明 → 経路計画ができない 2. 「子供Bが道路と歩道の境界にいる」を認識できない → Stuffの情報がないため、位置の文脈が分からない 3. 安全領域の判定ができない → 「歩道に寄せて停車」ができない → 緊急時の対応が困難 運転判断: 「子供2人、車2台がいる。どこを走る?」 → 経路が決められない

パノプティックセグメンテーションの場合:

出力: Things(個別識別): ・子供A: 歩道上、ID=1、安全度=高 ・子供B: 道路/歩道境界、ID=2、安全度=低 ・車1: 道路脇駐車、ID=3 ・車2: 道路脇駐車、ID=4 Stuff(領域分類): ・道路: 中央領域(走行可能) ・歩道: 両脇(歩行者領域) ・住宅: 背景 完全なシーン理解: 1. 子供Aは歩道上 → 安全 → 通常速度で通過可能 2. 子供Bは道路/歩道境界 → 要注意 → 飛び出しの可能性 → 減速して注意 3. 車1, 車2は道路脇 → 死角に注意 → 車の陰からの飛び出し警戒 4. 道路領域 → 走行可能 → 子供Bを避けて走行 5. 歩道領域 → 緊急時の退避先 運転判断: 「子供B(道路境界)に注意して減速。 駐車車両の陰に注意。 道路中央を走行。」 → 明確で安全な判断が可能

パノプティックの優位性まとめ:

1. 完全なシーン理解 Things + Stuff = すべてのピクセル → 見落としがない 2. 個別識別 + 文脈 「子供Bが道路/歩道境界にいる」 = インスタンス(子供B)+ セマンティック(道路/歩道) 3. 危険度の判定 物体の位置 × 領域の種類 = リスク評価 4. 予測と計画 個別追跡 + 領域認識 = 安全な経路計画 5. 緊急対応 全領域把握 → 退避先の特定

📝 STEP 15 のまとめ

✅ このステップで学んだこと

1. パノプティックセグメンテーション
・セマンティック + インスタンスの統合
・すべてのピクセルをラベル付け + 物体は個別識別
・完全なシーン理解を実現

2. Things と Stuff
・Things: 可算物体(人、車)→ 個別ID付与
・Stuff: 不可算領域(道路、空)→ クラスのみ

3. Panoptic FPN
・Instance Branch(Things用)+ Semantic Branch(Stuff用)
・統合アルゴリズムで1つのマップに結合

4. PQ(Panoptic Quality)
・PQ = SQ × RQ
・SQ: 分割品質(平均IoU)
・RQ: 認識品質(F1相当)

5. 応用
・自動運転での完全なシーン理解
・物体追跡 + 領域認識 = 安全な運転判断

💡 重要ポイント

パノプティックセグメンテーションは、セマンティックとインスタンスの「いいとこ取り」です。すべてのピクセルをカバーしながら、物体は個別に識別できます。

これでPart 4(セグメンテーション)が完了しました!
次のPart 5では、「Vision Transformerと最新技術」を学びます。
STEP 16では、ViT(Vision Transformer)の理論から始めます!

📝

学習メモ

コンピュータビジョン(CV) - Step 15

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE