İçeriğe geç

Teknik Yazılar

Görüntü İşleme··7 dk okuma

Küçük Nesne Tespiti ve SAHI: Dilimli Çıkarım Nasıl Çalışır?

Doğrulama setinde mAP50 0.95 alan model, sahada 60 metreden hedefi neden göremiyor? Cevap modelde değil, piksel bütçesinde.

Doğrulama setinde mAP50 0.950 alan bir model elimizde. Aerial görüntülerde manken ve çadır tespitini rahatça yapıyor. Sonra irtifayı 20 metreden 60 metreye çıkarıyoruz ve model kör oluyor — hedef hâlâ karede, gözle bakınca seçiliyor, ama tespit kutusu gelmiyor.

İlk refleks modeli suçlamak oluyor: “yeterince eğitilmemiş”, “daha büyük model lazım”. İkisi de genelde yanlış. Sorun modelin öğrendiği şeyde değil, modele ne kadar piksel verdiğimizde. YOLO11m’i 1280 piksellik girdilerle eğitip uçuşta 640 tam kare beslediğimizde, 1920×1080’lik RTSP akışını 3 kat küçültmüş oluyoruz. Karede 30 piksel genişliğindeki bir manken, modelin gözünde 10 piksele iniyor.

Bu yazıda küçük nesnelerin nerede kaybolduğunu, SAHI’nin (Slicing Aided Hyper Inference — dilimleme destekli çıkarım) bunu nasıl çözdüğünü ve bize kaç kat işlem yükü getirdiğini anlatıyoruz. Sonunda ne zaman değmediğini de konuşuyoruz — bizim uçuş senaryomuzda çoğu zaman değmiyor.

Küçük nesne neden kaybolur: üç kademeli kayıp

Kayıp tek yerde değil, üst üste binen üç yerde oluşuyor.

1. Girdi ölçeklemesi. Model sabit bir çözünürlükte çalışır (imgsz). 1920×1080’lik kareyi 640’a indirdiğinizde her yönde 3 kat piksel kaybedersiniz — yani nesnenin alanı 9’a bölünür.

2. Stride (adım) ve downsampling. YOLO11’in tespit başları üç piramit seviyesinden beslenir. Resmi yolo11.yaml yapılandırmasında bunlar açıkça işaretlidir: P3/8-small, P4/16-medium, P5/32-large. En ince seviye olan P3 bile girdiyi 8 kat küçültür. Modelin girdisinde 10 piksel olan bir nesne, P3 ızgarasında ~1.25 hücreye karşılık gelir.

3. Feature map (öznitelik haritası) çözünürlüğü. Bir buçuk hücrelik bir izden sınıf ve kutu regresyonu çıkarmak, ağın elindeki bilgiyi zorlar. COCO’nun “küçük nesne” tanımı zaten alanı 32×32 = 1024 pikselin altındaki nesnelerdir; biz uçuşta bunun çok altına iniyoruz.

Aynı 30 piksellik nesnenin farklı kurulumlarda modele nasıl göründüğü:

Kurulum (kaynak 1920×1080) Ölçek Modeldeki boyut P3/8 ızgarasında
Tam kare, imgsz=640 0.33× ~10 px ~1.3 hücre
Tam kare, imgsz=1280 0.67× ~20 px ~2.5 hücre
640’lık dilim, imgsz=640 1.00× 30 px ~3.8 hücre
512’lik dilim, imgsz=640 1.25× ~37 px ~4.7 hücre

Tablodaki tek değişken dilimleme. Model, ağırlıklar, eğitim — hepsi aynı.

SAHI dilimleme şeması

SAHI ne yapıyor

SAHI’nin fikri basit: modeli değiştirme, girdiyi değiştir. Üç adım var.

Dilimleme. Görüntü, örtüşen sabit boyutlu parçalara bölünür. Her dilim modele ayrı ayrı verilir. Dilim modelin imgsz değerinden küçükse yukarı ölçeklenir — nesne büyür.

Örtüşme. Dilim sınırına denk gelen bir nesne ikiye bölünmesin diye komşu dilimler üst üste biner. Varsayılan örtüşme oranı 0.2’dir.

Global birleştirme. Her dilimin kutuları orijinal görüntü koordinatlarına geri taşınır ve tüm kare üzerinde tek seferde birleştirilir. SAHI’nin varsayılan birleştiricisi GREEDYNMM, varsayılan eşleşme metriği ise IOS (intersection over smaller — kesişimin küçük kutuya oranı), eşiği 0.5’tir. IOS’un tercih edilmesinin sebebi, dilim sınırında yarım kalmış bir kutuyla tam kutunun IoU’sunun düşük çıkması ama IOS’unun yüksek çıkmasıdır.

