"""Before/after slider and point-cloud viewer for the demo, rendered as plain HTML. Gradio does not run scripts inside ``gr.HTML``, so the elements carry their data inline and ``HEAD_JS`` (passed to ``gr.Blocks(head=...)``) wires up every ``.iris-ba`` / ``.iris-cloud`` element as it appears. """ import base64 import io import numpy as np from PIL import Image HFOV_DEG = 60 # assumed horizontal field of view for back-projection MAX_POINTS = 80_000 def _data_uri(image: Image.Image, quality: int = 92) -> str: buf = io.BytesIO() image.convert("RGB").save(buf, "WEBP", quality=quality) return "data:image/webp;base64," + base64.b64encode(buf.getvalue()).decode() def slider_html(before: Image.Image, after: Image.Image, before_label: str, after_label: str, download: str | None = None) -> str: """``download`` (a file name) adds a button in the top-right corner that saves the *after* image, the same affordance the Generate tab's gr.Image has.""" before = before.convert("RGB").resize(after.size, Image.Resampling.BICUBIC) button = "" if download: button = ( f'' ) return ( f'
' f'{after_label}' f'{before_label}' f'{button}
↔
' f'{before_label}' f'{after_label}
' ) def cloud_html(depth: np.ndarray, image: Image.Image) -> str: """Back-project relative log depth to a coloured point cloud. The prediction is log depth up to scale and shift, so ``exp`` of it gives depth up to scale (shift in log space is a global scale); it is rescaled to a median of 3 for viewing. """ h, w = depth.shape rgb = np.asarray(image.convert("RGB").resize((w, h), Image.Resampling.BILINEAR)) stride = max(1, int(np.ceil(np.sqrt(h * w / MAX_POINTS)))) v, u = np.mgrid[0:h:stride, 0:w:stride] z = np.exp(depth[v, u].astype(np.float64)) z = 3 * z / np.median(z) f = (w / 2) / np.tan(np.radians(HFOV_DEG) / 2) xyz = np.stack([(u + 0.5 - w / 2) * z / f, (v + 0.5 - h / 2) * z / f, z], -1).reshape(-1, 3) payload = xyz.astype("') CSS = """ .iris-ba { position: relative; width: 100%; max-height: 70vh; margin: 0 auto; cursor: ew-resize; touch-action: none; user-select: none; overflow: hidden; background: #0c0c0c; } .iris-ba img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: block; } .iris-ba-line { position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: #fff; z-index: 3; } .iris-ba-line span { position: absolute; top: 50%; left: 50%; width: 40px; height: 40px; transform: translate(-50%, -50%); display: flex; align-items: center; justify-content: center; border-radius: 999px; background: #fff; color: #000; font: 12px 'IBM Plex Mono', monospace; box-shadow: 0 4px 14px rgba(0,0,0,.35); } .iris-ba-label { position: absolute; bottom: 16px; z-index: 2; pointer-events: none; border-radius: 4px; background: rgba(0,0,0,.6); padding: 6px 10px; color: #fff; backdrop-filter: blur(4px); font: 10px/1 'IBM Plex Mono', monospace; text-transform: uppercase; letter-spacing: .12em; } /* a copy of the image toolbar download button gradio draws on the Generate tab's result (gradio Image .top-panel / .icon-button): same box, same CSS variables, so it follows light/dark exactly like the native button — white with a hairline border in light, black in dark */ .iris-ba-dl { position: absolute; top: 0; right: 0; z-index: 20; display: flex; align-items: center; justify-content: center; box-sizing: border-box; width: 22px; height: 23px; padding: 1px; border: 0; border-left: 1px solid var(--border-color-primary); border-bottom: 1px solid var(--border-color-primary); border-radius: 0; background: var(--block-background-fill); color: var(--block-label-text-color); cursor: pointer; } .iris-ba-dl:hover { background: var(--background-fill-secondary); } .iris-ba-dl svg { display: block; width: 14px; height: 14px; } .iris-cloud { position: relative; width: 100%; aspect-ratio: 4 / 3; cursor: grab; touch-action: none; overflow: hidden; border: 1px solid var(--sl-rule); margin-top: 12px; } .iris-cloud:active { cursor: grabbing; } .iris-cloud canvas { display: block; width: 100%; height: 100%; } """ HEAD_JS = """ """