オブジェクト検出とセグメンテーション: 比較と使用例
コンピューター ビジョンの問題に取り組む場合、適切なタスクとアーキテクチャを選択する そして基本的なこと。 物体検出, セマンティックセグメンテーション, インスタンスのセグメンテーション e パノプティックセグメンテーション 私はそうではありません 交換可能な代替案: それぞれが異なる質問に答え、異なる計算要件を備えています 特定のユースケースに適しています。間違ったアプローチを選択すると、リソースが無駄になったり、 さらに悪いことに、顧客の問題は解決されません。
この記事では、主要なビジュアル コンピュータ ビジョン タスクを厳密に比較します。 PyTorch での実際的な実装と、適切なアプローチを選択するための具体的なガイドラインを示します。 あなたのプロジェクトで。
何を学ぶか
- 検出、セマンティック、インスタンス、パノプティック セグメンテーションの基本的な違い
- いつどのアプローチを使用するか: 実用的なデシジョン ツリー
- 各タスクの主なアーキテクチャとそのトレードオフ
- PyTorch でのマルチタスク パイプラインの完全な実装
- 各タスクの評価指標 (mAP、mIoU、PQ)
- 実際のハードウェアでの速度と精度のベンチマーク
- ケーススタディ: 自動運転車、医療監視、小売分析
1. コンピュータビジョンの主なタスク
アプローチを比較する前に、視覚的な例を使用して各タスクを正確に定義してみましょう。
Immagine input: una strada con 3 persone e 2 auto
┌─────────────────────────────────────────────────────────────────┐
│ IMAGE CLASSIFICATION: "strada con veicoli e persone" │
│ Output: 1 label per tutta l'immagine │
│ Non dice WHERE ne QUANTI oggetti ci sono │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ OBJECT DETECTION: 5 bounding boxes │
│ [persona(0.95) x1,y1,x2,y2] │
│ [persona(0.88) x1,y1,x2,y2] │
│ [persona(0.91) x1,y1,x2,y2] │
│ [auto(0.97) x1,y1,x2,y2] │
│ [auto(0.94) x1,y1,x2,y2] │
│ Sa WHERE e QUANTI, ma non la forma precisa │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ SEGMENTAZIONE SEMANTICA: ogni pixel ha una classe │
│ pixel(100,200)="persona", pixel(300,400)="auto" │
│ Sa la FORMA precisa, ma non distingue le istanze │
│ Tutte le "persone" = stessa categoria, non identità separate │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ SEGMENTAZIONE DI ISTANZA: maschera per ogni oggetto │
│ persona_1 = {pixel: (100,200),(101,200),...} │
│ persona_2 = {pixel: (250,180),(251,180),...} │
│ Sa la FORMA e distingue le ISTANZE separate │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ SEGMENTAZIONE PANOPTICA: unione di semantica + istanza │
│ "cose" (countable): istanza per persona e auto │
│ "stuff" (uncountable): semantica per strada, cielo, edifici │
│ Sa TUTTO: forma, classe, istanza, sfondo │
└─────────────────────────────────────────────────────────────────┘
1.1 詳細な技術比較
コンピュータ ビジョン タスクの比較
| タスク | 出力 | 複雑 | スピード | GPUメモリ | メトリック |
|---|---|---|---|---|---|
| 分類 | ラベル + プロブ | 低い | 非常に高い | 低い | トップ1/5のアクセス |
| 物体検出 | Bボックス+ラベル | 平均 | 高い | 平均 | mAP@0.5 |
| 秒セマンティクス | ピクセルラベルマップ | 中~高 | 平均 | 高い | ミオ |
| 秒実例 | BBox + マスク | 高い | 低~中 | 高い | mAP@マスク |
| 秒パノプティック | 全て | 非常に高い | 低い | 非常に高い | PQ |
2. オブジェクト検出: アーキテクチャと実装
2.1 シングルステージと 2 ステージ
オブジェクト検出器は、次の 2 つの大きなアーキテクチャ カテゴリに分類されます。
1 段検出器と 2 段検出器の比較
| 特性 | シングルステージ (YOLO、SSD、RetinaNet) | 2 段階 (高速 R-CNN、マスク R-CNN) |
|---|---|---|
| パイプライン | 単一ネットワーク、直接予測 | RPN は地域を提案してから分類を提案します |
| スピード | 高 (30 ~ 150+ FPS) | 低 (5 ~ 15 FPS) |
| 正確さ | 小さなオブジェクトではわずかに低くなります | 特に小さなオブジェクトの精度が向上 |
| 一般的な使用方法 | リアルタイム、エッジ、ビデオ | オフライン分析、最高の精度 |
| 現代の例 | YOLO26、RT-DETR、DINO-DETR | より高速な R-CNN、カスケード R-CNN、DETR |
import torch
import torchvision
from torchvision.models.detection import fasterrcnn_resnet50_fpn
from torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
def create_faster_rcnn(num_classes: int) -> torch.nn.Module:
"""
Faster R-CNN con backbone ResNet-50 + FPN pre-addestrato.
Two-stage: RPN (Region Proposal Network) + classificatore.
"""
# Carica con pesi COCO pre-addestrati
model = fasterrcnn_resnet50_fpn(
weights=FasterRCNN_ResNet50_FPN_Weights.DEFAULT
)
# Sostituisce il classificatore per il numero di classi custom
# +1 perchè la classe 0 e riservata al "background"
in_features = model.roi_heads.box_predictor.cls_score.in_features
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes + 1)
return model
def train_detection_model(model, data_loader, num_epochs: int = 10, lr: float = 0.005):
"""
Training loop per Faster R-CNN.
Il modello calcola automaticamente le loss interne (classification + bbox regression + RPN).
"""
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
model.train()
optimizer = torch.optim.SGD(
model.parameters(),
lr=lr,
momentum=0.9,
weight_decay=0.0005
)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)
for epoch in range(num_epochs):
total_loss = 0.0
for images, targets in data_loader:
images = [img.to(device) for img in images]
targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
# Faster R-CNN restituisce un dizionario di loss in training mode
loss_dict = model(images, targets)
losses = sum(loss for loss in loss_dict.values())
optimizer.zero_grad()
losses.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
total_loss += losses.item()
scheduler.step()
avg_loss = total_loss / len(data_loader)
print(f"Epoch {epoch+1}/{num_epochs} | Loss: {avg_loss:.4f}")
def inference_faster_rcnn(model, image_tensor: torch.Tensor,
score_threshold: float = 0.5) -> list[dict]:
"""Inference con Faster R-CNN - restituisce predizioni filtrate."""
device = next(model.parameters()).device
model.eval()
with torch.no_grad():
predictions = model([image_tensor.to(device)])
results = []
pred = predictions[0]
for i, score in enumerate(pred['scores']):
if score >= score_threshold:
results.append({
'bbox': pred['boxes'][i].tolist(),
'score': float(score),
'label': int(pred['labels'][i])
})
return results
3. セマンティックセグメンテーション
La セマンティックセグメンテーション 個々のピクセルにクラスラベルを割り当てます 画像の。インスタンスは区別されません。すべての「人」は同じクラスに属します。 完全なシーン分析(自動運転、医療分析、リモートセンシング)に最適です。
3.1 DeepLabv3: ひどい畳み込み
DeepLabv3 (Chen et al., 2017) は、 ひどい畳み込み (または拡張した convolutions): パラメータを増加させずに受容野を増加させる「穴」を伴う畳み込み、 解像度を低下させることなくマルチスケールのコンテキストをキャプチャするために不可欠です。
import torch
import torch.nn as nn
import torchvision.models.segmentation as seg_models
from torchvision.models.segmentation import DeepLabV3_ResNet50_Weights
def create_deeplabv3(num_classes: int) -> nn.Module:
"""
DeepLabv3 con backbone ResNet-50 pre-addestrato su COCO.
Usa Atrous Spatial Pyramid Pooling (ASPP) per multi-scale context.
"""
model = seg_models.deeplabv3_resnet50(
weights=DeepLabV3_ResNet50_Weights.DEFAULT
)
# Sostituisce il classificatore finale per il numero di classi custom
model.classifier[-1] = nn.Conv2d(
in_channels=256,
out_channels=num_classes,
kernel_size=1
)
# Anche l'auxiliary classifier (per training stability)
model.aux_classifier[-1] = nn.Conv2d(
in_channels=256,
out_channels=num_classes,
kernel_size=1
)
return model
def train_semantic_segmentation(model, data_loader, num_epochs: int = 20):
"""
Training loop per segmentazione semantica.
Loss: CrossEntropyLoss (ignora label -1 per pixel non annotati)
"""
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
criterion = nn.CrossEntropyLoss(ignore_index=255) # 255 = unlabeled pixel
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-4)
scheduler = torch.optim.lr_scheduler.PolynomialLR(optimizer, total_iters=num_epochs)
for epoch in range(num_epochs):
model.train()
total_loss = 0.0
for images, masks in data_loader:
images = images.to(device)
masks = masks.long().to(device) # [B, H, W] con valori 0..num_classes-1
# DeepLabv3 restituisce dict con 'out' e 'aux'
outputs = model(images)
main_loss = criterion(outputs['out'], masks)
aux_loss = criterion(outputs['aux'], masks) * 0.4 # peso ridotto
loss = main_loss + aux_loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
scheduler.step()
avg_loss = total_loss / len(data_loader)
miou = compute_miou(model, data_loader, device)
print(f"Epoch {epoch+1}/{num_epochs} | Loss: {avg_loss:.4f} | mIoU: {miou:.3f}")
def compute_miou(model, data_loader, device, num_classes: int = 21) -> float:
"""Calcola Mean IoU (metrica standard per segmentazione semantica)."""
model.eval()
intersection = torch.zeros(num_classes, device=device)
union = torch.zeros(num_classes, device=device)
with torch.no_grad():
for images, masks in data_loader:
images = images.to(device)
masks = masks.long().to(device)
preds = model(images)['out'].argmax(dim=1) # [B, H, W]
for cls in range(num_classes):
pred_cls = preds == cls
true_cls = masks == cls
intersection[cls] += (pred_cls & true_cls).sum()
union[cls] += (pred_cls | true_cls).sum()
iou = intersection / (union + 1e-10)
return float(iou[union > 0].mean())
4. マスク R-CNN を使用したインスタンスのセグメンテーション
La インスタンスのセグメンテーション オブジェクト検出を組み合わせる (バウンディング ボックス + クラス) 個々のインスタンスごとにピクセルレベルのセグメンテーションを使用します。各オブジェクトには独自のマスクがあります 独立したバイナリ。 マスク R-CNN (He et al.、2017) Faster R-CNN を拡張 マスク予測用に 3 番目の並列「ヘッド」を追加します。
import torch
import torchvision
from torchvision.models.detection import maskrcnn_resnet50_fpn
from torchvision.models.detection import MaskRCNN_ResNet50_FPN_Weights
from torchvision.models.detection.mask_rcnn import MaskRCNNPredictor
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
def create_mask_rcnn(num_classes: int) -> torch.nn.Module:
"""
Mask R-CNN: Faster R-CNN + Mask Head.
Output per ogni istanza: bbox + classe + maschera binaria 28x28.
"""
model = maskrcnn_resnet50_fpn(
weights=MaskRCNN_ResNet50_FPN_Weights.DEFAULT
)
# Sostituisce box predictor
in_features_box = model.roi_heads.box_predictor.cls_score.in_features
model.roi_heads.box_predictor = FastRCNNPredictor(
in_features_box, num_classes + 1
)
# Sostituisce mask predictor
in_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels
hidden_layer = 256
model.roi_heads.mask_predictor = MaskRCNNPredictor(
in_features_mask, hidden_layer, num_classes + 1
)
return model
def prepare_instance_target(boxes: list, labels: list, masks: list) -> dict:
"""
Prepara il target nel formato richiesto da Mask R-CNN.
masks: lista di array booleani [H, W] per ogni istanza.
"""
return {
'boxes': torch.tensor(boxes, dtype=torch.float32),
'labels': torch.tensor(labels, dtype=torch.int64),
'masks': torch.tensor(masks, dtype=torch.uint8) # [N, H, W]
}
def visualize_instance_predictions(image, predictions, score_threshold: float = 0.5):
"""
Visualizza bounding boxes e maschere di istanza su un'immagine.
"""
import numpy as np
import cv2
img = np.array(image)
colors = [(np.random.randint(100, 255), np.random.randint(100, 255),
np.random.randint(100, 255)) for _ in range(100)]
pred = predictions[0]
valid_idx = pred['scores'] >= score_threshold
for i, (box, mask, score, label) in enumerate(zip(
pred['boxes'][valid_idx],
pred['masks'][valid_idx],
pred['scores'][valid_idx],
pred['labels'][valid_idx]
)):
color = colors[i % len(colors)]
# Disegna bounding box
x1, y1, x2, y2 = [int(c) for c in box]
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
# Applica maschera semitrasparente
mask_binary = (mask[0].numpy() > 0.5).astype(np.uint8)
overlay = img.copy()
overlay[mask_binary == 1] = color
img = cv2.addWeighted(img, 0.6, overlay, 0.4, 0)
# Label con confidence
text = f"class {int(label)}: {float(score):.2f}"
cv2.putText(img, text, (x1, y1-5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
return img
5. 意思決定ツリー: どのタスクを選択するか?
Problema: "Cosa voglio sapere dell'immagine?"
|
├─ Solo "che oggetti ci sono"?
│ └── IMAGE CLASSIFICATION
│ Architetture: ResNet, EfficientNet, ViT
│ Esempi: quality gate industriale, filtro contenuti
│
├─ "Dove sono gli oggetti + quanti sono"?
│ └── OBJECT DETECTION
│ │
│ ├─ Serve velocità real-time (>30 FPS)?
│ │ └── Single-Stage: YOLO26, RT-DETR
│ │
│ └─ Serve massima accuratezza (oggetti piccoli)?
│ └── Two-Stage: Faster R-CNN, DETR
│
├─ "Che classe e ogni pixel" (no distinzione istanze)?
│ └── SEGMENTAZIONE SEMANTICA
│ Architetture: DeepLabv3, FCN, SegFormer
│ Esempi: analisi stradale, medica, telerilevamento
│
├─ "Separare ogni oggetto + sua forma esatta"?
│ └── SEGMENTAZIONE DI ISTANZA
│ Architetture: Mask R-CNN, SOLOv2, YOLACT
│ Esempi: conteggio oggetti, robotica, biologia
│
└─ "Tutto: oggetti separati + sfondo classificato"?
└── SEGMENTAZIONE PANOPTICA
Architetture: Panoptic FPN, Mask2Former
Esempi: guida autonoma completa, scene understanding
各タスクのユースケース
| セクタ | 検出 | 秒セマンティクス | 秒実例 | パノプティック |
|---|---|---|---|---|
| 自動車 | 歩行者/車両検知 | セグメント道路/車線 | 各ポーンを分離する | 完全な自己完結型シーン |
| 医者 | CTで病変の位置を特定する | セグメント臓器 | それぞれの腫瘍を分離する | 完全な解剖学的分析 |
| 小売り | カウンター棚製品 | 棚割図 | 各製品を識別する | 完全な棚分析 |
| 産業用 | 欠陥の検出 (境界ボックス) | 欠陥領域の分類 | 各欠陥をセグメント化する | 完全な部品検査 |
| 農業 | 木の実を数えます | セグメント植生 | それぞれの果物を切り分ける | 完全なフィールドマップ |
6. マルチタスク パイプライン: 検出 + セグメンテーション
実際のアプリケーションの多くでは、効率を高めるために複数のタスクを単一のアーキテクチャに結合すると便利です。 計算的な。実践的な例: 小売分析では、製品をローカライズする必要があります。 (検出)棚上の占有領域をセグメント化する(セマンティック セグメンテーション)よりも。
import torch
import torch.nn as nn
import torchvision.models as models
class MultiTaskDetectionSegmentation(nn.Module):
"""
Architettura multi-task che condivide un backbone ResNet-50 + FPN
tra due head: detection e segmentazione semantica.
"""
def __init__(self, num_det_classes: int, num_seg_classes: int):
super().__init__()
# Backbone condiviso: ResNet-50 con FPN
backbone = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
# Estrae feature a più scale
self.layer1 = nn.Sequential(backbone.conv1, backbone.bn1,
backbone.relu, backbone.maxpool,
backbone.layer1) # 1/4 risoluzione
self.layer2 = backbone.layer2 # 1/8
self.layer3 = backbone.layer3 # 1/16
self.layer4 = backbone.layer4 # 1/32
# FPN (Feature Pyramid Network) per multi-scale features
self.fpn = nn.ModuleDict({
'p5': nn.Conv2d(2048, 256, 1),
'p4': nn.Conv2d(1024, 256, 1),
'p3': nn.Conv2d(512, 256, 1),
'p2': nn.Conv2d(256, 256, 1),
})
# Detection head (semplificato)
self.det_head = nn.Sequential(
nn.Conv2d(256, 256, 3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, num_det_classes * (4 + 1), 1)
# 4 bbox coords + 1 objectness per ogni classe
)
# Segmentation head (decoder con upsampling)
self.seg_head = nn.Sequential(
nn.Conv2d(256, 256, 3, padding=1),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(256, 128, 4, stride=2, padding=1), # 2x upsample
nn.ReLU(inplace=True),
nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1), # 4x upsample
nn.ReLU(inplace=True),
nn.Conv2d(64, num_seg_classes, 1)
)
def forward(self, x: torch.Tensor) -> dict:
# Backbone
c2 = self.layer1(x) # 1/4
c3 = self.layer2(c2) # 1/8
c4 = self.layer3(c3) # 1/16
c5 = self.layer4(c4) # 1/32
# FPN top-down pathway
p5 = self.fpn['p5'](c5)
p4 = self.fpn['p4'](c4) + nn.functional.interpolate(p5, scale_factor=2)
p3 = self.fpn['p3'](c3) + nn.functional.interpolate(p4, scale_factor=2)
p2 = self.fpn['p2'](c2) + nn.functional.interpolate(p3, scale_factor=2)
# Task-specific heads
det_output = self.det_head(p3) # detection sul livello P3
seg_output = self.seg_head(p2) # segmentation su P2 (più alta risoluzione)
# Upsample seg output a dimensione input
seg_output = nn.functional.interpolate(
seg_output, size=x.shape[-2:], mode='bilinear', align_corners=False
)
return {'detection': det_output, 'segmentation': seg_output}
def compute_multitask_loss(outputs: dict, det_targets, seg_targets) -> torch.Tensor:
"""
Loss combinata multi-task con pesi bilanciati.
Loss totale = w_det * L_det + w_seg * L_seg
"""
det_criterion = nn.BCEWithLogitsLoss()
seg_criterion = nn.CrossEntropyLoss(ignore_index=255)
det_loss = det_criterion(outputs['detection'], det_targets)
seg_loss = seg_criterion(outputs['segmentation'], seg_targets)
# Pesi relativi (da tuning sperimentale)
total_loss = 1.0 * det_loss + 0.5 * seg_loss
return total_loss, {'det': det_loss.item(), 'seg': seg_loss.item()}
7. ベストプラクティスとパフォーマンスの比較
COCO データセットのベンチマーク パフォーマンス (2025)
| モデル | タスク | マップ/ミリオウ | FPS(V100) | パラメータ |
|---|---|---|---|---|
| ヨロ26m | 検出 | 57.2マップ | 100+ | 25M |
| より高速な R-CNN R50 | 検出 | 40.2マップ | 18 | 41M |
| DeepLabv3 R50 | 秒セマンティクス | 74.3万IoU | 45 | 39M |
| セグフォーマー-B5 | 秒セマンティクス | 83.1ミリオU | 15 | 85M |
| マスク R-CNN R50 | 秒実例 | 36.1 マップ | 14 | 44M |
| マスク2旧R50 | パノプティック | 51.9PQ | 8 | 44M |
よくある設計上の間違い
- 検出で十分な場合はセグメンテーションを使用します。 オブジェクトを数えたり位置を特定したりするだけの場合は、検出を使用します。セグメンテーションは、注釈を付けてトレーニングするのにはるかにコストがかかります。
- リアルタイム要件を無視します。 14 FPS で R-CNN をマスクすることは、ライブ監視システムには受け入れられません。レイテンシー要件に基づいてアーキテクチャを選択してください。
- セグメンテーション用の不均衡なデータセット: クラスがピクセルの 95% (背景など) を占める場合、モデルはそれを自明に学習します。加重損失またはクラス サンプリングを使用します。
- mIoU と MAP が混同されている: これらは異なる指標です。 mIoU はピクセルごとの精度 (セグメンテーション) を測定し、mAP は境界ボックスの品質 (検出) を測定します。
- バランスをとらずにマルチタスク: マルチタスク アーキテクチャでは、さまざまなタスクの損失の規模が大きく異なる可能性があります。勾配の正規化または不確実性の重み付けを使用します。
結論
私たちは、コンピューター ビジョン タスクの基本的な違いから、タスクの全領域を調査してきました。 実際の実装まで:
- 分類、検出、セマンティック、インスタンス、パノプティックセグメンテーションには、出力、コスト、ユースケースが異なります
- YOLO26 はリアルタイム検出の王様です。高速な R-CNN はオフライン精度に優れています
- DeepLabv3 はセマンティック セグメンテーションに最適です。マスク R-CNN はインスタンスの区別を追加します
- マルチタスク アーキテクチャにより、複数のタスクを共有バックボーンと組み合わせることができます。
- 提示された意思決定ツリーは、各問題に対する適切なアプローチの選択をガイドします。







