0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

手探りしてみる CV/ ML/ NN: 9日目 sam3を深掘りする話 2

Posted at

Tracker の中身を追う - sam3 深掘り

前回は Detector の仕組みを見ました。Detector は Learned Queries を使って画像から最大100個のオブジェクト候補を検出する DETR-style のモデルでした。

今回は Tracker を深掘りします。Video処理で時間的に一貫したセグメンテーションを実現するのがTrackerの役割です。そして調べていくと、意外な発見がありました。


${\huge \textsf{Trackerはテキストプロンプト使わない}}$

自分的にはこれは結構驚きだった・・・

1. DetectorとTrackerの役割分担

まず基本的な構成を確認しておきましょう。

Sam3VideoBase
├── Detector (Sam3ImageOnVideo)
│   └── テキスト・幾何学プロンプトで新規検出
│   └── 解像度: 1008×1008
│   └── バックボーン: ViT (Shared) + Detector Neck
│
└── Tracker (Sam3TrackerPredictor)
    └── 既存オブジェクトを時間的に追跡
    └── 解像度: 288×288 (latent)
    └── バックボーン: ViT (Shared) + Tracker Neck (SAM2-style)

重要なのは、両方が同じViTバックボーンを共有しているということです。

  • Detector: 毎フレーム新しいオブジェクトを検出
  • Tracker: 既存のオブジェクトを追跡

そして最後に統合レイヤー(Sam3VideoBase)が両者の出力をマージします。


2. Trackerへの入力 - 何が渡される?

新しい幾何学プロンプト(ポイント)がTrackerに渡される時、何が入力されるのでしょうか?

入力の構成

# Trackerへの入力
track_step(
    current_vision_feats,      # 現在のTracker用特徴 [B, C, 288, 288]
    point_inputs,              # ポイント座標 [B, P, 2] (UV座標)
    output_dict,               # 過去のメモリ
    use_prev_mem_frame=True,   # メモリ使用フラグ
)

1. 現在フレームの画像特徴

  • 共通ViTバックボーンからTracker専用Neckを通って出力された特徴
  • サイズは288×288のlatent特徴(Detector用の高解像度特徴とは異なるパス)
  • SAM2の設計を踏襲しているが、バックボーン自体はSAM3のViTを使用

2. 前フレームのメモリ

最大 6フレーム分 のメモリが使われます:

num_maskmem = 7  # 現在フレーム + 過去6フレーム

# メモリの構成
- Spatial memory features: 過去のマスク特徴最大6フレーム
- Object pointers: オブジェクトの高レベル表現最大16個

メモリフレームの選択:

t_rel == 1: 直前のフレーム (frame_idx - 1)
t_rel >= 2: r フレーム間隔 (evaluation時に設定可能)

3. 幾何学プロンプト(ポイント)

重要なのは、単純なUV座標が渡されるということです:

point_inputs = {
    "point_coords": [[x, y], ...],  # [B, P, 2] 絶対ピクセル座標
    "point_labels": [1, 0, ...],     # [B, P] (1=positive, 0=negative)
}

エンコーディングはTracker内部で行われます:

  1. Prompt Encoder: Fourier Feature Encoding
  2. SAM Mask Decoder: 画像特徴とcross-attention

3. 衝撃の事実:テキストプロンプトはTrackerに渡らない

ここで重要な発見がありました。Trackerはテキストプロンプトを使いません。

毎フレーム実行される run_backbone_and_detection()

# Step 1: テキストをDetectorに渡す
text_outputs = self.detector.backbone.forward_text(
    input_batch.find_text_batch
)

sam3_image_out = self.detector.forward_video_grounding_multigpu(
    backbone_out={
        "img_batch_all_stages": input_batch.img_batch,
        **text_outputs,  # ← テキスト特徴(毎フレーム)
    },
    ...
)

# Step 3: Tracker用の特徴(テキストなし!)
tracker_backbone_out = {
    "vision_features": tracker_backbone_fpn[-1],  # Tracker用特徴のみ
    "vision_pos_enc": ...,
    "backbone_fpn": tracker_backbone_fpn,
}

役割分担:

  • Detector: テキストプロンプトで新しいオブジェクトを検出(毎フレーム)
  • Tracker: 幾何学情報とメモリで既存オブジェクトを追跡

これはSAM3の基本アーキテクチャで、Sam3VideoBaseを継承する全てのモデルで共通です。


4. 初回プロンプトの特別性

幾何学プロンプト(ポイント)を追加する時、実は2つのケースがあります。

ケース1: 初めて追跡するフレーム

