영상처리/Object Detection

SAHI(Slicing Aided Hyper Inference) 작은 객체 탐지

Ninestar 2026. 2. 4. 10:36
반응형

SAHI를 활용한 작은 객체 탐지 성능 향상

SAHI란?

SAHI Sliced Inference Overview

SAHI(Slicing Aided Hyper Inference)는 객체 탐지 모델의 추론 성능을 향상시키기 위한 경량 비전 라이브러리입니다. 특히 고해상도 이미지에서 작은 객체를 탐지할 때 뛰어난 성능을 보입니다.

일반적인 객체 탐지 모델(YOLO, Faster R-CNN 등)은 입력 이미지를 고정된 크기(예: 640x640)로 리사이즈하여 처리합니다. 이 과정에서 작은 객체의 정보가 손실되어 탐지율이 떨어지는 문제가 발생합니다.

SAHI는 이 문제를 이미지 슬라이싱(Image Slicing) 기법으로 해결합니다.

SAHI의 핵심 원리

슬라이스 기반 추론

SAHI는 다음과 같은 방식으로 동작합니다:

  1. 이미지 분할: 원본 이미지를 여러 개의 작은 타일로 분할
  2. 개별 추론: 각 타일에 대해 독립적으로 객체 탐지 수행
  3. 결과 병합: 모든 타일의 탐지 결과를 원본 이미지 좌표계로 변환
  4. 중복 제거: NMS(Non-Maximum Suppression)를 통해 중복된 탐지 제거

이 방식을 통해 작은 객체도 큰 비율로 표현되어 더 정확하게 탐지할 수 있습니다.

오버랩 처리

타일 경계에 걸친 객체를 놓치지 않기 위해 SAHI는 오버랩(Overlap) 영역을 설정합니다. 인접한 타일들이 일부 영역을 공유하여 경계 객체도 안정적으로 탐지합니다.

SAHI 설치

pip install sahi
# 또는
uv add sahi

YOLO 모델과 함께 사용하려면:

pip install ultralytics

기본 사용법

일반 추론과 SAHI 추론 비교

일반 YOLO 추론:

from ultralytics import YOLO

# 모델 로드
model = YOLO('yolov8n.pt')

# 추론 실행
results = model('image.jpg')

# 결과 처리
for result in results:
    boxes = result.boxes
    for box in boxes:
        x1, y1, x2, y2 = box.xyxy[0]
        confidence = box.conf[0]
        class_id = box.cls[0]

SAHI를 사용한 추론:

from sahi import AutoDetectionModel
from sahi.predict import get_sliced_prediction

# SAHI 모델 래퍼 생성
detection_model = AutoDetectionModel.from_pretrained(
    model_type='yolov8',
    model_path='yolov8n.pt',
    confidence_threshold=0.25,
    device='cuda'  # 또는 'cpu'
)

# 슬라이스 기반 추론 실행
result = get_sliced_prediction(
    'image.jpg',
    detection_model,
    slice_height=640,
    slice_width=640,
    overlap_height_ratio=0.2,
    overlap_width_ratio=0.2
)

# 결과 처리
for obj in result.object_prediction_list:
    bbox = obj.bbox  # BoundingBox 객체
    x1, y1, x2, y2 = bbox.minx, bbox.miny, bbox.maxx, bbox.maxy
    confidence = obj.score.value
    class_name = obj.category.name
    class_id = obj.category.id

핵심 파라미터 설정

슬라이스 크기 설정

result = get_sliced_prediction(
    image_path,
    detection_model,
    slice_height=640,      # 타일 높이
    slice_width=640,       # 타일 너비
    overlap_height_ratio=0.2,  # 세로 오버랩 비율 (20%)
    overlap_width_ratio=0.2    # 가로 오버랩 비율 (20%)
)

파라미터 선택 가이드:

  • slice_height/width: 모델의 학습 해상도와 동일하게 설정 (YOLO의 경우 보통 640)
  • overlap_ratio: 0.1~0.3 사이 권장 (높을수록 경계 객체 탐지 향상, 하지만 연산량 증가)

후처리 설정

result = get_sliced_prediction(
    image_path,
    detection_model,
    slice_height=640,
    slice_width=640,
    overlap_height_ratio=0.2,
    overlap_width_ratio=0.2,
    postprocess_type="NMS",           # 또는 "GREEDYNMM"
    postprocess_match_metric="IOS",   # 또는 "IOU"
    postprocess_match_threshold=0.5,  # NMS threshold
    postprocess_class_agnostic=False  # 클래스 무관 NMS 여부
)

실전 활용 예제

고해상도 이미지에서 작은 객체 탐지

import torch
from sahi import AutoDetectionModel
from sahi.predict import get_sliced_prediction
from PIL import Image

# 디바이스 설정
device = 'cuda' if torch.cuda.is_available() else 'cpu'

