Spaces:
Sleeping
Sleeping
| """Vision inference pipeline. Takes a film scan and returns defect JSON.""" | |
| from __future__ import annotations | |
| import logging | |
| import time | |
| from pathlib import Path | |
| from typing import Any | |
| from models.vision.minicpm_wrapper import get_detector | |
| logger = logging.getLogger(__name__) | |
| ALLOWED_LABELS = {"dust", "dirt", "scratch", "long_hair", "short_hair"} | |
| def extract_defects(image: Any) -> dict: | |
| """Run defect extraction on a PIL image. Returns defect dict + metadata.""" | |
| started = time.perf_counter() | |
| detector = get_detector() | |
| raw = detector.detect(image) | |
| elapsed = time.perf_counter() - started | |
| defects = raw.get("defects", []) | |
| if not isinstance(defects, list): | |
| logger.warning("Model output 'defects' is not a list: %r", type(defects)) | |
| defects = [] | |
| cleaned: list[dict] = [] | |
| dropped = 0 | |
| for d in defects: | |
| if not isinstance(d, dict): | |
| dropped += 1 | |
| continue | |
| label = d.get("label") | |
| bbox = d.get("bbox") | |
| if label not in ALLOWED_LABELS: | |
| dropped += 1 | |
| continue | |
| if not isinstance(bbox, (list, tuple)) or len(bbox) != 4: | |
| dropped += 1 | |
| continue | |
| try: | |
| x_min, y_min, x_max, y_max = (float(v) for v in bbox) | |
| except (TypeError, ValueError): | |
| dropped += 1 | |
| continue | |
| if not (0.0 <= x_min <= 1.0 and 0.0 <= y_min <= 1.0): | |
| dropped += 1 | |
| continue | |
| if not (0.0 <= x_max <= 1.0 and 0.0 <= y_max <= 1.0): | |
| dropped += 1 | |
| continue | |
| if x_max <= x_min or y_max <= y_min: | |
| dropped += 1 | |
| continue | |
| cleaned.append({"label": label, "bbox": [x_min, y_min, x_max, y_max]}) | |
| label_counts: dict[str, int] = {} | |
| for d in cleaned: | |
| label_counts[d["label"]] = label_counts.get(d["label"], 0) + 1 | |
| return { | |
| "defects": cleaned, | |
| "defect_count": len(cleaned), | |
| "label_counts": label_counts, | |
| "dropped_count": dropped, | |
| "inference_seconds": round(elapsed, 3), | |
| "model_path": detector.model_path, | |
| } | |
| def extract_defects_from_path(image_path: str | Path) -> dict: | |
| """Convenience: open image from path and run extraction.""" | |
| from PIL import Image | |
| img = Image.open(image_path).convert("RGB") | |
| return extract_defects(img) | |