"""FlashReFocus: interactive image refocusing (Hugging Face Space, ZeroGPU). 1. Restore: Stage I (one-step diffusion with detail weight omega and the decoder refiner) restores the all-in-focus image once, and Depth Anything V2 estimates its depth once. 2. Click the restored image (or the depth map) to focus; only the 1.3M-parameter renderer runs per edit. Modes: one focal plane; two focal planes (first click A, second click B); tilt-shift (a click sets the sharp row). 3. The aperture (blur strength K), the depth curve and an optional user depth map re-render the current edit. """ import time import gradio as gr import numpy as np import spaces from PIL import Image, ImageDraw, ImageFont import engine SINGLE, DUAL, TILT = "📍 One focal plane", "👥 Two focal planes", "🏙️ Tilt-shift" EXAMPLES = ["examples/portrait_city.jpg", "examples/portrait_aquarium.jpg", "examples/flower.jpg", "examples/dock.jpg", "examples/sculpture.jpg"] FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" try: ImageFont.truetype(FONT, 12) except OSError: FONT = None def fit(img, long_side): """Long side at most `long_side`, both sides multiples of 16.""" w, h = img.size s = min(1.0, long_side / max(w, h)) w2, h2 = max(16, int(w * s) // 16 * 16), max(16, int(h * s) // 16 * 16) return img.convert("RGB").resize((w2, h2), Image.LANCZOS) @spaces.GPU(duration=40) def gpu_restore(rgb, omega): t0 = time.perf_counter() aif, depth = engine.restore(rgb, omega) return aif, depth, time.perf_counter() - t0 @spaces.GPU(duration=10) def gpu_render(aif, depth, focal_depth, strength): t0 = time.perf_counter() out = engine.render(aif, depth, focal_depth, strength) return out, time.perf_counter() - t0 def depth_image(depth): return Image.fromarray((np.clip(1 - depth, 0, 1) * 255).astype(np.uint8)) # near is bright def scene_depth(state, gamma): """The depth the edits start from: the estimate (or the user's map), with the depth curve D^gamma applied.""" d = state["user_depth"] if state.get("user_depth") is not None else state["depth"] return np.power(np.clip(d, 0, 1), float(gamma)).astype(np.float32) def depth_at(depth, point): h, w = depth.shape return float(depth[min(max(point[1], 0), h - 1), min(max(point[0], 0), w - 1)]) def edit(state, mode, gamma, band): """(depth to render, focal depth D_f) for the current mode, or None when a focal point is still missing.""" d = scene_depth(state, gamma) h, w = d.shape if mode == TILT: return engine.tilt_shift(h, w, state.get("tilt_row", 0.5), float(band)), 0.5 pts = state["pts"] if not pts: return None if mode == DUAL and len(pts) == 2: da, db = depth_at(d, pts[0]), depth_at(d, pts[1]) return engine.two_focal_planes(d, da, db), da return d, depth_at(d, pts[0]) def restore(img, long_side, omega, gamma): if img is None: return None, None, None, None, "Upload an image or pick an example first." rgb = np.asarray(fit(img, int(long_side))) aif, depth, t = gpu_restore(rgb, float(omega)) state = {"aif": aif, "depth": depth, "user_depth": None, "pts": [], "tilt_row": 0.5} msg = (f"Restored {aif.shape[1]}×{aif.shape[0]} with ω = {float(omega):.2f} and estimated its depth in {t:.2f} s. " "**Click the restored image or the depth map to focus.**") return Image.fromarray(aif), depth_image(scene_depth(state, gamma)), None, state, msg def render(state, strength, mode, gamma, band, show_marker): """(refocused image, depth view, message).""" edited = edit(state, mode, gamma, band) if edited is None: return None, depth_image(scene_depth(state, gamma)), "Click the restored image or the depth map to focus." depth, focal_depth = edited out, dt = gpu_render(state["aif"], depth, focal_depth, float(strength)) out = Image.fromarray(out) if show_marker: out = draw_markers(out, state, mode) k = f"aperture K = {float(strength):.2f}" + (" (extrapolated)" if float(strength) > 1 else "") if mode == TILT: msg = f"Tilt-shift band at {state.get('tilt_row', 0.5):.0%} of the height, {k}: rendered in {dt:.2f} s." elif mode == DUAL and len(state["pts"]) == 2: msg = f"Two focal planes A {state['pts'][0]} and B {state['pts'][1]}, {k}: rendered in {dt:.2f} s." elif mode == DUAL: msg = f"Plane A {state['pts'][0]} set (rendered in {dt:.2f} s). Click a second point for plane B." else: msg = f"Focus {state['pts'][0]} (D_f = {focal_depth:.2f}), {k}: rendered in {dt:.2f} s." return out, depth_image(depth), msg def draw_markers(im, state, mode): """Focal points (A / B with two planes), or the sharp row of tilt-shift, on a 3x supersampled overlay.""" im = im.convert("RGBA"); S = 3 ov = Image.new("RGBA", (im.width * S, im.height * S), (0, 0, 0, 0)); d = ImageDraw.Draw(ov) r = max(9, round(min(im.size) * 0.022)) * S; lw = max(2, round(r / 6)) if mode == TILT: y = state.get("tilt_row", 0.5) * ov.height for x in range(0, ov.width, 12 * lw): d.line([x, y, x + 6 * lw, y], fill=(255, 255, 255, 220), width=lw) return Image.alpha_composite(im, ov.resize(im.size, Image.LANCZOS)).convert("RGB") font = ImageFont.truetype(FONT, round(r * 1.25)) if FONT else ImageFont.load_default() for i, (x, y) in enumerate(state["pts"]): cx, cy = x * S, y * S d.ellipse([cx - r - lw, cy - r - lw, cx + r + lw, cy + r + lw], outline=(0, 0, 0, 170), width=lw * 3) d.ellipse([cx - r, cy - r, cx + r, cy + r], outline=(255, 255, 255, 255), width=lw) dot = lw * 1.3 d.ellipse([cx - dot, cy - dot, cx + dot, cy + dot], fill=(255, 255, 255, 255)) if mode == DUAL: tx = cx + r + lw * 2 if cx + r * 3 < ov.width else cx - r * 2.2 d.text((tx, max(0, cy - r * 1.9)), "AB"[i], font=font, fill=(255, 255, 255, 255), stroke_width=max(2, lw), stroke_fill=(0, 0, 0, 200)) return Image.alpha_composite(im, ov.resize(im.size, Image.LANCZOS)).convert("RGB") def aif_view(state, mode): return draw_markers(Image.fromarray(state["aif"]), state, mode) def on_click(state, strength, mode, gamma, band, show_marker, evt: gr.SelectData): if not state: return None, None, None, state, "Restore an image first." p = (int(evt.index[0]), int(evt.index[1])) if mode == TILT: state = dict(state, tilt_row=p[1] / state["aif"].shape[0]) else: state = dict(state, pts=state["pts"] + [p] if mode == DUAL and len(state["pts"]) == 1 else [p]) out, depth_view, msg = render(state, strength, mode, gamma, band, show_marker) return aif_view(state, mode), out, depth_view, state, msg def on_change(state, strength, mode, gamma, band, show_marker): if not state: return gr.update(), None, gr.update(), state, "" if mode != DUAL and len(state["pts"]) > 1: state = dict(state, pts=state["pts"][:1]) out, depth_view, msg = render(state, strength, mode, gamma, band, show_marker) return aif_view(state, mode), out, depth_view, state, msg def on_user_depth(state, user_depth, strength, mode, gamma, band, show_marker): if not state: return gr.update(), None, gr.update(), state, "Restore an image first, then upload its depth map." h, w = state["aif"].shape[:2] state = dict(state, user_depth=None if user_depth is None else engine.depth_from_image(user_depth, h, w)) out, depth_view, msg = render(state, strength, mode, gamma, band, show_marker) note = "Using your depth map. " if user_depth is not None else "Using the estimated depth again. " return aif_view(state, mode), out, depth_view, state, note + msg def on_clear(state, gamma): if not state: return gr.update(), None, gr.update(), state, "" state = dict(state, pts=[], tilt_row=0.5) return (Image.fromarray(state["aif"]), None, depth_image(scene_depth(state, gamma)), state, "Focal points cleared. Click the restored image or the depth map to focus.") RECOMMENDED = {"long_side": 1024, "omega": 1.25, "gamma": 1.0, "band": 0.06, "strength": 0.5, "mode": SINGLE, "show_marker": False} def on_reset(state): """Recommended settings (the paper's defaults), the estimated depth again; focal points are kept.""" r = RECOMMENDED controls = [r["long_side"], r["omega"], r["gamma"], r["band"], r["strength"], r["mode"], r["show_marker"], None] if not state: return controls + [gr.update(), None, gr.update(), state, "Settings reset to the recommended values."] state = dict(state, user_depth=None, pts=state["pts"][:1]) out, depth_view, msg = render(state, r["strength"], r["mode"], r["gamma"], r["band"], r["show_marker"]) note = "Settings reset to the recommended values (ω applies on the next Restore). " return controls + [aif_view(state, r["mode"]), out, depth_view, state, note + msg] LOGO = """ """ HEADER = f"""
Interactive image refocusing in seconds: restore a photo once, then click to refocus, open the aperture, or keep two subjects sharp.