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

SAHI(Slicing Aided Hyper Inference)는 객체 탐지 모델의 추론 성능을 향상시키기 위한 경량 비전 라이브러리입니다. 특히 고해상도 이미지에서 작은 객체를 탐지할 때 뛰어난 성능을 보입니다.
일반적인 객체 탐지 모델(YOLO, Faster R-CNN 등)은 입력 이미지를 고정된 크기(예: 640x640)로 리사이즈하여 처리합니다. 이 과정에서 작은 객체의 정보가 손실되어 탐지율이 떨어지는 문제가 발생합니다.
SAHI는 이 문제를 이미지 슬라이싱(Image Slicing) 기법으로 해결합니다.
SAHI의 핵심 원리
슬라이스 기반 추론
SAHI는 다음과 같은 방식으로 동작합니다:
- 이미지 분할: 원본 이미지를 여러 개의 작은 타일로 분할
- 개별 추론: 각 타일에 대해 독립적으로 객체 탐지 수행
- 결과 병합: 모든 타일의 탐지 결과를 원본 이미지 좌표계로 변환
- 중복 제거: 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로 쉬운 통합
- ✅ 다양한 객체 탐지 모델 지원
- ✅ 후처리 옵션 풍부
참고 자료:
- SAHI GitHub: https://github.com/obss/sahi
- 공식 문서: https://docs.sahi.ai/
- 논문: https://arxiv.org/abs/2202.06934
'영상처리 > Object Detection' 카테고리의 다른 글
| 학습 데이터 형식이 다른 라벨들 원하는 형식으로 변경하기 (120) | 2023.09.13 |
|---|