is_init_cond_frame = frame_idx not in inference_state["frames_already_tracked"]
# → True (初回)

# メモリを使わない!
pix_feat_with_mem = current_vision_feats + self.no_mem_embed

特徴:

  • is_init_cond_frame=True
  • メモリなしno_mem_embedのみ)
  • SAM-likeの単一フレームセグメンテーション

ケース2: 既に追跡済みのフレームに追加プロンプト

is_init_cond_frame = False  # 既に追跡済み

# メモリを使う
pix_feat_with_mem = self._prepare_memory_conditioned_features(
    current_vision_feats,
    output_dict,  # 前6フレーム + オブジェクトポインター16個
    ...
)

特徴:

  • is_init_cond_frame=False
  • メモリあり(前6フレーム + ポインター16個)
  • SAM2-likeのinteractive refinement

通常のpropagation(プロンプトなし)

# propagate_in_video()内
current_out = self._run_single_frame_inference(
    is_init_cond_frame=False,  # 常にFalse
    point_inputs=None,          # プロンプトなし
    use_prev_mem_frame=True,    # メモリ使う
)

差分まとめ:

ケース メモリ プロンプト 動作
初回プロンプト なし ポイント SAM-like
追加プロンプト あり ポイント SAM2-like refinement
Propagation あり なし 自動追跡

5. DetectorとTrackerの統合 - IoUマッチング

毎フレーム、DetectorとTrackerの両方が動いています。では、どう統合されるのでしょうか?

統合のフロー

# 毎フレーム
Detector  N個のマスク [N, 1008, 1008] + スコア [N]
Tracker   M個のマスク [M, 288, 288] + obj_id [M]

# build_outputs()で統合
1. TrackerマスクをF.interpolateで1008×1008
2. IoUマッチングN×M総当たり
3. マッチング判定と新規追加

IoUマッチングの詳細

# N×M IoUマトリックス計算
for each Detector mask (N個):
    for each Tracker mask (M個):
        iou = compute_iou(det_mask, trk_mask)

# マッチング判定
if max_iou > assoc_iou_thresh:
    # 既存オブジェクト → Trackerで継続
    use_tracker_mask(obj_id)
else:
    # 新規オブジェクト
    if detector_score > new_det_thresh:
        # 新規性 + 信頼性 → 追加
        add_new_object(new_obj_id)

判定基準:

  • 新規性: IoUマッチングで既存Trackerとマッチしない
  • 信頼性: Detectorスコア > 閾値

両方満たせば新しいobj_idで追加されます。

デフォルトの動作

# build_outputs()内

# Part 1: 既存オブジェクト(Tracker)
for obj_id, mask in zip(existing_obj_ids, tracker_masks):
    obj_id_to_mask[obj_id] = mask  # Trackerのまま

# Part 2: 新規オブジェクト(Detector)
for obj_id, mask in zip(new_obj_ids, detector_masks):
    obj_id_to_mask[obj_id] = mask  # Detectorから追加

# Part 3: Reconditioning(デフォルト無効)
if reconditioning_enabled:
    # 条件満たせばDetectorで置き換え
    obj_id_to_mask[obj_id] = detector_mask

デフォルト動作:

  • 既存オブジェクト → Trackerで継続(Detectorで置き換えない)
  • 新規オブジェクト → Detectorで追加
  • 置き換え(Reconditioning) → 無効reconstruction_bbox_iou_thresh=0.0

6. まとめ

Trackerの特徴

  1. 入力:

    • Tracker用特徴(288×288 latent)
    • 前フレームのメモリ(最大6フレーム + ポインター16個)
    • 幾何学プロンプト(ポイント、UV座標)
    • テキストプロンプトは使わない
  2. 動作モード:

    • 初回プロンプト:メモリなし(SAM-like)
    • 追加プロンプト:メモリあり(SAM2-like)
    • Propagation:自動追跡
  3. 統合方式:

    • IoU総当たりマッチング(N×M)
    • 新規性(IoU)+ 信頼性(スコア)で判定
    • デフォルトはTracker優先

DetectorとTrackerの役割分担

[Detector]
- テキストプロンプトで新規検出
- ViT特徴(1008×1008)
- 毎フレーム実行
- 学習されたPixelDecoder/FPN

[Tracker]
- 幾何学プロンプトで追跡
- Tracker用特徴(288×288)
- 前フレームのメモリ活用
- F.interpolateで1008×1008に

[統合レイヤー]
- IoUマッチング
- 既存→Tracker、新規→Detector
- obj_id管理

前回のDetectorと合わせて、SAM3のVideo処理の全体像が見えてきました。




参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?