Source code for dmtools.animation

import imageio
import numpy as np
from math import ceil
from typing import List
import logging
from .io import read, _discretize
from . import sound
from ._log import _log_msg
import os

[docs]def clip(path: str, start: int = 0, end: int = -1) -> List[np.ndarray]: """Return a list of images in the given directory. Images are ordered according to their name. Hence, the following naming convention is recommend. name0000.png, name0001.png, ... Args: path (str): String directory path. start (int, optional): Starting frame. Defaults to 0. end (int, optional): Ending frame. Defaults to -1. Returns: List[np.ndarray]: List of NumPy arrays representing images. """ def listdir_nohidden(path): for f in os.listdir(path): if not f.startswith('.'): yield f files = sorted(listdir_nohidden(path)) files = files[start:end] paths = ["%s/%s" % (path, f) for f in files] frames = [read(path) for path in paths] return frames
def _pad_to_16(M: np.ndarray) -> np.ndarray: # TODO: Get a better understanding why image demensions need to be # multiplies of 16. It appears this requirement is no longer from ffmpeg. # Adapted from code by: m,n,*_ = M.shape y_pad = (ceil(m/16)*16-m) y_pad_split = (y_pad // 2, y_pad // 2 + y_pad % 2) x_pad = (ceil(n/16)*16-n) x_pad_split = (x_pad // 2, x_pad // 2 + x_pad % 2) if len(M.shape) == 3: return np.pad(M, (y_pad_split, x_pad_split, (0, 0))) else: return np.pad(M, (y_pad_split, x_pad_split))
[docs]def to_mp4(frames: List[np.ndarray], path: str, fps: int, s: int = 1, audio: sound.WAV = None): """Write an animation as a .mp4 file using ffmpeg through imageio.mp4 Args: frames (List[np.ndarray]): List of frames in the animation. audio (sound.WAV): Audio for the animation (None if no audio). path (str): String file path. fps (int): Frames per second. s (int, optional): Multiplier for scaling. Defaults to 1. """ frames = [_discretize(f, 255).astype(np.uint8) for f in frames] frames = [_pad_to_16(f) for f in frames] imageio.mimwrite(uri="tmp.mp4" if audio is not None else path, ims=frames, format='FFMPEG', fps=fps, output_params=["-vf", "scale=iw*%d:ih*%d" % (s, s), "-sws_flags", "neighbor"]) if audio is not None: audio.to_wav("tmp.wav") os.system("ffmpeg -i %s -i %s -c:v copy -c:a aac -y %s" % ("tmp.mp4", "tmp.wav", path)) os.system("rm tmp.mp4") os.system("rm tmp.wav") name = path.split('/')[-1], os.stat(path).st_size))