Spaces:
Sleeping
Sleeping
| """Gradio demo: PCB defect + golden reference -> enriched image (A11_CA prebackbone).""" | |
| from __future__ import annotations | |
| import os | |
| import socket | |
| from pathlib import Path | |
| # HF Spaces: SSR needs Node and often serves unstyled HTML when it fails. | |
| os.environ.setdefault("GRADIO_SSR_MODE", "false") | |
| _ROOT = Path(__file__).resolve().parent | |
| def _is_hf_space() -> bool: | |
| return bool(os.environ.get("SPACE_ID") or os.environ.get("SYSTEM") == "spaces") | |
| def _configure_runtime() -> None: | |
| os.environ.setdefault( | |
| "PREBACKBONE_ONLY_WEIGHTS", | |
| str(_ROOT / "weights" / "prebackbone_a11_ca.pt"), | |
| ) | |
| if _is_hf_space(): | |
| os.environ.setdefault("PRELOAD_MODEL", "1") | |
| _configure_runtime() | |
| import gradio_patch | |
| gradio_patch.apply() | |
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image | |
| from prebackbone_infer import enrich_pair, get_enricher | |
| TITLE = "RefDiffNet — PCB Reference–Defect Enrichment" | |
| DESCRIPTION = """ | |
| Upload a **defect PCB image** and its **golden reference** (same H×W, pre-aligned — e.g. `*_input.jpg` / `*_reference.jpg` from training). | |
| The RefDiffNet prebackbone outputs an **enriched** image: `enriched = defect + α · gate · delta` | |
| """ | |
| def run_demo(defect_path: str | None, reference_path: str | None): | |
| if not defect_path or not reference_path: | |
| raise gr.Error("Please upload both defect and reference images.") | |
| defect_rgb, ref_rgb, enriched_rgb = enrich_pair(defect_path, reference_path) | |
| defect_rgb = np.array(defect_rgb, dtype=np.uint8, copy=True) | |
| ref_rgb = np.array(ref_rgb, dtype=np.uint8, copy=True) | |
| enriched_rgb = np.array(enriched_rgb, dtype=np.uint8, copy=True) | |
| h = max(defect_rgb.shape[0], ref_rgb.shape[0], enriched_rgb.shape[0]) | |
| def _pad(im: np.ndarray) -> np.ndarray: | |
| if im.shape[0] == h: | |
| return im | |
| pad = h - im.shape[0] | |
| return np.pad(im, ((0, pad), (0, 0), (0, 0)), mode="constant", constant_values=114) | |
| row = np.concatenate([_pad(defect_rgb), _pad(ref_rgb), _pad(enriched_rgb)], axis=1) | |
| return Image.fromarray(enriched_rgb), Image.fromarray(row) | |
| def _example_pairs() -> list[list[str]]: | |
| """All *_input / *_reference pairs bundled in examples/ (from yolo26 prebackbone_samples).""" | |
| pairs: list[list[str]] = [] | |
| ex_dir = _ROOT / "examples" | |
| if not ex_dir.is_dir(): | |
| return pairs | |
| for inp in sorted(ex_dir.glob("*_input.jpg")): | |
| ref = inp.with_name(inp.name.replace("_input.", "_reference.")) | |
| if ref.is_file(): | |
| pairs.append([str(inp), str(ref)]) | |
| return pairs | |
| def _preload_model() -> None: | |
| try: | |
| get_enricher() | |
| print("[RefDiffNet] Prebackbone loaded.") | |
| except FileNotFoundError as e: | |
| print(f"[RefDiffNet] WARN: {e}") | |
| def build_demo() -> gr.Interface: | |
| return gr.Interface( | |
| fn=run_demo, | |
| inputs=[ | |
| gr.Image(type="filepath", label="Defect image (input)", height=320), | |
| gr.Image(type="filepath", label="Golden reference", height=320), | |
| ], | |
| outputs=[ | |
| gr.Image(type="pil", label="Enriched output", height=360), | |
| gr.Image(type="pil", label="Defect | Reference | Enriched", height=360), | |
| ], | |
| title=TITLE, | |
| description=DESCRIPTION.strip(), | |
| examples=_example_pairs() or None, | |
| examples_per_page=6, | |
| cache_examples=False, | |
| flagging_mode="never", | |
| theme=gr.themes.Soft(), | |
| ) | |
| demo = build_demo() | |
| def _local_launch_kwargs() -> dict: | |
| server_name = os.environ.get("GRADIO_SERVER_NAME", "127.0.0.1") | |
| preferred_port = int(os.environ.get("PORT", os.environ.get("GRADIO_SERVER_PORT", "7860"))) | |
| bind_host = "127.0.0.1" if server_name in ("127.0.0.1", "localhost") else server_name | |
| server_port = preferred_port | |
| if server_name in ("127.0.0.1", "localhost"): | |
| for port in range(preferred_port, preferred_port + 20): | |
| with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: | |
| sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
| try: | |
| sock.bind((bind_host, port)) | |
| server_port = port | |
| break | |
| except OSError: | |
| continue | |
| share = os.environ.get("GRADIO_SHARE", "").lower() in ("1", "true", "yes") | |
| return { | |
| "server_name": server_name, | |
| "server_port": server_port, | |
| "share": share, | |
| } | |
| def _launch() -> None: | |
| """Launch with SSR off (required for themed UI on HF Spaces).""" | |
| kwargs: dict = {"show_error": True, "ssr_mode": False} | |
| if _is_hf_space(): | |
| demo.launch(**kwargs) | |
| else: | |
| demo.launch( | |
| **_local_launch_kwargs(), | |
| inbrowser=False, | |
| prevent_thread_lock=False, | |
| **kwargs, | |
| ) | |
| if __name__ == "__main__": | |
| print(f"[RefDiffNet] gradio {getattr(gr, '__version__', 'unknown')} ssr_mode=False") | |
| if os.environ.get("PRELOAD_MODEL", "0").lower() in ("1", "true", "yes"): | |
| _preload_model() | |
| _launch() | |