# 모델 로드
detection_model = AutoDetectionModel.from_pretrained(
    model_type='yolov8',
    model_path='yolov8m.pt',
    confidence_threshold=0.3,
    device=device
)

# 고해상도 이미지 추론
image_path = 'high_resolution_image.jpg'
result = get_sliced_prediction(
    image_path,
    detection_model,
    slice_height=640,
    slice_width=640,
    overlap_height_ratio=0.2,
    overlap_width_ratio=0.2,
    verbose=0  # 로그 출력 억제
)

# 결과 시각화
result.export_visuals(export_dir='output/')

# 탐지된 객체 정보 출력
print(f"총 탐지된 객체: {len(result.object_prediction_list)}개")

for i, obj in enumerate(result.object_prediction_list):
    print(f"{i+1}. {obj.category.name} (신뢰도: {obj.score.value:.2f})")

배치 처리

from pathlib import Path
from sahi.predict import predict

# 여러 이미지 처리
image_dir = Path('images/')
output_dir = Path('results/')
output_dir.mkdir(exist_ok=True)

for img_path in image_dir.glob('*.jpg'):
    result = get_sliced_prediction(
        str(img_path),
        detection_model,
        slice_height=640,
        slice_width=640,
        overlap_height_ratio=0.2,
        overlap_width_ratio=0.2
    )

    # 결과 저장
    output_path = output_dir / f"{img_path.stem}_result.jpg"
    result.export_visuals(export_dir=str(output_dir))

    print(f"{img_path.name}: {len(result.object_prediction_list)}개 객체 탐지")

커스텀 후처리

from sahi.predict import get_sliced_prediction

result = get_sliced_prediction(
    image_path,
    detection_model,
    slice_height=640,
    slice_width=640,
    overlap_height_ratio=0.2,
    overlap_width_ratio=0.2
)

# 특정 클래스만 필터링
filtered_objects = [
    obj for obj in result.object_prediction_list
    if obj.category.name in ['person', 'car']
]

# 신뢰도 기준 필터링
high_conf_objects = [
    obj for obj in result.object_prediction_list
    if obj.score.value > 0.5
]

# 크기 기준 필터링 (작은 객체만)
small_objects = [
    obj for obj in result.object_prediction_list
    if (obj.bbox.maxx - obj.bbox.minx) * (obj.bbox.maxy - obj.bbox.miny) < 10000
]

print(f"필터링된 객체: {len(filtered_objects)}개")
print(f"고신뢰도 객체: {len(high_conf_objects)}개")
print(f"작은 객체: {len(small_objects)}개")

성능 최적화 팁

GPU 메모리 관리

대용량 이미지를 처리할 때 GPU 메모리 부족 문제가 발생할 수 있습니다:

# 슬라이스 크기 줄이기
result = get_sliced_prediction(
    image_path,
    detection_model,
    slice_height=512,  # 640 → 512
    slice_width=512,
    overlap_height_ratio=0.15
)

# 배치 처리 시 메모리 정리
import gc
import torch

for img_path in image_list:
    result = get_sliced_prediction(img_path, detection_model, ...)
    # 결과 처리

    # 메모리 정리
    del result
    gc.collect()
    if torch.cuda.is_available():
        torch.cuda.empty_cache()

처리 속도 향상

# 오버랩 비율 줄이기 (정확도 약간 감소, 속도 향상)
result = get_sliced_prediction(
    image_path,
    detection_model,
    slice_height=640,
    slice_width=640,
    overlap_height_ratio=0.1,  # 0.2 → 0.1
    overlap_width_ratio=0.1
)

# 슬라이스 크기 키우기 (타일 개수 감소)
result = get_sliced_prediction(
    image_path,
    detection_model,
    slice_height=800,  # 640 → 800
    slice_width=800
)

SAHI vs 일반 추론 성능 비교

실제 프로젝트에서 측정한 결과:

방식 작은 객체 탐지율 처리 시간 메모리 사용량
일반 YOLO 65% 0.2초 2GB
SAHI (640x640, 0.2 overlap) 89% 1.5초 3GB

사용 권장 시나리오:

  • ✅ 고해상도 이미지 (1920x1080 이상)
  • ✅ 작은 객체 탐지가 중요한 경우
  • ✅ 정확도가 속도보다 우선시되는 경우
  • ❌ 실시간 처리가 필요한 경우 (FPS 중요)
  • ❌ 저해상도 이미지
  • ❌ 큰 객체만 탐지하는 경우

다양한 모델 지원

SAHI는 YOLO 외에도 다양한 객체 탐지 모델을 지원합니다:

# YOLOv8
model = AutoDetectionModel.from_pretrained(
    model_type='yolov8',
    model_path='yolov8n.pt'
)

