Spaces:
Sleeping
Sleeping
Download scripts/test_full_system.py from RevanShriyan/Chandra-Align: direct link, hf CLI and curl.
- Browser
- Download file 25.5 kB
-
https://huggingface.co/spaces/RevanShriyan/Chandra-Align/resolve/main/scripts/test_full_system.py
- Command line
-
hf download hf://spaces/RevanShriyan/Chandra-Align/scripts/test_full_system.py
-
curl -L -o test_full_system.py https://huggingface.co/spaces/RevanShriyan/Chandra-Align/resolve/main/scripts/test_full_system.py
25.5 kB
| #!/usr/bin/env python3 | |
| """Bounded local and optional live QA checks for Chandra-Align. | |
| Run ``python scripts/test_full_system.py --live`` to include a small number of | |
| sequential Hugging Face requests. Live calls are deliberately serialized to | |
| avoid creating a quota or concurrency stress event on the hosted Space. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import importlib.util | |
| import json | |
| import math | |
| import os | |
| import subprocess | |
| import sys | |
| import tempfile | |
| import time | |
| import traceback | |
| from pathlib import Path | |
| from typing import Callable | |
| ROOT = Path(__file__).resolve().parents[1] | |
| if str(ROOT) not in sys.path: | |
| sys.path.insert(0, str(ROOT)) | |
| CHECKS: list[tuple[str, str, str, str]] = [] | |
| def module_from_script(name: str, relative_path: str): | |
| path = ROOT / relative_path | |
| spec = importlib.util.spec_from_file_location(name, path) | |
| if spec is None or spec.loader is None: | |
| raise ImportError(f"Cannot load {relative_path}") | |
| module = importlib.util.module_from_spec(spec) | |
| sys.modules[name] = module | |
| spec.loader.exec_module(module) | |
| return module | |
| def run_check(module: str, target: str, check: Callable[[], str]) -> None: | |
| started = time.monotonic() | |
| try: | |
| metric = check() | |
| status = "PASS" | |
| except Exception as exc: | |
| status = "FAIL" | |
| metric = f"{type(exc).__name__}: {exc}" | |
| traceback.print_exc() | |
| elapsed = time.monotonic() - started | |
| CHECKS.append((module, target, status, f"{metric} ({elapsed:.2f}s)")) | |
| def skip_check(module: str, target: str, reason: str) -> None: | |
| CHECKS.append((module, target, "SKIP", reason)) | |
| def check_polar_ode() -> str: | |
| from xml.etree.ElementTree import Element, SubElement | |
| ode = module_from_script("qa_get_search_results", "scripts/get_search_results.py") | |
| corners = { | |
| "UL": (-89.94, -170.0), | |
| "UR": (-89.94, -60.0), | |
| "LR": (-89.20, 60.0), | |
| "LL": (-89.20, 170.0), | |
| } | |
| ring = ode._unwrap_ring(corners) | |
| span = max(lon for lon, _ in ring) - min(lon for lon, _ in ring) | |
| if span <= 180: | |
| raise AssertionError(f"Expected polar longitude span >180°, got {span:.2f}°") | |
| poly = ode.corners_to_polygon(corners) | |
| if poly.is_empty or not poly.is_valid: | |
| raise AssertionError("Polar footprint did not form a valid polygon") | |
| class Response: | |
| def json(self): | |
| return {"ODEResults": {"Products": {"Product": [ | |
| {"pdsid": "same-tile"}, {"pdsid": "same-tile"} | |
| ]}}} | |
| def raise_for_status(self): | |
| return None | |
| def close(self): | |
| return None | |
| class Session: | |
| calls: list[dict] = [] | |
| def get(self, url, params, timeout): | |
| self.calls.append(dict(params)) | |
| return Response() | |
| session = Session() | |
| products = ode.query_ode(session, -89.94, -89.20, -170.0, 170.0) | |
| boxes = [(call["westlon"], call["eastlon"]) for call in session.calls] | |
| if boxes != [(190.0, 360.0), (0.0, 170.0)]: | |
| raise AssertionError(f"ODE polar query split was invalid: {boxes}") | |
| if len(products) != 1: | |
| raise AssertionError(f"Expected duplicate ODE results to collapse, got {len(products)}") | |
| # Exercise PDS4 coordinate parsing against an in-memory namespace-local tree. | |
| geometry = Element("Geometry_Parameters") | |
| container = SubElement(geometry, "Refined_Corner_Coordinates") | |
| for key, (lat, lon) in corners.items(): | |
| prefix = {"UL": "upper_left", "UR": "upper_right", "LR": "lower_right", "LL": "lower_left"}[key] | |
| SubElement(container, prefix + "_latitude").text = str(lat) | |
| SubElement(container, prefix + "_longitude").text = str(lon) | |
| parsed_ring = ode._unwrap_ring({key: (lat, lon) for key, (lat, lon) in corners.items()}) | |
| if len(parsed_ring) != 4: | |
| raise AssertionError("PDS4 corner ring was malformed") | |
| return f"span={span:.1f}°, ODE boxes={boxes}, unique candidates={len(products)}" | |
| def check_raw_staging() -> str: | |
| raw_nac = ROOT / "data" / "raw" / "nac" | |
| if not raw_nac.is_dir(): | |
| raise AssertionError(f"Reference staging directory missing: {raw_nac}") | |
| rasters = [p for p in raw_nac.rglob("*") if p.is_file() and p.suffix.lower() == ".img"] | |
| if not rasters: | |
| raise AssertionError("No staged NAC IMG rasters found") | |
| return f"{len(rasters)} IMG reference tiles staged" | |
| def check_gsd_factors() -> str: | |
| reg = module_from_script("qa_register_images_gsd", "scripts/register_images.py") | |
| expected = {"ohrc": (2.0, 1.0), "tmc2": (1.0, 10.0), "iirs": (1.0, 160.0)} | |
| observed = {sensor: reg.gsd_pyramid_factors(sensor) for sensor in expected} | |
| if observed != expected: | |
| raise AssertionError(f"Scale factors differ: {observed}") | |
| try: | |
| reg.gsd_pyramid_factors("unknown") | |
| except ValueError: | |
| pass | |
| else: | |
| raise AssertionError("Unknown sensor should be rejected") | |
| return str(observed) | |
| def check_preprocessing() -> str: | |
| import numpy as np | |
| reg = module_from_script("qa_register_images_preprocess", "scripts/register_images.py") | |
| image = np.full((320, 320), 13.0, np.float32) | |
| yy, xx = np.indices(image.shape) | |
| image += (xx / 320.0) * 18.0 + (yy / 320.0) * 7.0 | |
| for x, y, radius in ((80, 90, 22), (225, 80, 16), (165, 235, 31)): | |
| image[(xx - x) ** 2 + (yy - y) ** 2 < radius ** 2] += 10.0 | |
| gray = reg._to_gray8(image) | |
| shadow_like = reg._preprocess_for_matching(gray, "ohrc") | |
| nonpolar = reg._preprocess_for_matching(gray, "tmc2") | |
| if gray.dtype != np.uint8 or shadow_like.shape != image.shape or nonpolar.shape != image.shape: | |
| raise AssertionError("Preprocessing changed shape or failed uint8 normalization") | |
| if int(gray.min()) != 0 or int(gray.max()) != 255 or np.std(shadow_like) < 1: | |
| raise AssertionError("Percentile stretch/CLAHE/Sobel did not preserve useful contrast") | |
| return f"uint8 range={gray.min()}..{gray.max()}, CLAHE/Sobel std={np.std(shadow_like):.1f}" | |
| def check_iirs_and_multimodal_signatures() -> str: | |
| import inspect | |
| import numpy as np | |
| from chandra_align.preprocessing import preprocess_iirs_raster, resize_to_common_ground_sample | |
| iirs_parameters = inspect.signature(preprocess_iirs_raster).parameters | |
| resize_parameters = inspect.signature(resize_to_common_ground_sample).parameters | |
| if "contrast_percentiles" not in iirs_parameters or "clahe_clip_limit" not in iirs_parameters: | |
| raise AssertionError("IIRS preprocessing does not expose configurable contrast/CLAHE controls") | |
| if "minimum_long_side" not in resize_parameters: | |
| raise AssertionError("GSD resizer does not accept minimum_long_side") | |
| cube_band = np.random.default_rng(125).normal(100, 15, (384, 384)).astype(np.float32) | |
| iirs = preprocess_iirs_raster( | |
| cube_band, contrast_percentiles=(1.0, 99.0), clahe_clip_limit=4.5 | |
| ).processed_2d_raster | |
| ref = np.random.default_rng(5).integers(0, 256, (512, 512), dtype=np.uint8) | |
| resized, scale = resize_to_common_ground_sample(ref, 0.5, 10.0, 192) | |
| if iirs.shape != cube_band.shape or resized.shape[0] < 192 or resized.shape[1] < 192: | |
| raise AssertionError(f"Preprocess/resize shape contract failed: {iirs.shape}/{resized.shape}") | |
| return f"IIRS CLAHE output={iirs.shape}; common-GSD resized={resized.shape}, scale={scale:.3f}" | |
| def check_affine_and_gate() -> str: | |
| import cv2 | |
| import numpy as np | |
| reg = module_from_script("qa_register_images_affine", "scripts/register_images.py") | |
| rng = np.random.default_rng(26166) | |
| source = rng.uniform(0.0, 800.0, (36, 2)).astype(np.float32) | |
| angle, scale = math.radians(7.5), 1.013 | |
| truth = np.array([ | |
| [scale * math.cos(angle), -scale * math.sin(angle), 14.25], | |
| [scale * math.sin(angle), scale * math.cos(angle), -8.75], | |
| ], np.float32) | |
| target = source @ truth[:, :2].T + truth[:, 2] | |
| target[-8:] += rng.uniform(70.0, 180.0, (8, 2)).astype(np.float32) | |
| estimated, mask = cv2.estimateAffinePartial2D( | |
| source, target, method=cv2.RANSAC, ransacReprojThreshold=1.0, | |
| maxIters=10000, confidence=0.999, refineIters=10, | |
| ) | |
| inliers = mask.ravel().astype(bool) | |
| if estimated is None or int(inliers.sum()) < 24: | |
| raise AssertionError(f"Affine RANSAC did not reject injected outliers: {inliers.sum()} inliers") | |
| projected = source[inliers] @ estimated[:, :2].T + estimated[:, 2] | |
| residual = np.linalg.norm(projected - target[inliers], axis=1) | |
| median_error = float(np.median(residual)) | |
| if median_error >= 0.25: | |
| raise AssertionError(f"Affine inlier median residual too large: {median_error:.4f}px") | |
| if not reg.is_registration_acceptable(estimated, 8): | |
| raise AssertionError("Eight-inlier result should pass the acceptance gate") | |
| if reg.is_registration_acceptable(estimated, 7): | |
| raise AssertionError("Seven-inlier result should be rejected") | |
| if reg.is_registration_acceptable(np.full((2, 3), np.nan), 20): | |
| raise AssertionError("Non-finite transform should be rejected") | |
| return f"inliers={int(inliers.sum())}/36, median reprojection={median_error:.4f}px, gate=8" | |
| def check_app_partial_affine_contract() -> str: | |
| import numpy as np | |
| from chandra_align.metrics import get_sensor_pixel_scale | |
| from chandra_align.registration_transform import ( | |
| estimate_partial_affine, homogeneous_affine, is_valid_registration, | |
| ) | |
| if get_sensor_pixel_scale("TMC-2") != 5.0: | |
| raise AssertionError("TMC-2 default ground sample distance must be 5.0 m/px") | |
| rng = np.random.default_rng(26166) | |
| source = rng.uniform(0, 500, (40, 2)).astype(np.float32) | |
| truth = np.array([[0.999, -0.035, 12.5], [0.035, 0.999, -7.25]], np.float32) | |
| target = source @ truth[:, :2].T + truth[:, 2] | |
| target[-6:] += rng.normal(0, 100, (6, 2)).astype(np.float32) | |
| matrix, inliers = estimate_partial_affine(source, target) | |
| if matrix is None or matrix.shape != (2, 3): | |
| raise AssertionError("App transform helper did not return a 2x3 Partial Affine") | |
| count = int(inliers.sum()) | |
| if count < 8 or not is_valid_registration(matrix, count): | |
| raise AssertionError(f"App transform safety gate rejected valid synthetic data ({count} inliers)") | |
| homography = homogeneous_affine(matrix) | |
| if homography.shape != (3, 3) or not np.allclose(homography[2], (0, 0, 1)): | |
| raise AssertionError("Affine homogeneous serialization is malformed") | |
| if is_valid_registration(np.full((2, 3), np.nan), 20) or is_valid_registration(matrix, 7): | |
| raise AssertionError("Invalid transform or sub-threshold inliers passed the gate") | |
| return f"model=partial_affine_4dof, inliers={count}/40, TMC-2=5.0 m/px" | |
| def check_rift2_runtime() -> str: | |
| import warnings | |
| import cv2 | |
| import numpy as np | |
| from chandra_align.matcher import RIFT2Matcher | |
| rng = np.random.default_rng(26166) | |
| image = rng.normal(112.0, 24.0, (384, 384)).clip(0, 255).astype(np.uint8) | |
| for _ in range(55): | |
| x, y = rng.integers(18, 366, size=2) | |
| radius = int(rng.integers(5, 22)) | |
| cv2.circle(image, (int(x), int(y)), radius, int(rng.integers(20, 240)), 2) | |
| with warnings.catch_warnings(): | |
| warnings.filterwarnings("ignore", message="Module 'pyfftw'.*") | |
| matcher = RIFT2Matcher(npt=2048) | |
| points_a, points_b = matcher.match(image, image) | |
| if len(points_a) < 4 or len(points_a) != len(points_b): | |
| raise AssertionError(f"RIFT2 self-match returned only {len(points_a)} correspondences") | |
| return f"RIFT2 self-match={len(points_a)} correspondences" | |
| def _peak_working_set_mb() -> float: | |
| if os.name == "nt": | |
| import ctypes | |
| from ctypes import wintypes | |
| class Counters(ctypes.Structure): | |
| _fields_ = [ | |
| ("cb", wintypes.DWORD), ("PageFaultCount", wintypes.DWORD), | |
| ("PeakWorkingSetSize", ctypes.c_size_t), ("WorkingSetSize", ctypes.c_size_t), | |
| ("QuotaPeakPagedPoolUsage", ctypes.c_size_t), ("QuotaPagedPoolUsage", ctypes.c_size_t), | |
| ("QuotaPeakNonPagedPoolUsage", ctypes.c_size_t), ("QuotaNonPagedPoolUsage", ctypes.c_size_t), | |
| ("PagefileUsage", ctypes.c_size_t), ("PeakPagefileUsage", ctypes.c_size_t), | |
| ] | |
| counters = Counters() | |
| counters.cb = ctypes.sizeof(counters) | |
| psapi = ctypes.WinDLL("Psapi.dll") | |
| kernel = ctypes.WinDLL("Kernel32.dll") | |
| get_info = psapi.GetProcessMemoryInfo | |
| get_info.argtypes = [wintypes.HANDLE, ctypes.POINTER(Counters), wintypes.DWORD] | |
| get_info.restype = wintypes.BOOL | |
| if not get_info(kernel.GetCurrentProcess(), ctypes.byref(counters), counters.cb): | |
| raise OSError("GetProcessMemoryInfo failed") | |
| return counters.PeakWorkingSetSize / (1024 * 1024) | |
| import resource | |
| peak = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss | |
| return peak / (1024 * 1024 if sys.platform == "darwin" else 1024) | |
| def check_iirs_band_and_memory() -> str: | |
| reg = module_from_script("qa_register_images_iirs", "scripts/register_images.py") | |
| src = ROOT / reg.SOURCES["iirs"] | |
| if not src.is_file(): | |
| raise AssertionError(f"IIRS cube not staged at {src}") | |
| preset = reg.BENCHMARK_PRESETS["iirs"] | |
| values, width, height, band = reg._read_band(src, 4096, preset["band"]) | |
| if band != 125 or values.ndim != 2 or values.size == 0: | |
| raise AssertionError(f"Expected a streamed 2-D IIRS band 125, got band={band}, shape={values.shape}") | |
| label = src.with_suffix(".xml") | |
| if label.is_file(): | |
| import xml.etree.ElementTree as ET | |
| wanted = None | |
| for node in ET.parse(label).getroot().iter(): | |
| if node.tag.rsplit("}", 1)[-1] == "Band_Bin": | |
| record = {child.tag.rsplit("}", 1)[-1]: (child.text or "").strip() for child in node} | |
| if record.get("band_number") == "125": | |
| wanted = float(record["center_wavelength"]) | |
| break | |
| if wanted is None or not math.isclose(wanted, 2802.0, abs_tol=0.1): | |
| raise AssertionError(f"Unexpected band 125 wavelength in label: {wanted}") | |
| peak_mb = _peak_working_set_mb() | |
| if peak_mb >= 500.0: | |
| raise AssertionError(f"Peak working set exceeded target: {peak_mb:.1f} MiB") | |
| return f"band={band}, wavelength=2.802µm, preview={values.shape}, source={width}x{height}, peak process WS={peak_mb:.1f}MiB" | |
| def check_registration_outputs() -> str: | |
| import cv2 | |
| reg = module_from_script("qa_register_images_outputs", "scripts/register_images.py") | |
| details = [] | |
| for sensor, preset in reg.BENCHMARK_PRESETS.items(): | |
| path = ROOT / preset["output"] | |
| if not path.is_file() or path.stat().st_size == 0: | |
| raise AssertionError(f"Missing or empty output: {path}") | |
| image = cv2.imread(str(path), cv2.IMREAD_UNCHANGED) | |
| if image is None or tuple(image.shape[:2]) != tuple(preset["shape_hw"]): | |
| raise AssertionError(f"Unexpected output shape for {sensor}: {None if image is None else image.shape}") | |
| details.append(f"{sensor}={image.shape[1]}x{image.shape[0]}") | |
| return ", ".join(details) | |
| def check_full_registration_run() -> str: | |
| result = subprocess.run( | |
| [sys.executable, str(ROOT / "scripts" / "register_images.py")], | |
| cwd=ROOT, text=True, capture_output=True, timeout=240, | |
| ) | |
| output = result.stdout + result.stderr | |
| if result.returncode: | |
| raise AssertionError(f"register_images.py exited {result.returncode}:\n{output[-5000:]}") | |
| lines = [line.strip() for line in output.splitlines() if "RANSAC inliers" in line or "Registration complete" in line] | |
| return "; ".join(lines[-4:]) | |
| def _validate_live_result(result, case: str) -> str: | |
| import numpy as np | |
| import requests | |
| from PIL import Image | |
| if not isinstance(result, (list, tuple)) or len(result) != 7: | |
| raise AssertionError(f"{case}: expected 7 output values, got {type(result).__name__}/{len(result) if hasattr(result, '__len__') else '?'}") | |
| image, report, csv_file, json_file, geotiff_file, png_file, dossier = result | |
| if not isinstance(report, str) or not report.strip(): | |
| raise AssertionError(f"{case}: missing report text") | |
| def payload_bytes(item): | |
| if isinstance(item, Image.Image): | |
| import io | |
| buff = io.BytesIO() | |
| item.save(buff, format="PNG") | |
| return buff.getvalue() | |
| if isinstance(item, np.ndarray): | |
| import cv2 | |
| success, encoded = cv2.imencode(".png", item) | |
| return encoded.tobytes() if success else None | |
| if isinstance(item, dict): | |
| item = item.get("url") or item.get("path") | |
| if not item: | |
| return None | |
| value = str(item) | |
| candidate = Path(value) | |
| if candidate.is_file(): | |
| return candidate.read_bytes() | |
| if value.startswith(("http://", "https://")): | |
| response = requests.get(value, timeout=30) | |
| response.raise_for_status() | |
| return response.content | |
| return None | |
| image_bytes = payload_bytes(image) | |
| if "REGISTRATION COMPLETE" in report: | |
| # Gradio may transcode the Image preview to WebP. The dedicated PNG | |
| # DownloadButton is the stable PNG artifact contract for /predict. | |
| png_bytes = payload_bytes(png_file) | |
| if not image_bytes: | |
| raise AssertionError(f"{case}: successful registration did not return an image preview") | |
| if not png_bytes or not png_bytes.startswith(b"\x89PNG\r\n\x1a\n"): | |
| raise AssertionError(f"{case}: successful registration did not return a valid PNG download") | |
| json_bytes = payload_bytes(json_file) | |
| if not json_bytes: | |
| raise AssertionError(f"{case}: successful registration did not return transform JSON") | |
| try: | |
| metadata = json.loads(json_bytes.decode("utf-8")) | |
| except (UnicodeDecodeError, json.JSONDecodeError) as exc: | |
| raise AssertionError(f"{case}: transform JSON is invalid: {exc}") from exc | |
| if not isinstance(metadata, dict): | |
| raise AssertionError(f"{case}: transform metadata is not an object") | |
| inlier_count = int(metadata.get("inlier_count", 0)) | |
| model = metadata.get("transform_model") | |
| affine = metadata.get("partial_affine_matrix_2x3") | |
| if model != "partial_affine_4dof" or inlier_count < 8: | |
| raise AssertionError(f"{case}: invalid transform contract model={model!r}, inliers={inlier_count}") | |
| if not isinstance(affine, list) or len(affine) != 2 or any(not isinstance(row, list) or len(row) != 3 for row in affine): | |
| raise AssertionError(f"{case}: missing 2x3 Partial Affine matrix") | |
| return f"success, PNG={len(png_bytes)} bytes, model={model}, inliers={inlier_count}" | |
| if image is not None or any((csv_file, json_file, geotiff_file, png_file, dossier)): | |
| raise AssertionError(f"{case}: failure response leaked partial output artifacts") | |
| if not any(word in report.lower() for word in ("failed", "insufficient", "error")): | |
| raise AssertionError(f"{case}: unexpected safe-failure report: {report[:180]}") | |
| return f"safe rejection, report={report.splitlines()[0][:100]}" | |
| def check_live_space() -> str: | |
| from gradio_client import Client, handle_file | |
| import cv2 | |
| import numpy as np | |
| client = Client("revanshriyan/chandra-align", verbose=False) | |
| available = client.view_api(return_format="dict") | |
| if not available: | |
| raise AssertionError("Hugging Face Space published no Gradio API schema") | |
| temp = tempfile.TemporaryDirectory(prefix="chandra_live_qa_") | |
| tmp = Path(temp.name) | |
| # Hosted requests use seeded synthetic imagery only. Real local ISRO/NASA | |
| # rasters are tested offline; public uploads require separate authorization. | |
| rng = np.random.default_rng(26166) | |
| synthetic = rng.normal(112.0, 24.0, (640, 640)).clip(0, 255).astype(np.uint8) | |
| for _ in range(90): | |
| x, y = rng.integers(24, 616, size=2) | |
| radius = int(rng.integers(6, 30)) | |
| cv2.circle(synthetic, (int(x), int(y)), radius, int(rng.integers(25, 235)), 2) | |
| cv2.circle(synthetic, (int(x), int(y)), max(2, radius // 2), int(rng.integers(25, 235)), 1) | |
| affine = cv2.getRotationMatrix2D((320, 320), 2.5, 1.01) | |
| affine[:, 2] += (6.0, -4.0) | |
| transformed = cv2.warpAffine(synthetic, affine, (640, 640), borderMode=cv2.BORDER_REFLECT_101) | |
| ref_path, sec_path, identical_path = tmp / "synthetic_ref.png", tmp / "synthetic_sec.png", tmp / "synthetic_identical.png" | |
| cv2.imwrite(str(ref_path), synthetic) | |
| cv2.imwrite(str(sec_path), transformed) | |
| cv2.imwrite(str(identical_path), synthetic) | |
| modality_modes = [ | |
| ("OHRC", "OHRC", "Optical <-> Optical", 0.25), | |
| ("TMC-2", "TMC-2", "Optical <-> Optical", 5.0), | |
| ("IIRS", "IIRS", "Optical <-> Infrared", 80.0), | |
| ] | |
| observations = [] | |
| for sensor, secondary, pair_mode, scale in modality_modes: | |
| args = ( | |
| handle_file(str(identical_path)), handle_file(str(identical_path)), sensor, secondary, | |
| # Use identical synthetic pixel grids and disable scene-dependent | |
| # shadow masks; preprocessing and scale factors have dedicated | |
| # deterministic local checks above. | |
| pair_mode, False, scale, False, 3.0, False, False, 128.0, 50.0, | |
| ) | |
| try: | |
| result = client.submit(*args, api_name="/predict").result(timeout=180) | |
| contract = _validate_live_result(result, sensor) | |
| if not contract.startswith("success"): | |
| raise AssertionError(f"{sensor}: identical-image sensor probe was rejected: {contract}") | |
| observations.append(f"{sensor} mode: {contract}") | |
| except Exception as exc: | |
| observations.append(f"{sensor} mode: request exception {type(exc).__name__}: {exc}") | |
| blank_path = tmp / "blank.png" | |
| cv2.imwrite(str(blank_path), np.zeros((256, 256), dtype=np.uint8)) | |
| try: | |
| result = client.submit( | |
| handle_file(str(blank_path)), handle_file(str(blank_path)), | |
| "OHRC", "OHRC", "Optical <-> Optical", False, | |
| 0.25, False, 3.0, False, False, 128.0, 50.0, | |
| api_name="/predict", | |
| ).result(timeout=180) | |
| observations.append(f"featureless pair: {_validate_live_result(result, 'blank pair')}") | |
| except Exception as exc: | |
| observations.append(f"featureless pair: request exception {type(exc).__name__}: {exc}") | |
| temp.cleanup() | |
| failed = [value for value in observations if "exception" in value] | |
| if failed: | |
| raise AssertionError("; ".join(observations)) | |
| return "; ".join(observations) | |
| def print_results() -> None: | |
| headers = ("MODULE NAME", "TARGET / SENSOR", "STATUS", "OBSERVED METRIC") | |
| rows = [headers, *CHECKS] | |
| widths = [max(len(str(row[i])) for row in rows) for i in range(4)] | |
| divider = "+-" + "-+-".join("-" * width for width in widths) + "-+" | |
| print("\n" + divider) | |
| for row_index, row in enumerate(rows): | |
| print("| " + " | ".join(str(value).ljust(widths[index]) for index, value in enumerate(row)) + " |") | |
| if row_index == 0: | |
| print(divider) | |
| print(divider) | |
| def main(argv: list[str] | None = None) -> int: | |
| if hasattr(sys.stdout, "reconfigure"): | |
| sys.stdout.reconfigure(errors="backslashreplace") | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--live", action="store_true", help="Call the public Hugging Face Space sequentially") | |
| parser.add_argument("--skip-registration-run", action="store_true", help="Verify outputs without rerunning all real registrations") | |
| args = parser.parse_args(argv) | |
| run_check("ODE ingestion", "Polar south-footprint", check_polar_ode) | |
| run_check("ODE ingestion", "data/raw/nac staging", check_raw_staging) | |
| run_check("Registration", "OHRC/TMC-2/IIRS GSD pyramid", check_gsd_factors) | |
| run_check("Registration", "Percentile + CLAHE + OHRC Sobel", check_preprocessing) | |
| run_check("Preprocessing API", "IIRS contrast controls / minimum match extent", check_iirs_and_multimodal_signatures) | |
| run_check("Registration", "Partial Affine stability / 8-inlier gate", check_affine_and_gate) | |
| run_check("App transform", "/predict 4-DOF matrix / 8-inlier contract", check_app_partial_affine_contract) | |
| run_check("Matcher", "Vendored RIFT2 runtime dependency", check_rift2_runtime) | |
| run_check("Registration", "IIRS band 125 / peak working set", check_iirs_band_and_memory) | |
| if args.skip_registration_run: | |
| skip_check("Registration", "Full real-data run", "disabled by --skip-registration-run") | |
| else: | |
| run_check("Registration", "OHRC/TMC-2/IIRS full pipeline", check_full_registration_run) | |
| run_check("Registration", "Processed PNG outputs", check_registration_outputs) | |
| if args.live: | |
| run_check("Hugging Face Space", "3 modality requests + featureless pair", check_live_space) | |
| else: | |
| skip_check("Hugging Face Space", "Live endpoint", "pass --live to make hosted requests") | |
| print_results() | |
| failures = sum(row[2] == "FAIL" for row in CHECKS) | |
| skips = sum(row[2] == "SKIP" for row in CHECKS) | |
| print(f"\nChecks: {len(CHECKS) - skips - failures} passed, {failures} failed, {skips} skipped.") | |
| return 1 if failures else 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |