Recognizer v0.3.6: HDR-friendly empty + lower threshold + debug dump
User reports all 34 cells classified as 미인식 with score 0.00 even when the grid was correctly cropped. Multiple compounding issues: 1. _is_empty required mean<60 (dark) AND std<14. HDR/bright captures produce pinkish empty slots with mean ~150-180, so even empty cells fell through to template matching. Drop the mean check; uniformity alone (std<18 grayscale, std<22 per-channel) is the real signal. 2. Score 0.00 across the board strongly suggests templates list was empty (only path that returns exactly 0.0). Track per-bucket load counts (slabs_ok/fail, artifacts_ok/fail) and surface them in the GUI status bar so a CDN failure is immediately visible. Currently no signal at all on download failure. 3. min_score 0.55 was tuned against simulator-clean renders. Real game captures have decorative cell borders, stack-count badges in corners, HDR shader effects. Lower to 0.35 and inset cell crops by 16% on each side before matching to skip the decorative frame. 4. Add 디버그 저장 button + dump_debug() that saves screenshot.png, bbox_crop.png, cells/<row>-<col>.png, and report.txt with top-3 matches per cell to %LOCALAPPDATA%/sephiria_inv/debug/<timestamp>/. Lets us iterate on tuning from real captures without round-tripping raw screenshots through chat each time.
This commit is contained in:
@@ -413,6 +413,7 @@ class ScreenshotFrame(ttk.Frame):
|
||||
ttk.Button(ctl, text="전체 화면 캡처", command=self._capture_screen).pack(side="left", padx=4)
|
||||
ttk.Button(ctl, text="파일 열기…", command=self._open_file).pack(side="left", padx=4)
|
||||
ttk.Button(ctl, text="영역 재지정", command=self._reselect_bbox).pack(side="left", padx=4)
|
||||
ttk.Button(ctl, text="디버그 저장", command=self._save_debug).pack(side="left", padx=4)
|
||||
ttk.Button(ctl, text="이 구성으로 계산", command=self._confirm).pack(side="right")
|
||||
|
||||
self.status = ttk.Label(
|
||||
@@ -506,6 +507,8 @@ class ScreenshotFrame(ttk.Frame):
|
||||
# First call may download a lot — keep artifacts on (user wants them)
|
||||
warm_templates(include_artifacts=True)
|
||||
self._templates_warmed = True
|
||||
from .recognizer import load_stats
|
||||
self._tpl_stats = load_stats()
|
||||
slot_num = int(round(self.slot_var.get()))
|
||||
cells = recognize_image(
|
||||
self.image, self.bbox,
|
||||
@@ -515,6 +518,27 @@ class ScreenshotFrame(ttk.Frame):
|
||||
except Exception as e:
|
||||
self.after(0, lambda: messagebox.showerror("인식 실패", str(e)))
|
||||
|
||||
def _save_debug(self) -> None:
|
||||
if self.image is None or not self.bbox:
|
||||
messagebox.showinfo("안내", "먼저 캡처 + 영역 지정을 해주세요.")
|
||||
return
|
||||
import os
|
||||
from datetime import datetime
|
||||
from .recognizer import dump_debug
|
||||
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
|
||||
out_dir = os.path.join(base, "sephiria_inv", "debug",
|
||||
datetime.now().strftime("%Y%m%d-%H%M%S"))
|
||||
try:
|
||||
slot_num = int(round(self.slot_var.get()))
|
||||
report = dump_debug(self.image, self.bbox, out_dir, slot_num=slot_num)
|
||||
messagebox.showinfo(
|
||||
"디버그 저장 완료",
|
||||
f"폴더에 screenshot.png, bbox_crop.png, cells/, report.txt 가 저장됨.\n\n{out_dir}\n\n"
|
||||
f"report: {report}",
|
||||
)
|
||||
except Exception as e:
|
||||
messagebox.showerror("디버그 저장 실패", str(e))
|
||||
|
||||
def _show_cells(self, cells: List[CellResult]) -> None:
|
||||
self.cells = cells
|
||||
for c in self.preview_inner.winfo_children():
|
||||
@@ -541,10 +565,22 @@ class ScreenshotFrame(ttk.Frame):
|
||||
kind_counts[effective["kind"]] = kind_counts.get(effective["kind"], 0) + 1
|
||||
self._make_cell(y, x, slot_id, effective)
|
||||
|
||||
stats = getattr(self, "_tpl_stats", None) or {}
|
||||
tpl_line = ""
|
||||
if stats:
|
||||
total_loaded = stats.get("slabs_ok", 0) + stats.get("artifacts_ok", 0)
|
||||
total_failed = stats.get("slabs_fail", 0) + stats.get("artifacts_fail", 0)
|
||||
tpl_line = (
|
||||
f"\n템플릿: 슬랩 {stats.get('slabs_ok',0)}/{stats.get('slabs_ok',0)+stats.get('slabs_fail',0)} · "
|
||||
f"아티팩트 {stats.get('artifacts_ok',0)}/{stats.get('artifacts_ok',0)+stats.get('artifacts_fail',0)}"
|
||||
)
|
||||
if total_loaded == 0:
|
||||
tpl_line += " (CDN 다운로드 실패 — 인터넷 연결/방화벽 확인)"
|
||||
msg = (
|
||||
f"석판 {kind_counts.get('slab', 0)} · 아티팩트 {kind_counts.get('artifact', 0)} · "
|
||||
f"빈칸 {kind_counts.get('empty', 0)} · 합쳐진(?) {kind_counts.get('merged', 0)} · "
|
||||
f"미인식 {kind_counts.get('unknown', 0)}\n"
|
||||
f"미인식 {kind_counts.get('unknown', 0)}"
|
||||
f"{tpl_line}\n"
|
||||
"셀을 클릭하면 종류/값을 교정할 수 있습니다. 끝나면 [이 구성으로 계산]."
|
||||
)
|
||||
self.status["text"] = msg
|
||||
|
||||
Reference in New Issue
Block a user