v0.3.0: game-window picker + NCC recognition + artifacts + ?-merged
- window_capture.py: enumerate top-level windows (pygetwindow) and capture a specific one via PrintWindow PW_RENDERFULLCONTENT (works on non-focused windows). Linux falls back to mss region grab. - recognizer.py: replace MAE matcher with NCC over numpy vectors. Each rotatable slab generates 4 templates (0/90/180/270). Adds 248 artifact templates and an empty-cell heuristic (low mean/std-dev). Cells below confidence floor are tagged "unknown" — likely merged "?" boxes. - gui.py: new ScreenshotFrame with [게임 창 선택] button → window picker dialog → bbox crop → recognize → editable preview grid with per-cell CellEditor that handles slab / artifact / merged(?) / empty. Merged cells let user pick which two slabs got combined + a level. - artifacts.py + bundled _artifacts.json (248 entries from WhiteDog1004/sephiria) for matching and rendering. - renderer.py: factored CDN fetch into _fetch_image; added fetch_artifact_image(). - requirements.txt: + numpy, pygetwindow (Win), pywin32 (Win). - docker-build-cmd.sh: upgrade PyInstaller to 5.x inside cdrx container so numpy DLL manifest reads work. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -62,22 +62,18 @@ def _local_path(slab_image: str) -> str:
|
||||
return os.path.join(CACHE_DIR, os.path.basename(slab_image))
|
||||
|
||||
|
||||
def fetch_slab_image(slab_image: str, timeout: float = 10.0) -> Optional[Image.Image]:
|
||||
"""Return a PIL Image for the slab, downloading + caching if needed.
|
||||
|
||||
Returns None if download fails — caller draws a placeholder.
|
||||
"""
|
||||
def _fetch_image(rel_or_url: str, timeout: float = 10.0) -> Optional[Image.Image]:
|
||||
"""Fetch an image from the CDN. Accepts a full URL or a 'slabs/foo.png' path."""
|
||||
_ensure_cache_dir()
|
||||
path = _local_path(slab_image)
|
||||
path = _local_path(rel_or_url)
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
return Image.open(path).convert("RGBA")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
import requests # lazy import; allow renderer use without network if cached
|
||||
|
||||
url = f"{CDN_BASE}/{slab_image.lstrip('/')}"
|
||||
import requests
|
||||
url = rel_or_url if rel_or_url.startswith("http") else f"{CDN_BASE}/{rel_or_url.lstrip('/')}"
|
||||
r = requests.get(url, timeout=timeout)
|
||||
if r.status_code != 200:
|
||||
return None
|
||||
@@ -88,6 +84,16 @@ def fetch_slab_image(slab_image: str, timeout: float = 10.0) -> Optional[Image.I
|
||||
return None
|
||||
|
||||
|
||||
def fetch_slab_image(slab_image: str, timeout: float = 10.0) -> Optional[Image.Image]:
|
||||
"""Return a PIL Image for the slab. Caches under CACHE_DIR."""
|
||||
return _fetch_image(slab_image, timeout=timeout)
|
||||
|
||||
|
||||
def fetch_artifact_image(url: str, timeout: float = 10.0) -> Optional[Image.Image]:
|
||||
"""Return a PIL Image for an artifact (full URL from artifacts.json)."""
|
||||
return _fetch_image(url, timeout=timeout)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Font
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user