sprite / NOTES.md
Cnass's picture
Upload 11 files
2414b69 verified
|
Raw History Blame Contribute Delete
8.24 kB
# Session notes
Oturumlar arası bağlam aktarımı için tutuluyor. Silinebilir.
---
## Bölüm 1 — ÇÖZÜLDÜ: segmentasyon (animasyon strip'leri + tileset)
Önceki oturumdan kalan iki problem de kapandı. Detay `README.md` §2'de;
burada neyin neden değiştiği özetleniyor.
### Problem 1 — animasyon strip'leri parçalanıyordu
`spritesheet.png` gibi sheet'lerde her kare tek sprite yerine düzinelerce
parçaya bölünüyordu. İki ayrı sebep vardı:
1. Bir karenin parçaları gap-tabanlı birleştirme yarıçapından uzakta.
2. Daha kötüsü: bu parçacıkların çoğu 1-2px olduğu için `find_components`
onları `MIN_SPRITE_SIZE_PX` altı speckle sayıp ATIYORDU. Kareler
sadece bölünmüyor, KAYBOLUYORDU (8 karenin 2'si hiç görünmüyordu).
"Klon" sorunu da buradan geliyordu — kalan özdeş küçük parçaları
duplicate detector haklı olarak klon işaretliyordu.
**Çözüm:** `detect_separator_grid()` — her kesim çizgisinin tamamen arka
plan olduğu bir periyot+faz arıyor. Böyle bir çizgi hiçbir piksel bloğunu
kesemez. Önceki oturumda takılınan nokta buydu: `_grid_candidate_divisors`
hücre boyutunun sheet boyutunu tam bölmesini şart koşuyordu ve skorlama
iki gerçek sheet arasında çelişiyordu. Yeni dedektör ikisine de ihtiyaç
duymuyor; kesimin doğruluğu içerik istatistiğinden ÇIKARILMIYOR, doğrudan
DOĞRULANIYOR.
İki incelik: **faz da aranmalı** (11px adımlı kafeste boşluklar 8, 19,
30... konumlarında), ve **"en küçük yasal periyot" yetmiyor** (parçacıklı
bir karenin içindeki tesadüfi boş satırdan geçen kesim yasal ama kareyi
ikiye böler; adaylar `hücre sayısı × en dar boşluk` ile puanlanıyor).
`regroup_by_grid()` gap sonucunu bu hücrelere topluyor ve **sadece
birleştirme** yapıyor: gap aşamasının zaten birleştirdiği bir sprite
(yaprak+sap) hücreleri union'lıyor, asla bölünmüyor.
### Problem 2 — `Dungeon_Tileset.png` tek dev blok oluyordu
Grid fallback devreye girmiyordu (eski koşul "en fazla 6 bölge" istiyordu,
sheet'te 21 bölge vardı).
**Çözüm:** aşırı-birleşme koşuluna alan tabanlı dal (bir ekseni baştan
sona kaplayan VE alanın ≥%25'ini yutan bölge) + `detect_tile_grid()` —
hücre adımını sheet'in kendi **kenar periyodikliğinden** okuyor. Kritik
nokta: gerçek adımın her KATI da gerçek tile sınırlarına denk gelir, o
yüzden en yüksek puanı hep en büyük kat alır (16px tileset 32/80 okunur);
bu yüzden "en iyi puana yakın EN KÜÇÜK adım" seçiliyor. İki eksenin aynı
kare hücrede anlaşması da şart — bu, tek eksende periyodik animasyon
sheet'lerini bu yoldan uzak tutuyor.
### Sonuçlar
| sheet | önce | sonra | doğrusu |
|---|---|---|---|
| `spritesheet.png` 128×128 | 7 (2 kare kayıp) | 8 | 8 kare |
| `spritesheet (1).png` 512×256 | 54 | 27 | 27 kare |
| `spritesheet (2).png` 256×256 | 71 | 24 | 24 kare |
| `Dungeon_Tileset.png` 160×160 | 21 (dev blok) | 100 | 100 tile |
| `Dungeon_Tiles.png` 400×400 | 300 | 297 | 300 tile* |
\* 297 = 300 tile, biri 4 hücrelik kompozit obje olarak birleştirildi
(aşağıya bak). Beş sheet'te de kutular tüm ön plan piksellerini kapsıyor
(coverage 1.000).
### Ayrıca: O(n²) ölçeklenme
`merge_related_components` ve güven skorundaki en-yakın-komşu taraması
her çifti karşılaştırıyordu. İkisi de uzamsal indekse alındı; aday
kümeleri kesin testlerin dikkate alacağı çiftlerin üst kümesi olduğu için
**sonuçlar birebir aynı** (brute-force referansa karşı doğrulandı).
2048×2048'lik, ~16k sprite'a bölünen bir sheet: dakikalar → ~2s.
---
## Bölüm 2 — Kompozit objeler (çok hücreye yayılan tek obje)
Grid kesimi karolar için doğru, çok hücreye yayılan objeler için yanlış.
Üç hücreye inen bir merdiven tek şeydir; hücre hücre kesilince tek
başlarına anlamsız üç parça olur ve tekrar eden orta bölümü kendi diğer
yarısının duplicate'i sayılır.
`_composite_cell_groups()` grid kesiminden sonra **tek bir bağlı çizim
parçasına** ait hücreleri tekrar birleştiriyor. İki incelik:
- Test **bağlı-bileşen kimliği**, "iki komşu hücre sınırda değiyor mu"
DEĞİL. Bunlar eşdeğer görünür ama değildir: iki düz dolgu karosu tüm
ortak kenarları boyunca değer ve saçma bir sprite'a kaynaşır. (Önce
bunu denedim, `Dungeon_Tileset.png`'de tam olarak bunu yaptı.)
- Kimlik de tek başına yetmiyor: zindanın zemini ve duvarları da tek
devasa bağlı bölge. Ayıran şey büyüklük — kompozit obje birkaç hücre
(`GRID_COMPOSITE_MAX_CELLS`, varsayılan 6), arazi düzinelerce.
**Bilinen sınır (açıkça yazıyorum):** bu sadece obje kendi başına bir
çizim adasıysa çalışıyor. `Dungeon_Tileset.png`'de sütunlar ve kapı
çevredeki araziye DEĞECEK şekilde çizilmiş — üst bölge tamamı 17.399
piksellik tek opak ada, sheet'in %68'i — yani hiçbir yerel kural bunları
zemini de kaynaştırmadan ayıramaz. Onlar için elle **Merge** aracı var.
Kendi arka planı üzerinde duran objeler (yaygın durum) otomatik hallediliyor.
---
## Bölüm 3 — Dataset kalitesi (yeni)
Sprite'ları doğru kesmek işin yarısı. Diğer yarısı sonucun eğitilmeye
değer olup olmadığı — farklı bir soru, farklı hata biçimleri. Detay
`README.md` §3'te.
- **`auto_tagger.py`** — kategori/style/tag önerisi. En güçlü sinyal
**provenans**: tile grid'den çıkan sprite tileset karosudur, separator
grid'den çıkan animasyon karesidir. Bunlar içerik tahmini değil, nasıl
üretildiğine dair gerçek. `AUTO_TAG_MIN_CONFIDENCE` altında KATEGORİ
"unknown"a düşer (yanlış kategori boş kategoriden kötüdür); style
iddiaları piksellerin destekleyebileceğiyle sınırlı (4 renk = Game Boy
DEĞİL; `gameboy` sadece gerçek 4-tonlu monokrom rampa için, NES tahmini
yok). Her otomatik satır insan dokunana kadar `auto_tagged=1` taşıyor.
- **Toplu atama** (`POST /api/batch/assign`) — kapsam: aktif sheet / tüm
pending / hepsi. Etiketler **varsayılan olarak eklemeli**, böylece
toplu geçiş auto-tagger'ın sprite başına bulduklarını ezmiyor.
- **`POST /api/batch/dedupe-keep-one`** — duplicate zincirini
**geçişli** takip edip (#40 → #12 → #3 tek kümedir) her kümeden en iyi
puanlıyı tutuyor, gerisini reddediyor. "Reject Duplicates"tan farkı:
bir tileset aynı zemin karosunu meşru olarak onlarca kez tekrar eder.
- **Train/val/test** (`dataset_manager.assign_split`) — deterministik
(hash, shuffle değil) ve **sheet bazlı gruplu**. Animasyonun 24 karesi
neredeyse aynı görüntüler; sprite bazlı bölmek kare 7'yi train'e kare
8'i test'e koyar ve skor ezberi ölçer. Az sayıda sheet varken bir bölüm
boş kalabilir — split önizlemesi bunu saklamak yerine söylüyor.
- **`dataset_report.py`** — kompozisyon uyarıları (bir sınıf >%60, çok
ince sınıflar, çoğu metadata denetlenmemiş, tek kaynak sheet, tek
çözünürlük) + kalite skoru kalibrasyonu. Kalibrasyon **çoğunluk taban
çizgisini** de raporluyor: en iyi eşik "hep büyük sınıfı tahmin et"i
ancak geçiyorsa, uydurma bir öneri yerine "bu skor senin kararlarını
ayırmıyor, elle incelemeye devam et" diyor.
- **Animasyon önizleme** (`GET /api/animation/frames`) — kareler
**orijinal sheet konumlarına** göre ortak bir tuvale yerleştiriliyor.
Sıkı kırpılmış crop'ları arka arkaya oynatmak animasyonu titretir.
## Test durumu
100/100 geçiyor: segmentation 26, dataset 20, api 14, auto_tagger 12,
extraction 10, duplicates 10, background 8.
`test_dataset.py` ve `test_api.py` `config.DB_PATH`'i geçici bir dosyaya
yönlendirip `database`'i reload ediyor, yani Space'in gerçek verisine
dokunmuyorlar (doğrulandı).
## Hâlâ yapılmadı
- Arayüzde segmentasyon modu seçici / "Force grid size" alanı yok.
`segment_sheet(mode=...)` üç modu da destekliyor ("auto", "gap",
"grid") ama `analyzer.process_sheet` her zaman "auto" çağırıyor;
bunu bir API parametresi olarak dışarı açmak küçük bir iş.
- Kompozit birleştirme, objenin araziye değdiği durumları çözemiyor
(yukarıda "bilinen sınır"). Bunun için semantik bir model gerekir.