Spaces:
Running on Zero
Running on Zero
File size: 2,069 Bytes
fcc874d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | """Render the zoom levels into one continuous zoom clip.
Each segment pushes in on level i until its frame is exactly the blurry crop that level i+1
was made from, then holds still and cross-fades blurry into sharp. The hold is the point:
it is where the viewer sees what the model actually added.
"""
import tempfile
import imageio.v2 as imageio
import numpy as np
from PIL import Image
from geometry import window_rect
FPS = 30
PUSH_FRAMES = 30
FADE_FRAMES = 14
TAIL_FRAMES = 24
def _ease(t):
return t * t * (3 - 2 * t)
def _lerp_rect(a, b, t):
return tuple(round(p + (q - p) * t) for p, q in zip(a, b))
def _frames(levels, upscale, center):
size = levels[0].size
full = (0, 0, size[0], size[1])
for i in range(len(levels) - 1):
target = window_rect(size, upscale, center)
for f in range(PUSH_FRAMES):
# f / (PUSH_FRAMES - 1), not f / PUSH_FRAMES: the last frame has to land exactly on
# `target`, which is the crop the next level was built from. Otherwise it jumps.
rect = _lerp_rect(full, target, _ease(f / (PUSH_FRAMES - 1)))
yield levels[i].crop(rect).resize(size, Image.BICUBIC)
blurry = np.asarray(levels[i].crop(target).resize(size, Image.BICUBIC), dtype=np.float32)
sharp = np.asarray(levels[i + 1], dtype=np.float32)
for f in range(FADE_FRAMES):
a = _ease((f + 1) / FADE_FRAMES)
yield Image.fromarray((blurry * (1 - a) + sharp * a).astype(np.uint8))
for _ in range(TAIL_FRAMES):
yield levels[-1]
def render(levels, upscale=4, center=(0.5, 0.5)):
"""Write an mp4 of the zoom and return its path. Needs at least two levels."""
if len(levels) < 2:
return None
path = tempfile.mkstemp(suffix=".mp4")[1]
with imageio.get_writer(path, fps=FPS, codec="libx264", quality=8,
pixelformat="yuv420p", macro_block_size=1) as out:
for frame in _frames(levels, upscale, center):
out.append_data(np.asarray(frame.convert("RGB")))
return path
|