객체 감지와 세분화: 비교 및 사용 사례
컴퓨터 비전 문제를 해결할 때 올바른 작업과 아키텍처 선택 그리고 기본. 객체 감지, 의미론적 분할, 인스턴스 분할 e Panoptic 세분화 나는 아니다 상호 교환 가능한 대안: 각각은 서로 다른 질문에 대답하고 서로 다른 계산 요구 사항을 갖습니다. 특정 사용 사례에 적합합니다. 잘못된 접근 방식을 선택하면 리소스가 낭비되거나 더 나쁜 것은 고객의 문제를 해결하지 못하는 것입니다.
이 기사에서는 주요 시각적 컴퓨터 비전 작업을 엄격하게 비교할 것입니다. PyTorch의 실제 구현과 올바른 접근 방식을 선택하기 위한 구체적인 지침을 제공합니다. 당신의 프로젝트에서.
무엇을 배울 것인가
- 감지, 의미 체계, 인스턴스 및 Panoptic 세분화의 근본적인 차이점
- 언제 어떤 접근법을 사용해야 하는가: 실용적인 의사결정 트리
- 각 작업의 주요 아키텍처와 장단점
- 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 Acc |
| 객체 감지 | BBox + 라벨 | 평균 | 높은 | 평균 | mAP@0.5 |
| 비서. 의미론 | 픽셀 라벨 지도 | 중간-높음 | 평균 | 높은 | 미우 |
| 비서. 사례 | BBox + 마스크 | 높은 | 낮음-중간 | 높은 | 맵@마스크 |
| 비서. 파놉틱 | 모두 | 매우 높음 | 낮은 | 매우 높음 | PQ |
2. 객체 감지: 아키텍처 및 구현
2.1 1단 대 2단
객체 감지기는 두 가지 광범위한 아키텍처 범주로 나뉩니다.
단일 단계 및 2단계 감지기
| 특성 | 단일 스테이지(YOLO, SSD, RetinaNet) | 2단계(더 빠른 R-CNN, 마스크 R-CNN) |
|---|---|---|
| 파이프라인 | 단일 네트워크, 직접 예측 | RPN은 지역을 제안한 후 분류합니다. |
| 속도 | 높음(30-150+FPS) | 낮음(5-15FPS) |
| 정확성 | 작은 물체의 경우 약간 낮음 | 정확성 향상, 특히 작은 물체 |
| 일반적인 사용 | 실시간, 엣지, 비디오 | 오프라인 분석, 최대 정밀도 |
| 현대적인 예 | 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)은 다음을 사용합니다. 심각한 회선 (또는 확장됨 컨볼루션): 매개변수를 증가시키지 않고 수용 필드를 증가시키는 "홀"이 있는 컨볼루션, 해상도를 줄이지 않고 다중 규모 컨텍스트를 캡처하는 데 필수적입니다.
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. Mask R-CNN을 이용한 인스턴스 분할
La 인스턴스 세분화 객체 감지 결합(경계 상자 + 클래스) 각 개별 인스턴스에 대해 픽셀 수준 분할을 사용합니다. 각 객체에는 고유한 마스크가 있습니다. 독립 바이너리. 마스크 R-CNN (He et al., 2017) Faster R-CNN 확장 마스크 예측을 위해 세 번째 병렬 "헤드"를 추가합니다.
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)
| 모델 | 작업 | 지도/mIoU | FPS(V100) | 매개변수 |
|---|---|---|---|---|
| 욜로26분 | 발각 | 57.2 맵 | 100+ | 25M |
| 더 빠른 R-CNN R50 | 발각 | 40.2맵 | 18 | 41M |
| DeepLabv3 R50 | 비서. 의미론 | 74.3mIoU | 45 | 39M |
| 세그포머-B5 | 비서. 의미론 | 83.1mIoU | 15 | 85M |
| 마스크 R-CNN R50 | 비서. 사례 | 36.1 맵 | 14 | 44M |
| 마스크2이전 R50 | 파놉틱 | 51.9PQ | 8 | 44M |
일반적인 디자인 실수
- 감지가 충분할 경우 분할을 사용합니다. 물체의 수를 세거나 위치를 찾아야 한다면 탐지를 사용하세요. 세분화는 주석을 달고 훈련하는 데 훨씬 더 많은 비용이 듭니다.
- 실시간 요구 사항을 무시합니다. 14FPS의 Mask R-CNN은 실시간 감시 시스템에 허용되지 않습니다. 대기 시간 요구 사항에 따라 아키텍처를 선택하세요.
- 세분화를 위한 불균형 데이터세트: 클래스가 픽셀의 95%(예: 배경)를 차지하는 경우 모델은 이를 간단하게 학습합니다. 가중 손실 또는 클래스 샘플링을 사용합니다.
- mIoU와 mAP의 혼동: 그것들은 다른 측정 항목입니다. mIoU는 픽셀별 정밀도(분할)를 측정하고, mAP는 경계 상자(탐지)의 품질을 측정합니다.
- 균형을 맞추지 않은 다중 작업: 다중 작업 아키텍처에서는 다양한 작업의 손실 규모가 매우 다를 수 있습니다. 기울기 정규화 또는 불확실성 가중치를 사용합니다.
결론
우리는 근본적인 차이점부터 컴퓨터 비전 작업의 전체 스펙트럼을 탐색했습니다. 실제 구현에:
- 분류, 탐지, 의미 체계, 인스턴스 및 Panoptic 세분화는 출력, 비용 및 사용 사례가 다릅니다.
- YOLO26은 실시간 탐지의 왕입니다. 더 빠른 R-CNN은 오프라인 정확도가 뛰어납니다.
- DeepLabv3은 의미론적 분할에 적합합니다. Mask R-CNN은 인스턴스 구별을 추가합니다.
- 다중 작업 아키텍처를 통해 여러 작업을 공유 백본과 결합할 수 있습니다.
- 제시된 의사 결정 트리는 각 문제에 대한 올바른 접근 방식을 선택하도록 안내합니다.