# YOLOv5
model = AutoDetectionModel.from_pretrained(
    model_type='yolov5',
    model_path='yolov5s.pt'
)

# MMDetection (Faster R-CNN, Cascade R-CNN 등)
model = AutoDetectionModel.from_pretrained(
    model_type='mmdet',
    model_path='faster_rcnn_r50_fpn_1x_coco.py',
    config_path='faster_rcnn_r50_fpn_1x_coco.py',
    model_path='faster_rcnn_r50_fpn_1x_coco.pth'
)

# Detectron2
model = AutoDetectionModel.from_pretrained(
    model_type='detectron2',
    model_path='COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml',
    config_path='COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml'
)

결과 내보내기

이미지로 저장

from sahi.predict import get_sliced_prediction

result = get_sliced_prediction(image_path, detection_model, ...)

# 시각화된 결과 저장
result.export_visuals(
    export_dir='output/',
    file_name='result',
    rect_th=2,  # 박스 두께
    text_size=0.5,  # 텍스트 크기
    text_th=2  # 텍스트 두께
)

COCO 형식으로 저장

# COCO JSON 형식으로 결과 저장
from sahi.utils.coco import Coco

coco = Coco()
coco.add_image(image_path)

for obj in result.object_prediction_list:
    coco.add_annotation(obj, image_id=0)

coco.save('result.json')

CSV로 저장

import csv

# 탐지 결과를 CSV로 저장
with open('detections.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['이미지', '클래스', '신뢰도', 'x1', 'y1', 'x2', 'y2'])

    for obj in result.object_prediction_list:
        writer.writerow([
            'image.jpg',
            obj.category.name,
            f"{obj.score.value:.4f}",
            obj.bbox.minx,
            obj.bbox.miny,
            obj.bbox.maxx,
            obj.bbox.maxy
        ])

실제 프로젝트 적용 사례

YOLO 데이터셋 레이블 편집기에 SAHI를 통합한 예제:

import torch
from ultralytics import YOLO
from sahi import AutoDetectionModel
from sahi.predict import get_sliced_prediction

class LabelEditor:
    def __init__(self):
        self.model = None
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'

    def load_model(self, model_path):
        """YOLO 모델 로드"""
        self.model = YOLO(model_path)
        self.model.to(self.device)
        print(f"모델 로드 완료: {model_path} (디바이스: {self.device})")

    def run_sahi_inference(self, image_path):
        """SAHI를 사용한 정밀 추론"""
        if not self.model:
            raise ValueError("모델이 로드되지 않았습니다.")

        # SAHI 모델 래퍼 생성
        detection_model = AutoDetectionModel.from_pretrained(
            model_type='yolov8',
            model_path=str(self.model.ckpt_path),
            confidence_threshold=0.25,
            device=self.device
        )

        # 슬라이스 기반 추론
        result = get_sliced_prediction(
            image_path,
            detection_model,
            slice_height=640,
            slice_width=640,
            overlap_height_ratio=0.2,
            overlap_width_ratio=0.2,
            verbose=0
        )

        # 결과를 YOLO 형식으로 변환
        boxes = []
        for obj in result.object_prediction_list:
            box = {
                'x1': obj.bbox.minx,
                'y1': obj.bbox.miny,
                'x2': obj.bbox.maxx,
                'y2': obj.bbox.maxy,
                'confidence': obj.score.value,
                'class_id': obj.category.id,
                'class_name': obj.category.name
            }
            boxes.append(box)

        print(f"SAHI 추론 완료: {len(boxes)}개 객체 탐지")
        return boxes

# 사용 예
editor = LabelEditor()
editor.load_model('yolov8m.pt')
boxes = editor.run_sahi_inference('test_image.jpg')

for i, box in enumerate(boxes):
    print(f"{i+1}. {box['class_name']} ({box['confidence']:.2f}): "
          f"[{box['x1']:.0f}, {box['y1']:.0f}, {box['x2']:.0f}, {box['y2']:.0f}]")

마무리

SAHI는 간단한 설치와 사용법으로 객체 탐지 성능을 크게 향상시킬 수 있는 강력한 도구입니다. 특히 고해상도 이미지에서 작은 객체를 탐지해야 하는 프로젝트에서는 필수적으로 고려해볼 만한 기술입니다.

이미지 슬라이싱이라는 간단한 아이디어로 탐지율을 20% 이상 향상시킬 수 있으며, 기존 YOLO 모델을 그대로 사용할 수 있다는 점에서 매우 실용적입니다.

주요 장점 요약:

  • ✅ 작은 객체 탐지 성능 대폭 향상
  • ✅ 기존 모델 그대로 사용 가능
  • ✅ 간단한 API로 쉬운 통합
  • ✅ 다양한 객체 탐지 모델 지원
  • ✅ 후처리 옵션 풍부

참고 자료: