.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/02_resize/06_benchmarking.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via Binder. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_02_resize_06_benchmarking.py: Benchmarking ============ This example benchmarks several 2D downsampling methods over a *set* of test images. For each image we: 1. Downsample by an image-specific zoom factor, then upsample back (round-trip). 2. Measure the runtime of the round-trip (forward + backward). 3. Compute SNR / MSE / SSIM on a local ROI. 4. Visualise the results with ROI-aware zooms: first a 2x2 figure showing the original image with its magnified ROI and the SplineOps Antialiasing first-pass image with the mapped ROI, then ROI montages of the original ROI and each method's first-pass ROI, all magnified with nearest-neighbour. We compare: - SciPy ndimage.zoom (cubic). - SplineOps Standard cubic interpolation. - SplineOps Cubic Antialiasing (oblique projection). - OpenCV INTER_CUBIC. - Pillow BICUBIC. - scikit-image (resize, cubic). - scikit-image (resize, cubic + anti_aliasing). - PyTorch (F.interpolate bicubic, CPU). - PyTorch (F.interpolate bicubic, antialias=True, CPU). Notes ----- - All ops run on grayscale images normalized to [0, 1] for metrics. - Methods with missing deps are marked "unavailable" in the console and skipped from the ROI montages. .. GENERATED FROM PYTHON SOURCE LINES 40-42 Imports and Configuration ------------------------- .. GENERATED FROM PYTHON SOURCE LINES 42-140 .. code-block:: Python from __future__ import annotations import math import os import sys import time from typing import Dict, List, Tuple, Optional import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as patches from urllib.request import urlopen from PIL import Image # Default storage dtype for comparison (change to np.float64 if desired) DTYPE = np.float32 # ROI / detail-window configuration ROI_MAG_TARGET = 256 # target height for nearest-neighbour zoom tiles ROI_TILE_TITLE_FONTSIZE = 12 ROI_SUPTITLE_FONTSIZE = 14 # currently unused, kept for consistency # Plot appearance for slide-friendly export PLOT_FIGSIZE = (14, 7) # same 2:1 ratio as (10, 5), just larger PLOT_TITLE_FONTSIZE = 18 PLOT_LABEL_FONTSIZE = 18 PLOT_TICK_FONTSIZE = 18 PLOT_LEGEND_FONTSIZE = 18 # Highlight styles (per method) used everywhere (ROI montages + plots) HIGHLIGHT_STYLE = { "SplineOps Standard cubic": { "color": "#C2410C", "lw": 3.0, }, "SplineOps Antialiasing cubic": { "color": "#BE185D", "lw": 3.0, }, } AA_METHOD_LABEL = "SplineOps Antialiasing cubic" AA_COLOR = HIGHLIGHT_STYLE[AA_METHOD_LABEL]["color"] def fmt_ms(seconds: float) -> str: """Format seconds as a short 'X.X ms' string.""" return f"{seconds * 1000.0:.1f} ms" # Benchmark configuration N_TRIALS = 10 # Optional deps try: import cv2 _HAS_CV2 = True # Undo OpenCV's Qt plugin path override to keep using the system/PyQt plugins os.environ.pop("QT_QPA_PLATFORM_PLUGIN_PATH", None) except Exception: _HAS_CV2 = False try: from scipy.ndimage import zoom as _ndi_zoom _HAS_SCIPY = True except Exception: _HAS_SCIPY = False try: from skimage.transform import resize as _sk_resize from skimage.metrics import structural_similarity as _ssim # noqa: F401 _HAS_SKIMAGE = True except Exception: _HAS_SKIMAGE = False _ssim = None try: import torch import torch.nn.functional as F _HAS_TORCH = True except Exception: _HAS_TORCH = False # SplineOps try: from splineops.resize import resize as sp_resize _HAS_SPLINEOPS = True except Exception as e: _HAS_SPLINEOPS = False _SPLINEOPS_IMPORT_ERR = str(e) try: from splineops.utils.specs import print_runtime_context _HAS_SPECS = True except Exception: print_runtime_context = None _HAS_SPECS = False .. GENERATED FROM PYTHON SOURCE LINES 142-144 Kodak Test Set Configuration ---------------------------- .. GENERATED FROM PYTHON SOURCE LINES 144-229 .. code-block:: Python KODAK_BASE = "https://r0k.us/graphics/kodak/kodak" KODAK_IMAGES = [ ("kodim05", f"{KODAK_BASE}/kodim05.png"), ("kodim07", f"{KODAK_BASE}/kodim07.png"), ("kodim14", f"{KODAK_BASE}/kodim14.png"), ("kodim15", f"{KODAK_BASE}/kodim15.png"), ("kodim19", f"{KODAK_BASE}/kodim19.png"), ("kodim22", f"{KODAK_BASE}/kodim22.png"), ("kodim23", f"{KODAK_BASE}/kodim23.png"), ] # Per-image zoom + ROI config IMAGE_CONFIG: Dict[str, Dict[str, object]] = { "kodim05": dict( zoom=0.15, roi_size_px=256, roi_center_frac=(0.75, 0.5), ), "kodim07": dict( zoom=0.15, roi_size_px=256, roi_center_frac=(0.40, 0.50), ), "kodim14": dict( zoom=0.3, roi_size_px=256, roi_center_frac=(0.75, 0.75), ), "kodim15": dict( zoom=0.3, roi_size_px=256, roi_center_frac=(0.30, 0.55), ), "kodim19": dict( zoom=0.2, roi_size_px=256, roi_center_frac=(0.65, 0.35), ), "kodim22": dict( zoom=0.2, roi_size_px=256, roi_center_frac=(0.50, 0.25), ), "kodim23": dict( zoom=0.15, roi_size_px=256, roi_center_frac=(0.40, 0.65), ), } def _load_kodak_gray(url: str) -> np.ndarray: """ Download a Kodak image, convert to grayscale [0, 1] in DTYPE (float32). """ with urlopen(url, timeout=10) as resp: img = Image.open(resp) arr = np.asarray(img, dtype=np.float64) if arr.ndim == 3 and arr.shape[2] >= 3: arr01 = arr / 255.0 gray = ( 0.2989 * arr01[..., 0] + 0.5870 * arr01[..., 1] + 0.1140 * arr01[..., 2] ) else: vmax = float(arr.max()) or 1.0 gray = arr / vmax return np.clip(gray, 0.0, 1.0).astype(DTYPE) def _load_kodak_rgb(url: str) -> np.ndarray: """ Download a Kodak image as RGB [0, 1] in DTYPE (float32). Used only for color visualizations; metrics remain on grayscale. """ with urlopen(url, timeout=10) as resp: img = Image.open(resp).convert("RGB") arr = np.asarray(img, dtype=np.float64) / 255.0 # H×W×3 in [0,1] return np.clip(arr, 0.0, 1.0).astype(DTYPE, copy=False) .. GENERATED FROM PYTHON SOURCE LINES 230-232 Utilities (Metrics, ROI, Plotting) ---------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 232-631 .. code-block:: Python def _snr_db(x: np.ndarray, y: np.ndarray) -> float: num = float(np.sum(x * x, dtype=np.float64)) den = float(np.sum((x - y) ** 2, dtype=np.float64)) if den == 0.0: return float("inf") if num == 0.0: return -float("inf") return 10.0 * math.log10(num / den) def _roi_rect_from_frac( shape: Tuple[int, int], roi_size_px: int, center_frac: Tuple[float, float], ) -> Tuple[int, int, int, int]: """Compute a square ROI inside an image, centred at fractional coordinates.""" H, W = shape[:2] row_frac, col_frac = center_frac size = int(min(roi_size_px, H, W)) if size < 1: size = min(H, W) center_r = int(round(row_frac * H)) center_c = int(round(col_frac * W)) row_top = int(np.clip(center_r - size // 2, 0, H - size)) col_left = int(np.clip(center_c - size // 2, 0, W - size)) return row_top, col_left, size, size def _crop_roi(arr: np.ndarray, rect: Tuple[int, int, int, int]) -> np.ndarray: r0, c0, h, w = rect return arr[r0 : r0 + h, c0 : c0 + w] def _nearest_big(roi: np.ndarray, target_h: int = ROI_MAG_TARGET) -> np.ndarray: """Enlarge a small ROI with nearest-neighbour so its height is ~target_h.""" h, w = roi.shape mag = max(1, int(round(target_h / max(h, 1)))) return np.repeat(np.repeat(roi, mag, axis=0), mag, axis=1) def _diff_normalized( orig: np.ndarray, rec: np.ndarray, *, max_abs: Optional[float] = None, ) -> np.ndarray: """ Normalize signed difference (rec - orig) into [0,1] for display. 0.5 = no difference, >0.5 positive, <0.5 negative. If max_abs is provided, it is used as a shared scale (same range across tiles). """ diff = rec.astype(np.float64, copy=False) - orig.astype(np.float64, copy=False) if max_abs is None: max_abs = float(np.max(np.abs(diff))) else: max_abs = float(max_abs) if max_abs <= 0.0: return 0.5 * np.ones_like(diff, dtype=DTYPE) norm = 0.5 + 0.5 * diff / max_abs norm = np.clip(norm, 0.0, 1.0) return norm.astype(DTYPE, copy=False) def _show_initial_original_vs_aa( gray: np.ndarray, roi_rect: Tuple[int, int, int, int], aa_first: np.ndarray, z: float, degree_label: str, ) -> None: """ Plot a 2x2 figure: Row 1: - Original image with red ROI box - Magnified original ROI Row 2: - SplineOps Antialiasing first-pass resized image on white canvas with mapped ROI box - Magnified mapped ROI from the Antialiasing first-pass """ H, W = gray.shape row0, col0, roi_h, roi_w = roi_rect roi_orig = _crop_roi(gray, roi_rect) roi_orig_big = _nearest_big(roi_orig, ROI_MAG_TARGET) H1, W1 = aa_first.shape center_r = row0 + roi_h / 2.0 center_c = col0 + roi_w / 2.0 roi_h_res = max(1, int(round(roi_h * z))) roi_w_res = max(1, int(round(roi_w * z))) if roi_h_res > H1 or roi_w_res > W1: aa_roi = aa_first row_top_res = 0 col_left_res = 0 roi_h_res = H1 roi_w_res = W1 else: center_r_res = int(round(center_r * z)) center_c_res = int(round(center_c * z)) row_top_res = int(np.clip(center_r_res - roi_h_res // 2, 0, H1 - roi_h_res)) col_left_res = int(np.clip(center_c_res - roi_w_res // 2, 0, W1 - roi_w_res)) aa_roi = aa_first[ row_top_res : row_top_res + roi_h_res, col_left_res : col_left_res + roi_w_res, ] aa_roi_big = _nearest_big(aa_roi, ROI_MAG_TARGET) canvas_aa = np.ones_like(gray, dtype=aa_first.dtype) h_copy = min(H, H1) w_copy = min(W, W1) canvas_aa[:h_copy, :w_copy] = aa_first[:h_copy, :w_copy] fig, axes = plt.subplots(2, 2, figsize=(10, 8)) # Row 1, left: original with ROI box ax = axes[0, 0] ax.imshow(gray, cmap="gray", interpolation="nearest", aspect="equal") rect = patches.Rectangle( (col0, row0), roi_w, roi_h, linewidth=2, edgecolor="red", facecolor="none", ) ax.add_patch(rect) ax.set_title( f"Original image with ROI ({H}×{W} px)", fontsize=ROI_TILE_TITLE_FONTSIZE, ) ax.axis("off") # Row 1, right: magnified original ROI ax = axes[0, 1] ax.imshow(roi_orig_big, cmap="gray", interpolation="nearest", aspect="equal") ax.set_title( f"Original ROI ({roi_h}×{roi_w} px, NN magnified)", fontsize=ROI_TILE_TITLE_FONTSIZE, ) ax.axis("off") # Row 2, left: Antialiasing first-pass on canvas with mapped ROI box ax = axes[1, 0] ax.imshow(canvas_aa, cmap="gray", interpolation="nearest", aspect="equal") if row_top_res < h_copy and col_left_res < w_copy: box_h = min(roi_h_res, h_copy - row_top_res) box_w = min(roi_w_res, w_copy - col_left_res) rect_aa = patches.Rectangle( (col_left_res, row_top_res), box_w, box_h, linewidth=2, edgecolor="red", facecolor="none", ) ax.add_patch(rect_aa) ax.set_title( f"{AA_METHOD_LABEL}\n(zoom ×{z:g}, {H1}×{W1} px)", fontsize=ROI_TILE_TITLE_FONTSIZE, color=AA_COLOR, fontweight="bold", multialignment="center", ) ax.axis("off") # Row 2, right: magnified Antialiasing ROI ax = axes[1, 1] ax.imshow(aa_roi_big, cmap="gray", interpolation="nearest", aspect="equal") ax.set_title( f"Antialiasing ROI ({roi_h_res}×{roi_w_res} px, NN magnified)", fontsize=ROI_TILE_TITLE_FONTSIZE, ) ax.axis("off") fig.tight_layout() plt.show() def _nearest_big_color(roi: np.ndarray, target_h: int = ROI_MAG_TARGET) -> np.ndarray: """ Enlarge a small color ROI (H×W×3) with nearest-neighbour so its height is ~target_h pixels. """ h, w, _ = roi.shape mag = max(1, int(round(target_h / max(h, 1)))) return np.repeat(np.repeat(roi, mag, axis=0), mag, axis=1) def show_intro_color( original_rgb: np.ndarray, shrunk_rgb: np.ndarray, roi_rect: Tuple[int, int, int, int], zoom: float, label: str, degree_label: str, ) -> None: """ 2×2 figure mirroring the benchmarking intro style, but in color. """ H, W, _ = original_rgb.shape row0, col0, roi_h, roi_w = roi_rect roi_orig = original_rgb[row0:row0 + roi_h, col0:col0 + roi_w, :] roi_orig_big = _nearest_big_color(roi_orig, target_h=ROI_MAG_TARGET) Hs, Ws, _ = shrunk_rgb.shape center_r = row0 + roi_h / 2.0 center_c = col0 + roi_w / 2.0 roi_h_res = max(1, int(round(roi_h * zoom))) roi_w_res = max(1, int(round(roi_w * zoom))) if roi_h_res > Hs or roi_w_res > Ws: roi_shrunk = shrunk_rgb row_top_res = 0 col_left_res = 0 roi_h_res = Hs roi_w_res = Ws else: center_r_res = int(round(center_r * zoom)) center_c_res = int(round(center_c * zoom)) row_top_res = int(np.clip(center_r_res - roi_h_res // 2, 0, Hs - roi_h_res)) col_left_res = int(np.clip(center_c_res - roi_w_res // 2, 0, Ws - roi_w_res)) roi_shrunk = shrunk_rgb[ row_top_res:row_top_res + roi_h_res, col_left_res:col_left_res + roi_w_res, : ] roi_shrunk_big = _nearest_big_color(roi_shrunk, target_h=ROI_MAG_TARGET) canvas = np.ones_like(original_rgb) h_copy = min(H, Hs) w_copy = min(W, Ws) canvas[:h_copy, :w_copy, :] = shrunk_rgb[:h_copy, :w_copy, :] fig, axes = plt.subplots(2, 2, figsize=(10, 8)) ax = axes[0, 0] ax.imshow(np.clip(original_rgb, 0.0, 1.0)) rect = patches.Rectangle( (col0, row0), roi_w, roi_h, linewidth=2, edgecolor="red", facecolor="none", ) ax.add_patch(rect) ax.set_title( f"Original image with ROI ({H}×{W} px)", fontsize=ROI_TILE_TITLE_FONTSIZE, ) ax.axis("off") ax = axes[0, 1] ax.imshow(np.clip(roi_orig_big, 0.0, 1.0)) ax.set_title( f"Original ROI ({roi_h}×{roi_w} px, NN magnified)", fontsize=ROI_TILE_TITLE_FONTSIZE, ) ax.axis("off") ax = axes[1, 0] ax.imshow(np.clip(canvas, 0.0, 1.0)) if row_top_res < h_copy and col_left_res < w_copy: box_h = min(roi_h_res, h_copy - row_top_res) box_w = min(roi_w_res, w_copy - col_left_res) rect2 = patches.Rectangle( (col_left_res, row_top_res), box_w, box_h, linewidth=2, edgecolor="red", facecolor="none", ) ax.add_patch(rect2) # Bottom-left: resized image on canvas (change title only here) if label == "Antialiasing": title_kw = dict( fontsize=ROI_TILE_TITLE_FONTSIZE, fontweight="bold", color=AA_COLOR, multialignment="center", ) ax.set_title( f"{AA_METHOD_LABEL}\n(zoom ×{zoom:g}, {Hs}×{Ws} px)", **title_kw, ) ax.axis("off") ax = axes[1, 1] ax.imshow(np.clip(roi_shrunk_big, 0.0, 1.0)) if label == "Antialiasing": title_kw = dict(fontsize=ROI_TILE_TITLE_FONTSIZE, fontweight="bold") if AA_COLOR is not None: title_kw["color"] = AA_COLOR ax.set_title( f"{label} ROI ({roi_h_res}×{roi_w_res} px, NN magnified)", **title_kw, ) else: ax.set_title( f"{label} ROI ({roi_h_res}×{roi_w_res} px, NN magnified)", fontsize=ROI_TILE_TITLE_FONTSIZE, ) ax.axis("off") fig.tight_layout() plt.show() def _smart_ylim( values: np.ndarray, *, hi_cap: float | None = None, lo_cap: float | None = None, pad_frac: float = 0.06, iqr_k: float = 1.5, q_floor: float = 10.0, # percentile used when min is an outlier min_span: float | None = None, ) -> tuple[float, float] | None: """ Robust y-limits for plots. - If the minimum is a strong outlier (below Q1 - k*IQR), use q_floor percentile as the lower bound. - Otherwise use the true min. - Add a small padding. - Optionally clamp/cap. """ v = np.asarray(values, dtype=np.float64) v = v[np.isfinite(v)] if v.size == 0: return None vmin = float(v.min()) vmax = float(v.max()) if vmin == vmax: span = float(min_span) if min_span is not None else (1e-3 if vmax <= 1.0 else 1.0) lo, hi = vmin - 0.5 * span, vmax + 0.5 * span else: q1, q3 = np.percentile(v, [25.0, 75.0]) iqr = float(q3 - q1) lo0 = vmin if iqr > 0.0: low_outlier_thr = float(q1 - iqr_k * iqr) if vmin < low_outlier_thr: lo0 = float(np.percentile(v, q_floor)) span = vmax - lo0 pad = pad_frac * span lo, hi = lo0 - pad, vmax + pad if min_span is not None and (hi - lo) < float(min_span): mid = 0.5 * (hi + lo) lo = mid - 0.5 * float(min_span) hi = mid + 0.5 * float(min_span) if lo_cap is not None: lo = max(lo, float(lo_cap)) if hi_cap is not None: hi = min(hi, float(hi_cap)) if lo >= hi: hi = lo + (float(min_span) if min_span is not None else 1e-6) return lo, hi def _highlight_tile(ax, *, color: str, lw: float = 3.0) -> None: # Full-axes border, works even with ax.axis("off") rect = patches.Rectangle( (0, 0), 1, 1, transform=ax.transAxes, fill=False, edgecolor=color, linewidth=lw, clip_on=False, ) ax.add_patch(rect) .. GENERATED FROM PYTHON SOURCE LINES 632-634 Round-Trip Backends and Time ---------------------------- .. GENERATED FROM PYTHON SOURCE LINES 634-982 .. code-block:: Python def _rt_splineops( gray: np.ndarray, z: float, preset: str ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """SplineOps standard / antialiasing cubic.""" if not _HAS_SPLINEOPS: return gray, gray, f"SplineOps unavailable: {_SPLINEOPS_IMPORT_ERR}" try: first = sp_resize(gray, zoom_factors=(z, z), method=preset) rec = sp_resize(first, output_size=gray.shape, method=preset) first = np.clip(first, 0.0, 1.0).astype(gray.dtype, copy=False) rec = np.clip(rec, 0.0, 1.0).astype(gray.dtype, copy=False) return first, rec, None except Exception as e: return gray, gray, str(e) def _rt_scipy( gray: np.ndarray, z: float ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """SciPy ndimage.zoom, cubic, reflect boundary.""" if not _HAS_SCIPY: return gray, gray, "SciPy not installed" try: order = 3 need_prefilter = True first = _ndi_zoom( gray, (z, z), order=order, prefilter=need_prefilter, mode="reflect", grid_mode=False, ) Hz, Wz = first.shape back = (gray.shape[0] / Hz, gray.shape[1] / Wz) rec = _ndi_zoom( first, back, order=order, prefilter=need_prefilter, mode="reflect", grid_mode=False, ) first = np.clip(first, 0.0, 1.0) rec = np.clip(rec, 0.0, 1.0) if rec.shape != gray.shape: h = min(rec.shape[0], gray.shape[0]) w = min(rec.shape[1], gray.shape[1]) r0 = (rec.shape[0] - h) // 2 r1 = r0 + h c0 = (rec.shape[1] - w) // 2 c1 = c0 + w rc = rec[r0:r1, c0:c1] g0 = (gray.shape[0] - h) // 2 g1 = g0 + h g2 = (gray.shape[1] - w) // 2 g3 = g2 + w tmp = np.zeros_like(gray) tmp[g0:g1, g2:g3] = rc rec = tmp return first.astype(gray.dtype, copy=False), rec.astype(gray.dtype, copy=False), None except Exception as e: return gray, gray, str(e) def _rt_opencv( gray: np.ndarray, z: float ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """OpenCV INTER_CUBIC.""" if not _HAS_CV2: return gray, gray, "OpenCV not installed" try: H, W = gray.shape H1 = int(round(H * z)) W1 = int(round(W * z)) first = cv2.resize(gray, (W1, H1), interpolation=cv2.INTER_CUBIC) rec = cv2.resize(first, (W, H), interpolation=cv2.INTER_CUBIC) first = np.clip(first, 0.0, 1.0).astype(gray.dtype, copy=False) rec = np.clip(rec, 0.0, 1.0).astype(gray.dtype, copy=False) return first, rec, None except Exception as e: return gray, gray, str(e) def _rt_pillow( gray: np.ndarray, z: float ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """Pillow BICUBIC on float32 images.""" try: from PIL import Image as _Image H, W = gray.shape H1 = int(round(H * z)) W1 = int(round(W * z)) im = _Image.fromarray(gray.astype(np.float32, copy=False), mode="F") first_im = im.resize((W1, H1), resample=_Image.Resampling.BICUBIC) rec_im = first_im.resize((W, H), resample=_Image.Resampling.BICUBIC) first = np.asarray(first_im, dtype=np.float32) rec = np.asarray(rec_im, dtype=np.float32) first = np.clip(first, 0.0, 1.0).astype(gray.dtype, copy=False) rec = np.clip(rec, 0.0, 1.0).astype(gray.dtype, copy=False) return first, rec, None except Exception as e: return gray, gray, str(e) def _rt_skimage( gray: np.ndarray, z: float ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """scikit-image resize, cubic, anti_aliasing=False.""" if not _HAS_SKIMAGE: return gray, gray, "scikit-image not installed" try: H, W = gray.shape H1 = int(round(H * z)) W1 = int(round(W * z)) first = _sk_resize( gray, (H1, W1), order=3, anti_aliasing=False, preserve_range=True, mode="reflect", ).astype(np.float64) rec = _sk_resize( first, (H, W), order=3, anti_aliasing=False, preserve_range=True, mode="reflect", ).astype(np.float64) first = np.clip(first, 0.0, 1.0).astype(gray.dtype, copy=False) rec = np.clip(rec, 0.0, 1.0).astype(gray.dtype, copy=False) return first, rec, None except Exception as e: return gray, gray, str(e) def _rt_skimage_aa( gray: np.ndarray, z: float ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """scikit-image resize, cubic, anti_aliasing=True on shrink.""" if not _HAS_SKIMAGE: return gray, gray, "scikit-image not installed" try: H, W = gray.shape H1 = int(round(H * z)) W1 = int(round(W * z)) first = _sk_resize( gray, (H1, W1), order=3, anti_aliasing=True, preserve_range=True, mode="reflect", ).astype(np.float64) rec = _sk_resize( first, (H, W), order=3, anti_aliasing=False, preserve_range=True, mode="reflect", ).astype(np.float64) first = np.clip(first, 0.0, 1.0).astype(gray.dtype, copy=False) rec = np.clip(rec, 0.0, 1.0).astype(gray.dtype, copy=False) return first, rec, None except Exception as e: return gray, gray, str(e) def _rt_torch( gray: np.ndarray, z: float ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """PyTorch F.interpolate bicubic (antialias=False).""" if not _HAS_TORCH: return gray, gray, "PyTorch not installed" try: arr = gray if arr.dtype == np.float32: t_dtype = torch.float32 elif arr.dtype == np.float64: t_dtype = torch.float64 else: t_dtype = torch.float32 arr = arr.astype(np.float32, copy=False) H, W = arr.shape H1 = int(round(H * z)) W1 = int(round(W * z)) x = torch.from_numpy(arr).to(t_dtype).unsqueeze(0).unsqueeze(0) first_t = F.interpolate( x, size=(H1, W1), mode="bicubic", align_corners=False, antialias=False, ) rec_t = F.interpolate( first_t, size=(H, W), mode="bicubic", align_corners=False, antialias=False, ) first = first_t[0, 0].detach().cpu().numpy() rec = rec_t[0, 0].detach().cpu().numpy() first = np.clip(first, 0.0, 1.0).astype(gray.dtype, copy=False) rec = np.clip(rec, 0.0, 1.0).astype(gray.dtype, copy=False) return first, rec, None except Exception as e: return gray, gray, str(e) def _rt_torch_aa( gray: np.ndarray, z: float ) -> Tuple[np.ndarray, np.ndarray, Optional[str]]: """PyTorch F.interpolate bicubic (antialias=True).""" if not _HAS_TORCH: return gray, gray, "PyTorch not installed" try: arr = gray if arr.dtype == np.float32: t_dtype = torch.float32 elif arr.dtype == np.float64: t_dtype = torch.float64 else: t_dtype = torch.float32 arr = arr.astype(np.float32, copy=False) H, W = arr.shape H1 = int(round(H * z)) W1 = int(round(W * z)) x = torch.from_numpy(arr).to(t_dtype).unsqueeze(0).unsqueeze(0) first_t = F.interpolate( x, size=(H1, W1), mode="bicubic", align_corners=False, antialias=True, ) rec_t = F.interpolate( first_t, size=(H, W), mode="bicubic", align_corners=False, antialias=True, ) first = first_t[0, 0].detach().cpu().numpy() rec = rec_t[0, 0].detach().cpu().numpy() first = np.clip(first, 0.0, 1.0).astype(gray.dtype, copy=False) rec = np.clip(rec, 0.0, 1.0).astype(gray.dtype, copy=False) return first, rec, None except Exception as e: return gray, gray, str(e) def _avg_time(rt_fn, repeats: int = N_TRIALS, warmup: bool = True): """ Run `rt_fn()` (which must return (first, rec, err)) `repeats` times. Returns (last_first, last_rec, mean_time, std_time, err). """ if warmup: try: first, rec, err = rt_fn() if err is not None: return np.array([]), np.array([]), float("nan"), float("nan"), err except Exception as e: return np.array([]), np.array([]), float("nan"), float("nan"), str(e) times: List[float] = [] last_first: Optional[np.ndarray] = None last_rec: Optional[np.ndarray] = None for _ in range(max(1, repeats)): t0 = time.perf_counter() first, rec, err = rt_fn() if err is not None: return np.array([]), np.array([]), float("nan"), float("nan"), err dt = time.perf_counter() - t0 times.append(dt) last_first = first last_rec = rec t_arr = np.asarray(times, dtype=np.float64) mean_t = float(t_arr.mean()) sd_t = float(t_arr.std(ddof=1 if len(t_arr) > 1 else 0)) assert last_first is not None and last_rec is not None return last_first, last_rec, mean_t, sd_t, None # Methods and backend keys BENCH_METHODS: List[Tuple[str, str]] = [ ("SplineOps Standard cubic", "spl_standard"), ("SplineOps Antialiasing cubic", "spl_aa"), ("OpenCV INTER_CUBIC", "opencv"), ("SciPy cubic", "scipy"), ("Pillow BICUBIC", "pillow"), ("scikit-image cubic", "skimage"), ("scikit-image cubic (AA)", "skimage_aa"), ("PyTorch bicubic (CPU)", "torch"), ("PyTorch bicubic (AA, CPU)", "torch_aa"), ] # Subsets for ROI main vs AA MAIN_METHOD_LABELS = [ "SplineOps Standard cubic", "SplineOps Antialiasing cubic", "OpenCV INTER_CUBIC", "SciPy cubic", "scikit-image cubic", "PyTorch bicubic (CPU)", ] AA_METHOD_LABELS = [ "SplineOps Standard cubic", "SplineOps Antialiasing cubic", "Pillow BICUBIC", "scikit-image cubic (AA)", "PyTorch bicubic (AA, CPU)", ] .. GENERATED FROM PYTHON SOURCE LINES 983-985 Benchmarking Helpers -------------------- .. GENERATED FROM PYTHON SOURCE LINES 985-1617 .. code-block:: Python def benchmark_image( img_name: str, gray: np.ndarray, zoom: float, roi_size_px: int, roi_center_frac: Tuple[float, float], degree_label: str = "Cubic", ) -> Dict[str, object]: """ Run the full round-trip benchmark on one image. Returns a dict with: - gray, z, roi_rect, roi, degree_label - aa_first (for intro plot) - roi_tiles (list of (name, tile) for first-pass ROI montages, grayscale) - diff_tiles (list of (name, tile) for ROI error montages, grayscale) - rows (per-method metrics) - diff_max_abs (shared max abs diff used for all diff tiles) """ H, W = gray.shape z = zoom roi_rect = _roi_rect_from_frac(gray.shape, roi_size_px, roi_center_frac) roi = _crop_roi(gray, roi_rect) roi_h = roi_rect[2] roi_w = roi_rect[3] center_r = roi_rect[0] + roi_h / 2.0 center_c = roi_rect[1] + roi_w / 2.0 print( f"\n=== {img_name} | zoom={z:.3f} | shape={H}×{W} " f"| ROI size≈{roi_size_px} px at center_frac={roi_center_frac} ===\n" ) rows: List[Dict[str, object]] = [] roi_tiles: List[Tuple[str, np.ndarray]] = [] diff_tiles: List[Tuple[str, np.ndarray]] = [] # Original ROI tile orig_tile = _nearest_big(roi, ROI_MAG_TARGET) roi_tiles.append(("Original", orig_tile)) # Zero-diff baseline (stays at mid-gray) diff_zero = 0.5 * np.ones_like(roi, dtype=DTYPE) diff_zero_big = _nearest_big(diff_zero, ROI_MAG_TARGET) diff_tiles.append(("Original (no diff)", diff_zero_big)) # Collect per-method recovered ROI for a shared error scale (per image) rec_roi_store: List[Tuple[str, np.ndarray]] = [] diff_max_abs = 0.0 aa_first_for_plot: Optional[np.ndarray] = None header = ( f"{'Method':<32} {'Time (mean)':>14} {'± SD':>10} " f"{'SNR (dB)':>10} {'MSE':>14} {'SSIM':>8}" ) print(header) print("-" * len(header)) for label, backend in BENCH_METHODS: if backend == "spl_standard": rt_fn = lambda gray=gray, z=z: _rt_splineops(gray, z, "cubic") elif backend == "spl_aa": rt_fn = lambda gray=gray, z=z: _rt_splineops(gray, z, "cubic-antialiasing") elif backend == "opencv": rt_fn = lambda gray=gray, z=z: _rt_opencv(gray, z) elif backend == "scipy": rt_fn = lambda gray=gray, z=z: _rt_scipy(gray, z) elif backend == "pillow": rt_fn = lambda gray=gray, z=z: _rt_pillow(gray, z) elif backend == "skimage": rt_fn = lambda gray=gray, z=z: _rt_skimage(gray, z) elif backend == "skimage_aa": rt_fn = lambda gray=gray, z=z: _rt_skimage_aa(gray, z) elif backend == "torch": rt_fn = lambda gray=gray, z=z: _rt_torch(gray, z) elif backend == "torch_aa": rt_fn = lambda gray=gray, z=z: _rt_torch_aa(gray, z) else: continue first, rec, t_mean, t_sd, err = _avg_time(rt_fn, repeats=N_TRIALS, warmup=True) if err is not None or first.size == 0 or rec.size == 0: print( f"{label:<32} {'unavailable':>14} {'':>10} " f"{'—':>10} {'—':>14} {'—':>8}" ) rows.append( dict( name=label, time=np.nan, sd=np.nan, snr=np.nan, mse=np.nan, ssim=np.nan, err=err, ) ) continue if backend == "spl_aa": aa_first_for_plot = first.copy() rec_roi = _crop_roi(rec, roi_rect) snr = _snr_db(roi, rec_roi) mse = float(np.mean((roi - rec_roi) ** 2, dtype=np.float64)) if _HAS_SKIMAGE and _ssim is not None: try: dr = float(roi.max() - roi.min()) if dr <= 0: dr = 1.0 ssim_val = float(_ssim(roi, rec_roi, data_range=dr)) except Exception: ssim_val = float("nan") else: ssim_val = float("nan") print( f"{label:<32} {fmt_ms(t_mean):>14} {fmt_ms(t_sd):>10} " f"{snr:>10.2f} {mse:>14.3e} {ssim_val:>8.4f}" ) rows.append( dict( name=label, time=t_mean, sd=t_sd, snr=snr, mse=mse, ssim=ssim_val, err=None, ) ) # First-pass ROI tile (in the resized domain) H1, W1 = first.shape roi_h_res = max(1, int(round(roi_h * z))) roi_w_res = max(1, int(round(roi_w * z))) if roi_h_res > H1 or roi_w_res > W1: first_roi = first else: center_r_res = int(round(center_r * z)) center_c_res = int(round(center_c * z)) row_top_res = int(np.clip(center_r_res - roi_h_res // 2, 0, H1 - roi_h_res)) col_left_res = int(np.clip(center_c_res - roi_w_res // 2, 0, W1 - roi_w_res)) first_roi = first[ row_top_res : row_top_res + roi_h_res, col_left_res : col_left_res + roi_w_res, ] tile = _nearest_big(first_roi, ROI_MAG_TARGET) roi_tiles.append((label, tile)) # Store recovered ROI (copy) for shared-scale diff montage rec_roi_store.append((label, rec_roi.astype(DTYPE, copy=True))) # Update shared max(|diff|) across all methods for this ROI d = rec_roi.astype(np.float64, copy=False) - roi.astype(np.float64, copy=False) diff_max_abs = max(diff_max_abs, float(np.max(np.abs(d)))) # Build diff tiles with ONE shared scale (per image) so all tiles are comparable diff_max_abs = max(diff_max_abs, 1e-12) for label, rec_roi_m in rec_roi_store: diff_roi = _diff_normalized(roi, rec_roi_m, max_abs=diff_max_abs) diff_tile = _nearest_big(diff_roi, ROI_MAG_TARGET) diff_tiles.append((label, diff_tile)) return dict( img_name=img_name, gray=gray, z=z, roi_rect=roi_rect, roi=roi, degree_label=degree_label, aa_first=aa_first_for_plot, roi_tiles=roi_tiles, diff_tiles=diff_tiles, diff_max_abs=diff_max_abs, rows=rows, ) def show_intro_from_bench(bench: Dict[str, object]) -> None: """2×2 introductory figure for SplineOps AA (grayscale).""" aa_first = bench["aa_first"] if aa_first is None: return _show_initial_original_vs_aa( gray=bench["gray"], # type: ignore[arg-type] roi_rect=bench["roi_rect"], # type: ignore[arg-type] aa_first=aa_first, # type: ignore[arg-type] z=bench["z"], # type: ignore[arg-type] degree_label=bench["degree_label"], # type: ignore[arg-type] ) def show_roi_montage_main_from_bench(bench: Dict[str, object]) -> None: """ Grayscale ROI montage (main subset): Original + SplineOps Standard cubic SplineOps Antialiasing cubic OpenCV INTER_CUBIC SciPy cubic scikit-image cubic PyTorch bicubic (CPU) """ roi_tiles: List[Tuple[str, np.ndarray]] = bench["roi_tiles"] # type: ignore[assignment] if not roi_tiles: return tile_map = {name: tile for name, tile in roi_tiles} names = ["Original"] for lbl in MAIN_METHOD_LABELS: if lbl in tile_map: names.append(lbl) rows, cols = 3, 3 fig, axes = plt.subplots(rows, cols, figsize=(3.2 * cols, 3.2 * rows)) axes = np.asarray(axes).reshape(rows, cols) for ax in axes.ravel(): ax.axis("off") for idx, name in enumerate(names): if idx >= rows * cols: break tile = tile_map[name] r, c = divmod(idx, cols) ax = axes[r, c] ax.imshow(tile, cmap="gray", interpolation="nearest") title_kw = dict(fontsize=ROI_TILE_TITLE_FONTSIZE) style = HIGHLIGHT_STYLE.get(name) if style is not None: c = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) title_kw.update(color=c, fontweight="bold") _highlight_tile(ax, color=c, lw=lw) ax.set_title(name, **title_kw) ax.axis("off") fig.tight_layout() plt.show() def show_roi_montage_aa_from_bench(bench: Dict[str, object]) -> None: """ Grayscale ROI montage (AA subset): Original + SplineOps Standard cubic SplineOps Antialiasing cubic Pillow BICUBIC scikit-image cubic (AA) PyTorch bicubic (AA, CPU) """ roi_tiles: List[Tuple[str, np.ndarray]] = bench["roi_tiles"] # type: ignore[assignment] if not roi_tiles: return tile_map = {name: tile for name, tile in roi_tiles} names = ["Original"] for lbl in AA_METHOD_LABELS: if lbl in tile_map: names.append(lbl) cols = 3 n_tiles = len(names) rows = max(1, (n_tiles + cols - 1) // cols) fig, axes = plt.subplots(rows, cols, figsize=(3.2 * cols, 3.2 * rows)) axes = np.asarray(axes).reshape(rows, cols) for ax in axes.ravel(): ax.axis("off") for idx, name in enumerate(names): if idx >= rows * cols: break tile = tile_map[name] r, c = divmod(idx, cols) ax = axes[r, c] ax.imshow(tile, cmap="gray", interpolation="nearest") title_kw = dict(fontsize=ROI_TILE_TITLE_FONTSIZE) style = HIGHLIGHT_STYLE.get(name) if style is not None: c = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) title_kw.update(color=c, fontweight="bold") _highlight_tile(ax, color=c, lw=lw) ax.set_title(name, **title_kw) ax.axis("off") fig.tight_layout() plt.show() def show_error_montage_main_from_bench(bench: Dict[str, object]) -> None: """ Grayscale error montage (main subset): (rec - orig) normalized: 0.5 = 0, <0.5 = negative, >0.5 = positive. """ diff_tiles: List[Tuple[str, np.ndarray]] = bench["diff_tiles"] # type: ignore[assignment] if not diff_tiles: return tile_map = {name: tile for name, tile in diff_tiles} names = ["Original (no diff)"] for lbl in MAIN_METHOD_LABELS: if lbl in tile_map: names.append(lbl) rows, cols = 3, 3 fig, axes = plt.subplots(rows, cols, figsize=(3.2 * cols, 3.2 * rows)) axes = np.asarray(axes).reshape(rows, cols) for ax in axes.ravel(): ax.axis("off") max_tile_slots = rows * cols - 1 # reserve bottom-right for legend num_tiles = min(len(names), max_tile_slots) for idx in range(num_tiles): name = names[idx] tile = tile_map[name] r, c = divmod(idx, cols) ax = axes[r, c] ax.imshow(tile, cmap="gray", interpolation="nearest", vmin=0.0, vmax=1.0) title_kw = dict(fontsize=ROI_TILE_TITLE_FONTSIZE) style = HIGHLIGHT_STYLE.get(name) if style is not None: c = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) title_kw.update(color=c, fontweight="bold") _highlight_tile(ax, color=c, lw=lw) ax.set_title(name, **title_kw) ax.axis("off") # Legend (unchanged) ax_leg = axes[-1, -1] ax_leg.axis("off") H_leg = ROI_MAG_TARGET W_leg = 32 y = np.linspace(1.0, 0.0, H_leg, dtype=np.float32) legend_img = np.repeat(y[:, None], W_leg, axis=1) ax_leg.imshow(legend_img, cmap="gray", vmin=0.0, vmax=1.0, aspect="auto") ax_leg.set_title("Diff legend", fontsize=ROI_TILE_TITLE_FONTSIZE, pad=4) ax_leg.text(1.05, 0.05, "-1", transform=ax_leg.transAxes, fontsize=8, va="bottom", ha="left") ax_leg.text(1.05, 0.50, "0 (no diff)", transform=ax_leg.transAxes, fontsize=8, va="center", ha="left") ax_leg.text(1.05, 0.95, "+1", transform=ax_leg.transAxes, fontsize=8, va="top", ha="left") fig.suptitle("Normalized signed difference in ROI", fontsize=ROI_SUPTITLE_FONTSIZE) fig.tight_layout(rect=[0, 0, 1, 0.95]) plt.show() def show_error_montage_aa_from_bench(bench: Dict[str, object]) -> None: """ Grayscale error montage (AA subset): (rec - orig) normalized: 0.5 = 0, <0.5 = negative, >0.5 = positive. """ diff_tiles: List[Tuple[str, np.ndarray]] = bench["diff_tiles"] # type: ignore[assignment] if not diff_tiles: return tile_map = {name: tile for name, tile in diff_tiles} names = ["Original (no diff)"] for lbl in AA_METHOD_LABELS: if lbl in tile_map: names.append(lbl) rows, cols = 3, 3 fig, axes = plt.subplots(rows, cols, figsize=(3.2 * cols, 3.2 * rows)) axes = np.asarray(axes).reshape(rows, cols) for ax in axes.ravel(): ax.axis("off") max_tile_slots = rows * cols - 1 num_tiles = min(len(names), max_tile_slots) for idx in range(num_tiles): name = names[idx] tile = tile_map[name] r, c = divmod(idx, cols) ax = axes[r, c] ax.imshow(tile, cmap="gray", interpolation="nearest", vmin=0.0, vmax=1.0) title_kw = dict(fontsize=ROI_TILE_TITLE_FONTSIZE) style = HIGHLIGHT_STYLE.get(name) if style is not None: c = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) title_kw.update(color=c, fontweight="bold") _highlight_tile(ax, color=c, lw=lw) ax.set_title(name, **title_kw) ax.axis("off") # Legend (unchanged) ax_leg = axes[-1, -1] ax_leg.axis("off") H_leg = ROI_MAG_TARGET W_leg = 32 y = np.linspace(1.0, 0.0, H_leg, dtype=np.float32) legend_img = np.repeat(y[:, None], W_leg, axis=1) ax_leg.imshow(legend_img, cmap="gray", vmin=0.0, vmax=1.0, aspect="auto") ax_leg.set_title("Diff legend", fontsize=ROI_TILE_TITLE_FONTSIZE, pad=4) ax_leg.text(1.05, 0.05, "-1", transform=ax_leg.transAxes, fontsize=8, va="bottom", ha="left") ax_leg.text(1.05, 0.50, "0 (no diff)", transform=ax_leg.transAxes, fontsize=8, va="center", ha="left") ax_leg.text(1.05, 0.95, "+1", transform=ax_leg.transAxes, fontsize=8, va="top", ha="left") fig.suptitle("Normalized signed difference in ROI", fontsize=ROI_SUPTITLE_FONTSIZE) fig.tight_layout(rect=[0, 0, 1, 0.95]) plt.show() def show_timing_plot_from_bench(bench: Dict[str, object]) -> None: """Horizontal bar chart of round-trip timing per method.""" rows: List[Dict[str, object]] = bench["rows"] # type: ignore[assignment] if not rows: return gray = bench["gray"] # type: ignore[index] H, W = gray.shape z = float(bench["z"]) degree_label = str(bench["degree_label"]) valid = [r for r in rows if np.isfinite(r.get("time", np.nan))] if not valid: return names = [r["name"] for r in valid] times = np.array([r["time"] for r in valid], dtype=np.float64) sds = np.array([r["sd"] for r in valid], dtype=np.float64) order = np.argsort(times) names = [names[i] for i in order] times = times[order] sds = sds[order] fig, ax = plt.subplots(figsize=PLOT_FIGSIZE) y = np.arange(len(names)) bars = ax.barh(y, times, xerr=sds, alpha=0.8) ax.set_yticks(y) ax.set_yticklabels(names, fontsize=PLOT_TICK_FONTSIZE) ax.tick_params(axis="x", labelsize=PLOT_TICK_FONTSIZE) ax.set_xlabel( f"Round-trip time (s) mean ± sd over {N_TRIALS} runs", fontsize=PLOT_LABEL_FONTSIZE, ) ax.set_title( f"Timing vs Method (H×W = {H}×{W}, zoom ×{z:g}, degree={degree_label})", fontsize=PLOT_TITLE_FONTSIZE, ) ax.grid(axis="x", alpha=0.3) # --- Color the METHOD NAMES (yticks) + outline highlighted bars --- for tick, name, bar in zip(ax.get_yticklabels(), names, bars.patches): style = HIGHLIGHT_STYLE.get(name) if style is not None: c = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) tick.set_color(c) tick.set_fontweight("bold") # Optional: outline the bar too (nice but not required) bar.set_edgecolor(c) bar.set_linewidth(lw) fig.tight_layout() plt.show() def show_snr_ssim_plot_from_bench(bench: Dict[str, object]) -> None: """Combined SNR/SSIM bar chart per method.""" if not (_HAS_SKIMAGE and _ssim is not None): return rows: List[Dict[str, object]] = bench["rows"] # type: ignore[assignment] if not rows: return gray = bench["gray"] # type: ignore[index] H, W = gray.shape z = float(bench["z"]) degree_label = str(bench["degree_label"]) valid = [ r for r in rows if np.isfinite(r.get("snr", np.nan)) and np.isfinite(r.get("ssim", np.nan)) ] if not valid: return names = [r["name"] for r in valid] snrs = np.array([r["snr"] for r in valid], dtype=np.float64) ssims = np.array([r["ssim"] for r in valid], dtype=np.float64) order = np.argsort(-snrs) names = [names[i] for i in order] snrs = snrs[order] ssims = ssims[order] x = np.arange(len(names)) width = 0.4 fig, ax1 = plt.subplots(figsize=PLOT_FIGSIZE) snr_color = "tab:blue" ssim_color = "tab:green" snr_bars = ax1.bar( x - width / 2, snrs, width, label="SNR (dB)", alpha=0.85, color=snr_color, ) ax1.set_ylabel("SNR (dB)", color=snr_color, fontsize=PLOT_LABEL_FONTSIZE) ax1.tick_params(axis="y", labelcolor=snr_color, labelsize=PLOT_TICK_FONTSIZE) ax1.set_xticks(x) ax1.set_xticklabels( names, rotation=30, ha="right", fontsize=PLOT_TICK_FONTSIZE, ) ax1.grid(axis="y", alpha=0.3) ax2 = ax1.twinx() ssim_bars = ax2.bar( x + width / 2, ssims, width, label="SSIM", alpha=0.6, color=ssim_color, ) ax2.set_ylabel("SSIM", color=ssim_color, fontsize=PLOT_LABEL_FONTSIZE) ax2.tick_params(axis="y", labelcolor=ssim_color, labelsize=PLOT_TICK_FONTSIZE) roi_h, roi_w = bench["roi_rect"][2], bench["roi_rect"][3] # type: ignore[index] ax1.set_title( f"SNR / SSIM vs Method (ROI = {roi_h}×{roi_w} px, zoom ×{z:g}, degree={degree_label})", fontsize=PLOT_TITLE_FONTSIZE, ) handles = [snr_bars[0], ssim_bars[0]] labels = ["SNR (dB)", "SSIM"] fig.legend( handles, labels, loc="upper right", bbox_to_anchor=(1, 1), fontsize=PLOT_LEGEND_FONTSIZE, ) # --- Highlight xtick labels (SplineOps methods) --- for tick, name in zip(ax1.get_xticklabels(), names): style = HIGHLIGHT_STYLE.get(name) if style is not None: col = style.get("color", "tab:blue") tick.set_color(col) tick.set_fontweight("bold") # --- Outline BOTH bars (now safe because ssim_bars exists) --- for i, name in enumerate(names): style = HIGHLIGHT_STYLE.get(name) if style is not None: col = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) snr_bars.patches[i].set_edgecolor(col) snr_bars.patches[i].set_linewidth(lw) ssim_bars.patches[i].set_edgecolor(col) ssim_bars.patches[i].set_linewidth(lw) # --- Smart truncated y-limits (robust) --- snr_lim = _smart_ylim(snrs, pad_frac=0.06, min_span=1.0) # dB if snr_lim is not None: ax1.set_ylim(*snr_lim) ssim_lim = _smart_ylim(ssims, lo_cap=0.0, hi_cap=1.0, pad_frac=0.02, min_span=0.02) if ssim_lim is not None: ax2.set_ylim(*ssim_lim) fig.tight_layout() plt.show() .. GENERATED FROM PYTHON SOURCE LINES 1618-1620 Color ROI Montage Helpers ------------------------- .. GENERATED FROM PYTHON SOURCE LINES 1620-1917 .. code-block:: Python def _first_pass_color_for_backend( backend: str, rgb: np.ndarray, z: float, ) -> Optional[np.ndarray]: """ First-pass color resized image for a given backend, for color ROIs only. """ H, W, C = rgb.shape H1 = int(round(H * z)) W1 = int(round(W * z)) if H1 < 1 or W1 < 1: return None try: if backend in ("spl_standard", "spl_aa"): if not _HAS_SPLINEOPS: return None method = "cubic" if backend == "spl_standard" else "cubic-antialiasing" zoom_hw = (z, z) channels = [] for c in range(C): ch = sp_resize( rgb[..., c], zoom_factors=zoom_hw, method=method, ) channels.append(ch) first = np.stack(channels, axis=-1) elif backend == "scipy": if not _HAS_SCIPY: return None channels = [] for c in range(C): ch = _ndi_zoom( rgb[..., c], (z, z), order=3, prefilter=True, mode="reflect", grid_mode=False, ) channels.append(ch) first = np.stack(channels, axis=-1) elif backend == "opencv": if not _HAS_CV2: return None arr = rgb.astype(np.float32, copy=False) first = cv2.resize(arr, (W1, H1), interpolation=cv2.INTER_CUBIC) elif backend == "pillow": from PIL import Image as _Image arr_uint8 = (np.clip(rgb, 0.0, 1.0) * 255).astype(np.uint8) im = _Image.fromarray(arr_uint8, mode="RGB") first_im = im.resize((W1, H1), resample=_Image.Resampling.BICUBIC) first = np.asarray(first_im, dtype=np.float32) / 255.0 elif backend == "skimage": if not _HAS_SKIMAGE: return None first = _sk_resize( rgb, (H1, W1, C), order=3, anti_aliasing=False, preserve_range=True, mode="reflect", ).astype(np.float32) elif backend == "skimage_aa": if not _HAS_SKIMAGE: return None first = _sk_resize( rgb, (H1, W1, C), order=3, anti_aliasing=True, preserve_range=True, mode="reflect", ).astype(np.float32) elif backend == "torch": if not _HAS_TORCH: return None arr = rgb if arr.dtype == np.float32: t_dtype = torch.float32 elif arr.dtype == np.float64: t_dtype = torch.float64 else: t_dtype = torch.float32 arr = arr.astype(np.float32, copy=False) x = torch.from_numpy(arr).to(t_dtype).permute(2, 0, 1).unsqueeze(0) first_t = F.interpolate( x, size=(H1, W1), mode="bicubic", align_corners=False, antialias=False, ) first = first_t[0].permute(1, 2, 0).detach().cpu().numpy() elif backend == "torch_aa": if not _HAS_TORCH: return None arr = rgb if arr.dtype == np.float32: t_dtype = torch.float32 elif arr.dtype == np.float64: t_dtype = torch.float64 else: t_dtype = torch.float32 arr = arr.astype(np.float32, copy=False) x = torch.from_numpy(arr).to(t_dtype).permute(2, 0, 1).unsqueeze(0) first_t = F.interpolate( x, size=(H1, W1), mode="bicubic", align_corners=False, antialias=True, ) first = first_t[0].permute(1, 2, 0).detach().cpu().numpy() else: return None return np.clip(first, 0.0, 1.0).astype(DTYPE, copy=False) except Exception: return None def show_roi_montage_color_main_from_bench( bench: Dict[str, object], orig_rgb: np.ndarray, ) -> None: """Color ROI montage for the main subset of methods.""" roi_rect = bench["roi_rect"] z = float(bench["z"]) row0, col0, roi_h, roi_w = roi_rect roi_orig = orig_rgb[row0:row0 + roi_h, col0:col0 + roi_w, :] orig_tile = _nearest_big_color(roi_orig, ROI_MAG_TARGET) tiles: List[Tuple[str, np.ndarray]] = [("Original (color)", orig_tile)] center_r = row0 + roi_h / 2.0 center_c = col0 + roi_w / 2.0 subset = [ ("SplineOps Standard cubic", "spl_standard"), ("SplineOps Antialiasing cubic", "spl_aa"), ("OpenCV INTER_CUBIC", "opencv"), ("SciPy cubic", "scipy"), ("scikit-image cubic", "skimage"), ("PyTorch bicubic (CPU)", "torch"), ] for label, backend in subset: first_color = _first_pass_color_for_backend(backend, orig_rgb, z) if first_color is None: continue H1, W1, _ = first_color.shape roi_h_res = max(1, int(round(roi_h * z))) roi_w_res = max(1, int(round(roi_w * z))) if roi_h_res > H1 or roi_w_res > W1: roi_first = first_color else: center_r_res = int(round(center_r * z)) center_c_res = int(round(center_c * z)) row_top_res = int(np.clip(center_r_res - roi_h_res // 2, 0, H1 - roi_h_res)) col_left_res = int(np.clip(center_c_res - roi_w_res // 2, 0, W1 - roi_w_res)) roi_first = first_color[ row_top_res : row_top_res + roi_h_res, col_left_res : col_left_res + roi_w_res, : ] tile = _nearest_big_color(roi_first, ROI_MAG_TARGET) tiles.append((label, tile)) rows, cols = 3, 3 fig, axes = plt.subplots(rows, cols, figsize=(3.2 * cols, 3.2 * rows)) axes = np.asarray(axes).reshape(rows, cols) for ax in axes.ravel(): ax.axis("off") for idx, (name, tile) in enumerate(tiles): if idx >= rows * cols: break r, c = divmod(idx, cols) ax = axes[r, c] ax.imshow(np.clip(tile, 0.0, 1.0)) title_kw = dict(fontsize=ROI_TILE_TITLE_FONTSIZE) style = HIGHLIGHT_STYLE.get(name) if style is not None: c = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) title_kw.update(color=c, fontweight="bold") _highlight_tile(ax, color=c, lw=lw) ax.set_title(name, **title_kw) ax.axis("off") fig.tight_layout() plt.show() def show_roi_montage_color_aa_from_bench( bench: Dict[str, object], orig_rgb: np.ndarray, ) -> None: """Color ROI montage for AA / smoothing subset.""" roi_rect = bench["roi_rect"] z = float(bench["z"]) row0, col0, roi_h, roi_w = roi_rect roi_orig = orig_rgb[row0:row0 + roi_h, col0:col0 + roi_w, :] orig_tile = _nearest_big_color(roi_orig, ROI_MAG_TARGET) tiles: List[Tuple[str, np.ndarray]] = [("Original (color)", orig_tile)] center_r = row0 + roi_h / 2.0 center_c = col0 + roi_w / 2.0 subset = [ ("SplineOps Standard cubic", "spl_standard"), ("SplineOps Antialiasing cubic", "spl_aa"), ("Pillow BICUBIC", "pillow"), ("scikit-image cubic (AA)", "skimage_aa"), ("PyTorch bicubic (AA, CPU)", "torch_aa"), ] for label, backend in subset: first_color = _first_pass_color_for_backend(backend, orig_rgb, z) if first_color is None: continue H1, W1, _ = first_color.shape roi_h_res = max(1, int(round(roi_h * z))) roi_w_res = max(1, int(round(roi_w * z))) if roi_h_res > H1 or roi_w_res > W1: roi_first = first_color else: center_r_res = int(round(center_r * z)) center_c_res = int(round(center_c * z)) row_top_res = int(np.clip(center_r_res - roi_h_res // 2, 0, H1 - roi_h_res)) col_left_res = int(np.clip(center_c_res - roi_w_res // 2, 0, W1 - roi_w_res)) roi_first = first_color[ row_top_res : row_top_res + roi_h_res, col_left_res : col_left_res + roi_w_res, : ] tile = _nearest_big_color(roi_first, ROI_MAG_TARGET) tiles.append((label, tile)) cols = 3 n_tiles = len(tiles) rows = max(1, (n_tiles + cols - 1) // cols) fig, axes = plt.subplots(rows, cols, figsize=(3.2 * cols, 3.2 * rows)) axes = np.asarray(axes).reshape(rows, cols) for ax in axes.ravel(): ax.axis("off") for idx, (name, tile) in enumerate(tiles): if idx >= rows * cols: break r, c = divmod(idx, cols) ax = axes[r, c] ax.imshow(np.clip(tile, 0.0, 1.0)) title_kw = dict(fontsize=ROI_TILE_TITLE_FONTSIZE) style = HIGHLIGHT_STYLE.get(name) if style is not None: c = style.get("color", "tab:blue") lw = float(style.get("lw", 3.0)) title_kw.update(color=c, fontweight="bold") _highlight_tile(ax, color=c, lw=lw) ax.set_title(name, **title_kw) ax.axis("off") fig.tight_layout() plt.show() .. GENERATED FROM PYTHON SOURCE LINES 1918-1920 Load All Images --------------- .. GENERATED FROM PYTHON SOURCE LINES 1920-1969 .. code-block:: Python orig_images: Dict[str, np.ndarray] = {} orig_images_rgb: Dict[str, np.ndarray] = {} for name, url in KODAK_IMAGES: gray = _load_kodak_gray(url) orig_images[name] = gray rgb = _load_kodak_rgb(url) orig_images_rgb[name] = rgb print(f"Loaded {name} from {url} | gray shape={gray.shape}, rgb shape={rgb.shape}") print("\nTimings averaged over " f"{N_TRIALS} runs per method (1 warm-up run not counted).\n") # Small helper for color intro using SplineOps antialiasing def _color_intro_for_image( img_name: str, bench: Dict[str, object], ) -> None: if not _HAS_SPLINEOPS: show_intro_from_bench(bench) return rgb = orig_images_rgb[img_name] roi_rect = bench["roi_rect"] z = float(bench["z"]) zoom_hw = (z, z) channels = [] for c in range(rgb.shape[2]): ch = sp_resize( rgb[..., c], zoom_factors=zoom_hw, method="cubic-antialiasing", ) channels.append(ch) aa_rgb = np.stack(channels, axis=-1) show_intro_color( original_rgb=rgb, shrunk_rgb=aa_rgb, roi_rect=roi_rect, zoom=z, label="Antialiasing", degree_label=bench["degree_label"], # type: ignore[arg-type] ) .. rst-class:: sphx-glr-script-out .. code-block:: none Loaded kodim05 from https://r0k.us/graphics/kodak/kodak/kodim05.png | gray shape=(512, 768), rgb shape=(512, 768, 3) Loaded kodim07 from https://r0k.us/graphics/kodak/kodak/kodim07.png | gray shape=(512, 768), rgb shape=(512, 768, 3) Loaded kodim14 from https://r0k.us/graphics/kodak/kodak/kodim14.png | gray shape=(512, 768), rgb shape=(512, 768, 3) Loaded kodim15 from https://r0k.us/graphics/kodak/kodak/kodim15.png | gray shape=(512, 768), rgb shape=(512, 768, 3) Loaded kodim19 from https://r0k.us/graphics/kodak/kodak/kodim19.png | gray shape=(768, 512), rgb shape=(768, 512, 3) Loaded kodim22 from https://r0k.us/graphics/kodak/kodak/kodim22.png | gray shape=(512, 768), rgb shape=(512, 768, 3) Loaded kodim23 from https://r0k.us/graphics/kodak/kodak/kodim23.png | gray shape=(512, 768), rgb shape=(512, 768, 3) Timings averaged over 10 runs per method (1 warm-up run not counted). .. GENERATED FROM PYTHON SOURCE LINES 1970-1972 Image: kodim05 -------------- .. GENERATED FROM PYTHON SOURCE LINES 1972-1989 .. code-block:: Python img_name = "kodim05" img_orig = orig_images[img_name] cfg = IMAGE_CONFIG[img_name] zoom = float(cfg["zoom"]) roi_size_px = int(cfg["roi_size_px"]) roi_center_frac = tuple(map(float, cfg["roi_center_frac"])) # type: ignore[arg-type] bench_kodim05 = benchmark_image( img_name=img_name, gray=img_orig, zoom=zoom, roi_size_px=roi_size_px, roi_center_frac=roi_center_frac, degree_label="Cubic", ) .. rst-class:: sphx-glr-script-out .. code-block:: none === kodim05 | zoom=0.150 | shape=512×768 | ROI size≈256 px at center_frac=(0.75, 0.5) === Method Time (mean) ± SD SNR (dB) MSE SSIM --------------------------------------------------------------------------------------------- SplineOps Standard cubic 2.6 ms 0.0 ms 7.29 1.768e-02 0.3098 SplineOps Antialiasing cubic 4.8 ms 0.0 ms 9.58 1.045e-02 0.3682 OpenCV INTER_CUBIC 0.4 ms 0.0 ms 7.53 1.672e-02 0.3285 SciPy cubic 31.3 ms 0.3 ms 7.36 1.740e-02 0.3106 Pillow BICUBIC 2.9 ms 0.1 ms 9.35 1.101e-02 0.3368 scikit-image cubic 32.1 ms 0.3 ms 7.52 1.678e-02 0.3233 scikit-image cubic (AA) 39.5 ms 0.1 ms 9.17 1.147e-02 0.3185 PyTorch bicubic (CPU) 2.3 ms 0.0 ms 7.53 1.672e-02 0.3285 PyTorch bicubic (AA, CPU) 1.3 ms 0.1 ms 9.35 1.101e-02 0.3368 .. GENERATED FROM PYTHON SOURCE LINES 1990-1992 Original and Resized ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 1992-1995 .. code-block:: Python _color_intro_for_image(img_name, bench_kodim05) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_001.png :alt: Original image with ROI (512×768 px), Original ROI (256×256 px, NN magnified), SplineOps Antialiasing cubic (zoom ×0.15, 77×115 px), Antialiasing ROI (38×38 px, NN magnified) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 1996-1998 ROI Comparison ~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 1998-2001 .. code-block:: Python show_roi_montage_main_from_bench(bench_kodim05) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_002.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2002-2004 ROI Comparison Error ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2004-2007 .. code-block:: Python show_error_montage_main_from_bench(bench_kodim05) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_003.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_003.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2008-2010 ROI Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2010-2013 .. code-block:: Python show_roi_montage_aa_from_bench(bench_kodim05) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_004.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_004.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2014-2016 ROI Comparison Error (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2016-2019 .. code-block:: Python show_error_montage_aa_from_bench(bench_kodim05) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_005.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_005.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2020-2022 ROI Color Comparison ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2022-2025 .. code-block:: Python show_roi_montage_color_main_from_bench(bench_kodim05, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_006.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_006.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2026-2028 ROI Color Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2028-2031 .. code-block:: Python show_roi_montage_color_aa_from_bench(bench_kodim05, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_007.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_007.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2032-2034 Timing Comparison ~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2034-2037 .. code-block:: Python show_timing_plot_from_bench(bench_kodim05) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_008.png :alt: Timing vs Method (H×W = 512×768, zoom ×0.15, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_008.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2038-2040 SNR/SSIM Comparison ~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2040-2043 .. code-block:: Python show_snr_ssim_plot_from_bench(bench_kodim05) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_009.png :alt: SNR / SSIM vs Method (ROI = 256×256 px, zoom ×0.15, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_009.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2044-2046 Image: kodim07 -------------- .. GENERATED FROM PYTHON SOURCE LINES 2046-2063 .. code-block:: Python img_name = "kodim07" img_orig = orig_images[img_name] cfg = IMAGE_CONFIG[img_name] zoom = float(cfg["zoom"]) roi_size_px = int(cfg["roi_size_px"]) roi_center_frac = tuple(map(float, cfg["roi_center_frac"])) # type: ignore[arg-type] bench_kodim07 = benchmark_image( img_name=img_name, gray=img_orig, zoom=zoom, roi_size_px=roi_size_px, roi_center_frac=roi_center_frac, degree_label="Cubic", ) .. rst-class:: sphx-glr-script-out .. code-block:: none === kodim07 | zoom=0.150 | shape=512×768 | ROI size≈256 px at center_frac=(0.4, 0.5) === Method Time (mean) ± SD SNR (dB) MSE SSIM --------------------------------------------------------------------------------------------- SplineOps Standard cubic 2.6 ms 0.0 ms 13.48 9.393e-03 0.4988 SplineOps Antialiasing cubic 4.7 ms 0.0 ms 15.71 5.618e-03 0.5280 OpenCV INTER_CUBIC 0.4 ms 0.0 ms 13.52 9.304e-03 0.5022 SciPy cubic 31.0 ms 0.1 ms 13.48 9.393e-03 0.4988 Pillow BICUBIC 2.8 ms 0.0 ms 15.51 5.882e-03 0.5091 scikit-image cubic 31.9 ms 0.1 ms 13.49 9.372e-03 0.4960 scikit-image cubic (AA) 39.4 ms 0.1 ms 15.29 6.191e-03 0.4948 PyTorch bicubic (CPU) 3.1 ms 1.1 ms 13.52 9.304e-03 0.5022 PyTorch bicubic (AA, CPU) 1.3 ms 0.0 ms 15.51 5.882e-03 0.5091 .. GENERATED FROM PYTHON SOURCE LINES 2064-2066 Original and Resized ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2066-2069 .. code-block:: Python _color_intro_for_image(img_name, bench_kodim07) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_010.png :alt: Original image with ROI (512×768 px), Original ROI (256×256 px, NN magnified), SplineOps Antialiasing cubic (zoom ×0.15, 77×115 px), Antialiasing ROI (38×38 px, NN magnified) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_010.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2070-2072 ROI Comparison ~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2072-2075 .. code-block:: Python show_roi_montage_main_from_bench(bench_kodim07) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_011.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_011.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2076-2078 ROI Comparison Error ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2078-2081 .. code-block:: Python show_error_montage_main_from_bench(bench_kodim07) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_012.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_012.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2082-2084 ROI Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2084-2087 .. code-block:: Python show_roi_montage_aa_from_bench(bench_kodim07) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_013.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_013.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2088-2090 ROI Comparison Error (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2090-2093 .. code-block:: Python show_error_montage_aa_from_bench(bench_kodim07) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_014.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_014.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2094-2096 ROI Color Comparison ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2096-2099 .. code-block:: Python show_roi_montage_color_main_from_bench(bench_kodim07, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_015.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_015.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2100-2102 ROI Color Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2102-2105 .. code-block:: Python show_roi_montage_color_aa_from_bench(bench_kodim07, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_016.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_016.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2106-2108 Timing Comparison ~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2108-2111 .. code-block:: Python show_timing_plot_from_bench(bench_kodim07) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_017.png :alt: Timing vs Method (H×W = 512×768, zoom ×0.15, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_017.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2112-2114 SNR/SSIM Comparison ~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2114-2117 .. code-block:: Python show_snr_ssim_plot_from_bench(bench_kodim07) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_018.png :alt: SNR / SSIM vs Method (ROI = 256×256 px, zoom ×0.15, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_018.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2118-2120 Image: kodim14 -------------- .. GENERATED FROM PYTHON SOURCE LINES 2120-2137 .. code-block:: Python img_name = "kodim14" img_orig = orig_images[img_name] cfg = IMAGE_CONFIG[img_name] zoom = float(cfg["zoom"]) roi_size_px = int(cfg["roi_size_px"]) roi_center_frac = tuple(map(float, cfg["roi_center_frac"])) # type: ignore[arg-type] bench_kodim14 = benchmark_image( img_name=img_name, gray=img_orig, zoom=zoom, roi_size_px=roi_size_px, roi_center_frac=roi_center_frac, degree_label="Cubic", ) .. rst-class:: sphx-glr-script-out .. code-block:: none === kodim14 | zoom=0.300 | shape=512×768 | ROI size≈256 px at center_frac=(0.75, 0.75) === Method Time (mean) ± SD SNR (dB) MSE SSIM --------------------------------------------------------------------------------------------- SplineOps Standard cubic 3.1 ms 0.0 ms 14.61 6.942e-03 0.6010 SplineOps Antialiasing cubic 5.8 ms 0.1 ms 16.58 4.417e-03 0.6797 OpenCV INTER_CUBIC 0.4 ms 0.0 ms 14.64 6.909e-03 0.6114 SciPy cubic 32.6 ms 0.1 ms 14.68 6.844e-03 0.6021 Pillow BICUBIC 3.1 ms 0.1 ms 16.31 4.698e-03 0.6390 scikit-image cubic 33.3 ms 0.1 ms 14.61 6.954e-03 0.6022 scikit-image cubic (AA) 38.0 ms 0.0 ms 16.26 4.757e-03 0.6368 PyTorch bicubic (CPU) 2.4 ms 0.0 ms 14.64 6.909e-03 0.6114 PyTorch bicubic (AA, CPU) 1.3 ms 0.0 ms 16.31 4.698e-03 0.6390 .. GENERATED FROM PYTHON SOURCE LINES 2138-2140 Original and Resized ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2140-2143 .. code-block:: Python _color_intro_for_image(img_name, bench_kodim14) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_019.png :alt: Original image with ROI (512×768 px), Original ROI (256×256 px, NN magnified), SplineOps Antialiasing cubic (zoom ×0.3, 154×230 px), Antialiasing ROI (77×77 px, NN magnified) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_019.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2144-2146 ROI Comparison ~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2146-2149 .. code-block:: Python show_roi_montage_main_from_bench(bench_kodim14) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_020.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_020.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2150-2152 ROI Comparison Error ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2152-2155 .. code-block:: Python show_error_montage_main_from_bench(bench_kodim14) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_021.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_021.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2156-2158 ROI Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2158-2161 .. code-block:: Python show_roi_montage_aa_from_bench(bench_kodim14) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_022.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_022.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2162-2164 ROI Comparison Error (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2164-2167 .. code-block:: Python show_error_montage_aa_from_bench(bench_kodim14) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_023.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_023.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2168-2170 ROI Color Comparison ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2170-2173 .. code-block:: Python show_roi_montage_color_main_from_bench(bench_kodim14, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_024.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_024.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2174-2176 ROI Color Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2176-2179 .. code-block:: Python show_roi_montage_color_aa_from_bench(bench_kodim14, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_025.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_025.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2180-2182 Time Comparison ~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2182-2185 .. code-block:: Python show_timing_plot_from_bench(bench_kodim14) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_026.png :alt: Timing vs Method (H×W = 512×768, zoom ×0.3, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_026.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2186-2188 SNR/SSIM Comparison ~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2188-2191 .. code-block:: Python show_snr_ssim_plot_from_bench(bench_kodim14) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_027.png :alt: SNR / SSIM vs Method (ROI = 256×256 px, zoom ×0.3, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_027.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2192-2194 Image: kodim15 -------------- .. GENERATED FROM PYTHON SOURCE LINES 2194-2211 .. code-block:: Python img_name = "kodim15" img_orig = orig_images[img_name] cfg = IMAGE_CONFIG[img_name] zoom = float(cfg["zoom"]) roi_size_px = int(cfg["roi_size_px"]) roi_center_frac = tuple(map(float, cfg["roi_center_frac"])) # type: ignore[arg-type] bench_kodim15 = benchmark_image( img_name=img_name, gray=img_orig, zoom=zoom, roi_size_px=roi_size_px, roi_center_frac=roi_center_frac, degree_label="Cubic", ) .. rst-class:: sphx-glr-script-out .. code-block:: none === kodim15 | zoom=0.300 | shape=512×768 | ROI size≈256 px at center_frac=(0.3, 0.55) === Method Time (mean) ± SD SNR (dB) MSE SSIM --------------------------------------------------------------------------------------------- SplineOps Standard cubic 3.1 ms 0.0 ms 17.97 2.034e-03 0.7223 SplineOps Antialiasing cubic 5.8 ms 0.0 ms 19.89 1.308e-03 0.7791 OpenCV INTER_CUBIC 0.4 ms 0.0 ms 18.08 1.987e-03 0.7288 SciPy cubic 32.5 ms 0.1 ms 17.97 2.034e-03 0.7223 Pillow BICUBIC 3.1 ms 0.0 ms 19.36 1.479e-03 0.7627 scikit-image cubic 33.5 ms 0.4 ms 17.99 2.025e-03 0.7231 scikit-image cubic (AA) 37.8 ms 0.1 ms 19.31 1.494e-03 0.7624 PyTorch bicubic (CPU) 2.4 ms 0.0 ms 18.08 1.987e-03 0.7288 PyTorch bicubic (AA, CPU) 1.5 ms 0.3 ms 19.36 1.479e-03 0.7627 .. GENERATED FROM PYTHON SOURCE LINES 2212-2214 Original and Resized ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2214-2217 .. code-block:: Python _color_intro_for_image(img_name, bench_kodim15) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_028.png :alt: Original image with ROI (512×768 px), Original ROI (256×256 px, NN magnified), SplineOps Antialiasing cubic (zoom ×0.3, 154×230 px), Antialiasing ROI (77×77 px, NN magnified) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_028.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2218-2220 ROI Comparison ~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2220-2223 .. code-block:: Python show_roi_montage_main_from_bench(bench_kodim15) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_029.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_029.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2224-2226 ROI Comparison Error ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2226-2229 .. code-block:: Python show_error_montage_main_from_bench(bench_kodim15) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_030.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_030.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2230-2232 ROI Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2232-2235 .. code-block:: Python show_roi_montage_aa_from_bench(bench_kodim15) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_031.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_031.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2236-2238 ROI Comparison Error (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2238-2241 .. code-block:: Python show_error_montage_aa_from_bench(bench_kodim15) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_032.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_032.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2242-2244 ROI Color Comparison ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2244-2247 .. code-block:: Python show_roi_montage_color_main_from_bench(bench_kodim15, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_033.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_033.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2248-2250 ROI Color Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2250-2253 .. code-block:: Python show_roi_montage_color_aa_from_bench(bench_kodim15, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_034.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_034.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2254-2256 Time Comparison ~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2256-2259 .. code-block:: Python show_timing_plot_from_bench(bench_kodim15) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_035.png :alt: Timing vs Method (H×W = 512×768, zoom ×0.3, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_035.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2260-2262 SNR/SSIM Comparison ~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2262-2265 .. code-block:: Python show_snr_ssim_plot_from_bench(bench_kodim15) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_036.png :alt: SNR / SSIM vs Method (ROI = 256×256 px, zoom ×0.3, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_036.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2266-2268 Image: kodim19 -------------- .. GENERATED FROM PYTHON SOURCE LINES 2268-2285 .. code-block:: Python img_name = "kodim19" img_orig = orig_images[img_name] cfg = IMAGE_CONFIG[img_name] zoom = float(cfg["zoom"]) roi_size_px = int(cfg["roi_size_px"]) roi_center_frac = tuple(map(float, cfg["roi_center_frac"])) # type: ignore[arg-type] bench_kodim19 = benchmark_image( img_name=img_name, gray=img_orig, zoom=zoom, roi_size_px=roi_size_px, roi_center_frac=roi_center_frac, degree_label="Cubic", ) .. rst-class:: sphx-glr-script-out .. code-block:: none === kodim19 | zoom=0.200 | shape=768×512 | ROI size≈256 px at center_frac=(0.65, 0.35) === Method Time (mean) ± SD SNR (dB) MSE SSIM --------------------------------------------------------------------------------------------- SplineOps Standard cubic 3.3 ms 0.0 ms 11.44 2.413e-02 0.4145 SplineOps Antialiasing cubic 5.2 ms 0.0 ms 13.82 1.394e-02 0.4540 OpenCV INTER_CUBIC 0.4 ms 0.0 ms 11.52 2.367e-02 0.4287 SciPy cubic 32.9 ms 0.0 ms 11.44 2.413e-02 0.4145 Pillow BICUBIC 2.8 ms 0.0 ms 13.69 1.438e-02 0.4299 scikit-image cubic 33.7 ms 0.1 ms 11.47 2.396e-02 0.4216 scikit-image cubic (AA) 40.8 ms 0.1 ms 13.55 1.483e-02 0.4189 PyTorch bicubic (CPU) 2.4 ms 0.0 ms 11.52 2.367e-02 0.4287 PyTorch bicubic (AA, CPU) 1.3 ms 0.0 ms 13.69 1.438e-02 0.4299 .. GENERATED FROM PYTHON SOURCE LINES 2286-2288 Original and Resized ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2288-2291 .. code-block:: Python _color_intro_for_image(img_name, bench_kodim19) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_037.png :alt: Original image with ROI (768×512 px), Original ROI (256×256 px, NN magnified), SplineOps Antialiasing cubic (zoom ×0.2, 154×102 px), Antialiasing ROI (51×51 px, NN magnified) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_037.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2292-2294 ROI Comparison ~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2294-2297 .. code-block:: Python show_roi_montage_main_from_bench(bench_kodim19) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_038.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_038.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2298-2300 ROI Comparison Error ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2300-2303 .. code-block:: Python show_error_montage_main_from_bench(bench_kodim19) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_039.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_039.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2304-2306 ROI Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2306-2309 .. code-block:: Python show_roi_montage_aa_from_bench(bench_kodim19) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_040.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_040.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2310-2312 ROI Comparison Error (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2312-2315 .. code-block:: Python show_error_montage_aa_from_bench(bench_kodim19) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_041.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_041.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2316-2318 ROI Color Comparison ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2318-2321 .. code-block:: Python show_roi_montage_color_main_from_bench(bench_kodim19, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_042.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_042.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2322-2324 ROI Color Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2324-2327 .. code-block:: Python show_roi_montage_color_aa_from_bench(bench_kodim19, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_043.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_043.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2328-2330 Time Comparison ~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2330-2333 .. code-block:: Python show_timing_plot_from_bench(bench_kodim19) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_044.png :alt: Timing vs Method (H×W = 768×512, zoom ×0.2, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_044.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2334-2336 SNR/SSIM Comparison ~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2336-2339 .. code-block:: Python show_snr_ssim_plot_from_bench(bench_kodim19) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_045.png :alt: SNR / SSIM vs Method (ROI = 256×256 px, zoom ×0.2, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_045.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2340-2342 Image: kodim22 -------------- .. GENERATED FROM PYTHON SOURCE LINES 2342-2359 .. code-block:: Python img_name = "kodim22" img_orig = orig_images[img_name] cfg = IMAGE_CONFIG[img_name] zoom = float(cfg["zoom"]) roi_size_px = int(cfg["roi_size_px"]) roi_center_frac = tuple(map(float, cfg["roi_center_frac"])) # type: ignore[arg-type] bench_kodim22 = benchmark_image( img_name=img_name, gray=img_orig, zoom=zoom, roi_size_px=roi_size_px, roi_center_frac=roi_center_frac, degree_label="Cubic", ) .. rst-class:: sphx-glr-script-out .. code-block:: none === kodim22 | zoom=0.200 | shape=512×768 | ROI size≈256 px at center_frac=(0.5, 0.25) === Method Time (mean) ± SD SNR (dB) MSE SSIM --------------------------------------------------------------------------------------------- SplineOps Standard cubic 2.7 ms 0.0 ms 12.26 8.970e-03 0.4499 SplineOps Antialiasing cubic 5.1 ms 0.0 ms 14.70 5.118e-03 0.5099 OpenCV INTER_CUBIC 0.4 ms 0.0 ms 12.46 8.567e-03 0.4574 SciPy cubic 31.4 ms 0.1 ms 12.26 8.970e-03 0.4499 Pillow BICUBIC 2.8 ms 0.0 ms 14.49 5.370e-03 0.4823 scikit-image cubic 32.0 ms 0.1 ms 12.40 8.688e-03 0.4482 scikit-image cubic (AA) 38.1 ms 0.1 ms 14.40 5.482e-03 0.4728 PyTorch bicubic (CPU) 2.5 ms 0.0 ms 12.46 8.567e-03 0.4574 PyTorch bicubic (AA, CPU) 1.3 ms 0.0 ms 14.49 5.370e-03 0.4823 .. GENERATED FROM PYTHON SOURCE LINES 2360-2362 Original and Resized ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2362-2365 .. code-block:: Python _color_intro_for_image(img_name, bench_kodim22) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_046.png :alt: Original image with ROI (512×768 px), Original ROI (256×256 px, NN magnified), SplineOps Antialiasing cubic (zoom ×0.2, 102×154 px), Antialiasing ROI (51×51 px, NN magnified) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_046.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2366-2368 ROI Comparison ~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2368-2371 .. code-block:: Python show_roi_montage_main_from_bench(bench_kodim22) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_047.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_047.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2372-2374 ROI Comparison Error ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2374-2377 .. code-block:: Python show_error_montage_main_from_bench(bench_kodim22) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_048.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_048.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2378-2380 ROI Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2380-2383 .. code-block:: Python show_roi_montage_aa_from_bench(bench_kodim22) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_049.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_049.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2384-2386 ROI Comparison Error (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2386-2389 .. code-block:: Python show_error_montage_aa_from_bench(bench_kodim22) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_050.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_050.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2390-2392 ROI Color Comparison ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2392-2395 .. code-block:: Python show_roi_montage_color_main_from_bench(bench_kodim22, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_051.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_051.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2396-2398 ROI Color Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2398-2401 .. code-block:: Python show_roi_montage_color_aa_from_bench(bench_kodim22, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_052.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_052.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2402-2404 Time Comparison ~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2404-2407 .. code-block:: Python show_timing_plot_from_bench(bench_kodim22) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_053.png :alt: Timing vs Method (H×W = 512×768, zoom ×0.2, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_053.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2408-2410 SNR/SSIM Comparison ~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2410-2414 .. code-block:: Python show_snr_ssim_plot_from_bench(bench_kodim22) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_054.png :alt: SNR / SSIM vs Method (ROI = 256×256 px, zoom ×0.2, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_054.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2415-2417 Image: kodim23 -------------- .. GENERATED FROM PYTHON SOURCE LINES 2417-2434 .. code-block:: Python img_name = "kodim23" img_orig = orig_images[img_name] cfg = IMAGE_CONFIG[img_name] zoom = float(cfg["zoom"]) roi_size_px = int(cfg["roi_size_px"]) roi_center_frac = tuple(map(float, cfg["roi_center_frac"])) # type: ignore[arg-type] bench_kodim23 = benchmark_image( img_name=img_name, gray=img_orig, zoom=zoom, roi_size_px=roi_size_px, roi_center_frac=roi_center_frac, degree_label="Cubic", ) .. rst-class:: sphx-glr-script-out .. code-block:: none === kodim23 | zoom=0.150 | shape=512×768 | ROI size≈256 px at center_frac=(0.4, 0.65) === Method Time (mean) ± SD SNR (dB) MSE SSIM --------------------------------------------------------------------------------------------- SplineOps Standard cubic 2.5 ms 0.0 ms 16.45 4.817e-03 0.6700 SplineOps Antialiasing cubic 4.7 ms 0.1 ms 18.58 2.947e-03 0.7029 OpenCV INTER_CUBIC 0.4 ms 0.0 ms 16.39 4.885e-03 0.6758 SciPy cubic 31.5 ms 0.7 ms 16.45 4.817e-03 0.6700 Pillow BICUBIC 2.8 ms 0.0 ms 18.29 3.155e-03 0.6917 scikit-image cubic 31.6 ms 0.1 ms 16.39 4.878e-03 0.6714 scikit-image cubic (AA) 39.3 ms 0.1 ms 18.07 3.316e-03 0.6852 PyTorch bicubic (CPU) 2.3 ms 0.0 ms 16.39 4.885e-03 0.6758 PyTorch bicubic (AA, CPU) 1.3 ms 0.0 ms 18.29 3.155e-03 0.6917 .. GENERATED FROM PYTHON SOURCE LINES 2435-2437 Original and Resized ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2437-2440 .. code-block:: Python _color_intro_for_image(img_name, bench_kodim23) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_055.png :alt: Original image with ROI (512×768 px), Original ROI (256×256 px, NN magnified), SplineOps Antialiasing cubic (zoom ×0.15, 77×115 px), Antialiasing ROI (38×38 px, NN magnified) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_055.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2441-2443 ROI Comparison ~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2443-2446 .. code-block:: Python show_roi_montage_main_from_bench(bench_kodim23) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_056.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_056.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2447-2449 ROI Comparison Error ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2449-2452 .. code-block:: Python show_error_montage_main_from_bench(bench_kodim23) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_057.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_057.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2453-2455 ROI Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2455-2458 .. code-block:: Python show_roi_montage_aa_from_bench(bench_kodim23) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_058.png :alt: Original, SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_058.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2459-2461 ROI Comparison Error (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2461-2464 .. code-block:: Python show_error_montage_aa_from_bench(bench_kodim23) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_059.png :alt: Normalized signed difference in ROI, Original (no diff), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU), Diff legend :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_059.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2465-2467 ROI Color Comparison ~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2467-2470 .. code-block:: Python show_roi_montage_color_main_from_bench(bench_kodim23, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_060.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, OpenCV INTER_CUBIC, SciPy cubic, scikit-image cubic, PyTorch bicubic (CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_060.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2471-2473 ROI Color Comparison (Antialiased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2473-2476 .. code-block:: Python show_roi_montage_color_aa_from_bench(bench_kodim23, orig_images_rgb[img_name]) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_061.png :alt: Original (color), SplineOps Standard cubic, SplineOps Antialiasing cubic, Pillow BICUBIC, scikit-image cubic (AA), PyTorch bicubic (AA, CPU) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_061.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2477-2479 Time Comparison ~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2479-2482 .. code-block:: Python show_timing_plot_from_bench(bench_kodim23) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_062.png :alt: Timing vs Method (H×W = 512×768, zoom ×0.15, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_062.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2483-2485 SNR/SSIM Comparison ~~~~~~~~~~~~~~~~~~~ .. GENERATED FROM PYTHON SOURCE LINES 2485-2488 .. code-block:: Python show_snr_ssim_plot_from_bench(bench_kodim23) .. image-sg:: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_063.png :alt: SNR / SSIM vs Method (ROI = 256×256 px, zoom ×0.15, degree=Cubic) :srcset: /auto_examples/02_resize/images/sphx_glr_06_benchmarking_063.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 2489-2494 Runtime Context --------------- Finally, we print a short summary of the runtime environment and the storage dtype used for the benchmark. .. GENERATED FROM PYTHON SOURCE LINES 2494-2497 .. code-block:: Python if _HAS_SPECS and print_runtime_context is not None: print_runtime_context(include_threadpools=True) print(f"Benchmark storage dtype: {np.dtype(DTYPE).name}") .. rst-class:: sphx-glr-script-out .. code-block:: none Runtime context: Python : 3.12.12 (CPython) OS : Linux 6.11.0-1018-azure (x86_64) CPU : x86_64 | logical cores: 4 Process : pid=2422 | Python threads=1 NumPy/SciPy : 2.2.6/1.16.3 Matplotlib : 3.10.8 | backend: agg splineops : 1.2.1 | native ext present: True Extra libs : Pillow=12.0.0, OpenCV=4.12.0, scikit-image=0.25.2, PyTorch=2.9.1+cu128 SPLINEOPS_ACCEL=always OMP_NUM_THREADS=4 Benchmark storage dtype: float32 .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 53.155 seconds) .. _sphx_glr_download_auto_examples_02_resize_06_benchmarking.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: binder-badge .. image:: images/binder_badge_logo.svg :target: https://mybinder.org/v2/gh/splineops/splineops.github.io/main?urlpath=lab/tree/notebooks_binder/auto_examples/02_resize/06_benchmarking.ipynb :alt: Launch binder :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: 06_benchmarking.ipynb <06_benchmarking.ipynb>` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: 06_benchmarking.py <06_benchmarking.py>` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: 06_benchmarking.zip <06_benchmarking.zip>` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_