Bir de sessiz ama önemli bir varsayılan var: perform_standard_pred=True. SAHI dilimlere ek olarak tam kare üzerinde de bir tahmin yapar. Kütüphanenin kendi belgesindeki gerekçe net — “büyük nesne tespit doğruluğunu artırmak için”. Dilimler büyük nesneleri kesip parçaladığı için yalnız dilimli çıkarım büyükleri kaybeder.

from sahi import AutoDetectionModel
from sahi.predict import get_sliced_prediction

detection_model = AutoDetectionModel.from_pretrained(
    model_type="ultralytics",     # 2026 itibarıyla YOLO11/YOLO26 için doğru tip
    model_path="runs/detect/train/weights/best.pt",
    confidence_threshold=0.25,
    device="cuda:0",
)

result = get_sliced_prediction(
    "frames/0142.jpg",
    detection_model,
    slice_height=512,
    slice_width=512,
    overlap_height_ratio=0.2,     # varsayılan
    overlap_width_ratio=0.2,      # varsayılan
    perform_standard_pred=True,   # varsayılan — büyük nesneler için bırakın
    postprocess_type="GREEDYNMM", # varsayılan
    postprocess_match_metric="IOS",
    postprocess_match_threshold=0.5,
)

for p in result.object_prediction_list:
    print(p.category.name, p.score.value, p.bbox.to_xyxy())

Kurulum tarafında Ultralytics’in kendi rehberi pip install -U ultralytics sahi diyor.

Gerçek kazanç ne kadar?

SAHI makalesi (Akyon, Altinuc, Temizel — ICIP 2022), VisDrone ve xView havadan görüntü veri kümelerinde üç dedektörle ölçüm yapıyor. Bildirilen sonuçlar:

Dedektör Sadece dilimli çıkarım + dilimli ince ayar (kümülatif)
FCOS %6.8 AP %12.7 AP
VFNet %5.1 AP %13.4 AP
TOOD %5.3 AP %14.5 AP

Buradaki asıl mesaj ikinci sütun: sadece çıkarımı dilimlemek kazancın yarısını bile vermiyor. Eğitim verisini de dilimlediğinizde kazanç yaklaşık iki katına çıkıyor. Sebebi ölçek uyuşması — model tam karelerde eğitilip dilimlerde çalıştırılırsa, uçuşta hiç görmediği bir nesne ölçeğiyle karşılaşır.

Not: Ultralytics’in SAHI rehberi nicel bir AP rakamı vermiyor, sadece niteliksel fayda anlatıyor. Sayılar makaleden.

Eğitim verisini dilimlemek

SAHI’nin CLI’ı bunu tek komutta yapıyor; etiketler dilim koordinatlarına otomatik taşınıyor.

# COCO formatındaki veri kümesini dilimlere böl
sahi coco slice \
  --image_dir data/aerial/images \
  --dataset_json_path data/aerial/annotations.json \
  --slice_size 512 \
  --overlap_ratio 0.2 \
  --out_dir data/aerial_sliced

# Ultralytics formatına çevir
sahi coco yolo --image_dir ... --dataset_json_path ...

Dikkat edilecek iki varsayılan var. min_area_ratio varsayılanı 0.1’dir: kırpılan bir anotasyonun alanı orijinalinin %10’unun altına düşerse etiket sessizce atılır. Bir de --ignore_negative_samples bayrağı — bunu vermezseniz hiç nesne içermeyen dilimler de veri kümesine girer. Bunlar değerli negatif örneklerdir (yanlış pozitifleri azaltır), ama epoch süresini ve sınıf dengesini bozacak kadar çok olabilirler. Kendi veri kümemiz zaten dengesiz olduğu için (sınıflar arası ~7:1) negatif dilim oranını gözden geçirmeden açmak istemedik.

Maliyet: kaç forward pass?

Bu SAHI’nin faturası ve pazarlığa açık değil. 1920×1080’lik bir kare için SAHI’nin dilim üretim algoritmasına göre dilim sayıları:

Dilim boyutu Örtüşme Izgara Dilim sayısı +tam kare = toplam geçiş
1024×1024 0.2 3×2 6 7
640×640 0.2 4×2 8 9
512×512 0.2 5×3 15 16
512×512 0.4 6×3 18 19

Bizim uçuş kurulumumuzda (Jetson Orin NX 16GB, YOLO11m, 640 tam kare) ölçtüğümüz hız 15-20 FPS, yani kare başına kabaca 50-67 ms. 640’lık dilimlerle 9 geçiş yapılırsa bu kaba çarpımla ~450-600 ms’ye, yani ~2 FPS’e karşılık gelir. Bu bir projeksiyon; araç üzerinde ölçmedik. Ama büyüklük mertebesi net: SAHI, otonom uçuş sırasında canlı tespit için kullanacağımız bir şey değil.

Örtüşmeyi 0.2’den 0.4’e çıkarmanın 512’lik dilimlerde dilim sayısını 15’ten 18’e taşıdığına da dikkat edin — %20 ek maliyet, kayda değer bir kazanç garantisi olmadan.

Ne zaman değer, ne zaman değmez

Senaryo SAHI? Gerekçe
Uçuş sırasında canlı tespit (15-20 FPS hedefi) Hayır N kat geçiş FPS’i kullanılamaz seviyeye düşürür
Uçuş sonrası kayıttan hedef taraması Evet Gecikme önemsiz, kaçırılan hedef önemli
FastMosaic ile üretilen büyük ortofoto üzerinde tespit Evet Girdi zaten çok yüksek çözünürlüklü, tam kare beslemek anlamsız
Hedef karede >100 piksel (düşük irtifa yaklaşma) Hayır Tam kare zaten yeterli piksel veriyor
Etiketli veri az ve hedefler küçük Evet, ama önce dilimli ince ayar Kazancın yarısından fazlası eğitim tarafında
Model P2 başıyla yeniden eğitilebiliyor Önce onu dene Mimari çözüm çıkarımda ekstra maliyet getirmez

Son satır önemli: SAHI tek seçenek değil. Küçük nesne için stride-4’lük bir P2 tespit başı eklemek de bir yol — bu çıkarım maliyetini SAHI kadar artırmaz, ama modeli yeniden eğitmeyi ve mimariye dokunmayı gerektirir.

Sık düşülen tuzaklar

Örtüşmeyi nesne boyutundan küçük seçmek. 512’lik dilimde 0.2 örtüşme = 102 piksel bant. 102 pikselden geniş bir nesne her iki dilimde de kesik kalabilir. Kural: örtüşme bandı, beklediğiniz en büyük nesneden geniş olsun.

Dilimi çok küçük seçmek. Bağlam kaybolur. Model bir mankeni sadece silüetinden değil, çevresindeki zeminden de ayırt eder. 256’lık dilimlerde bu bağlam gider ve yanlış pozitifler artar.

perform_standard_pred=False yapmak. “Zaten dilimliyorum, tam kareye ne gerek var” diye kapatılıyor. Sonuç: büyük nesnelerde doğruluk düşüşü. Kütüphane bu parametreyi tam olarak bu yüzden varsayılan olarak açık bırakıyor.

Eğitim tam karede, çıkarım dilimde. En yaygın hata ve makaledeki sayıların söylediği şey de bu — kazancın büyük kısmı eğitim verisini de dilimlemekten geliyor.

min_area_ratio etkisini görmemek. sahi coco slice sonrası dilimlenmiş veri kümesindeki toplam anotasyon sayısını orijinalle karşılaştırın. Beklenmedik bir düşüş varsa kutularınız kırpma filtresine takılıyor demektir.

Sınıf-agnostik birleştirmeyi varsaymak. postprocess_class_agnostic varsayılanı False’tur; aynı nesne üzerinde farklı sınıflardan iki kutu varsa bunlar birleştirilmez. İki sınıflı bir modelde (bizde manken + çadır) bu genelde istenen davranıştır, ama farkında olarak seçin.

Pratik özet

  1. Önce piksel bütçesini hesaplayın. Hedefiniz modelin girdisinde kaç piksel? 20’nin altındaysa sorun mimaride değil, ölçekte. Model değiştirmeden önce bu sayıyı çıkarın.
  2. SAHI’yi çıkarım yaması olarak değil, veri hattı kararı olarak ele alın. sahi coco slice ile eğitim setini de dilimleyin; makaledeki kazancın yarısından fazlası oradan geliyor.
  3. Dilim boyutunu hedef boyutundan değil, örtüşme bandından seçin. Örtüşme bandı beklediğiniz en büyük nesneden geniş olmalı. 1080p için 512 veya 640 iyi başlangıçlar.
  4. N geçiş maliyetini önceden hesaplayın, sonradan şaşırmayın. 1080p + 640 dilim + 0.2 örtüşme = 9 forward pass. Gerçek zamanlı bir döngüye bunu koymadan önce FPS bütçenizi kontrol edin.
  5. SAHI’yi uçuş sonrası analize, tam kare çıkarımı uçuşa ayırın. Bizim planımız bu: uçuşta 640 tam kare ve 15-20 FPS, yerde kayıt üzerinde dilimli tarama.

Kaynaklar: SAHI makalesi (Akyon, Altinuc, Temizel, ICIP 2022, arXiv:2202.06934); Ultralytics SAHI dilimli çıkarım rehberi; obss/sahi deposu (sahi/predict.py, sahi/slicing.py, docs/cli.md); Ultralytics yolo11.yaml model yapılandırması.

  • SAHI
  • küçük-nesne-tespiti
  • YOLO
  • çıkarım
  • veri-hazırlama

Zor bir mühendislik probleminiz mi var?

Yapılabilir mi, ne kadar sürer, nasıl kurulur: konuşarak başlayalım.

Projenizi Konuşalım