Source code for visionsim.interpolate

from __future__ import annotations

import json
import os
from functools import partial
from pathlib import Path
from typing import Literal

import numpy as np
from natsort import natsorted
from rich.progress import Progress

from .pose import pose_interp  # noqa: F401
from .rife.inference_img import interpolate_img as rife  # noqa: F401


[docs] def interpolate_poses(transforms, normalize: bool = False, n: int = 2, k: int = 3): """Interpolate between pose matrices Args: transforms: List of pose matrices to interpolate between normalize (bool): Whether the interpolation should be normalized or not n (int): Number of poses to interpolate between existing poses k (int): Order of spline interpolation, see :class:`pose_interp <visionsim.interpolate.pose.pose_interp>` :return: List of interpolated poses """ # Process from frames folder frames = natsorted(transforms["frames"], key=lambda f: f["file_path"]) # Run the pose interpolation num_frames = len(transforms["frames"]) indices = np.linspace(0, num_frames - 1, num_frames) interp_indices = np.linspace(0, num_frames - 1, n * num_frames - (n - 1)) pose_spline = pose_interp([f["transform_matrix"] for f in frames], ts=indices, normalize=normalize, k=k) new_poses = pose_spline(interp_indices) return new_poses
[docs] def interpolate_frames( input_dir: str | os.PathLike, output_dir: str | os.PathLike, interpolation_method: Literal["rife"] = "rife", n: int = 2, ): """Interpolate between image frames Note: Currently only RIFE is supported for frame interpolation but we intend to add more Args: input_dir (pathlib.Path): Path to directory containing images to interpolate output_dir (pathlib.Path): Path to directory to output interpolated frames interpolation_method (str): The image interpolation method used n (int): Number of frames to interpolate between existing frames """ if n < 2 or not n & (n - 1) == 0: raise ValueError(f"Can only interpolate by a power of 2, greater or equal to 2, not {n}.") input_dir = Path(input_dir).resolve() output_dir = Path(output_dir).resolve() output_dir.mkdir(parents=True, exist_ok=True) # NOTE: We only process pngs right now img_paths = [str(p) for p in input_dir.glob("frames/*.png")] # Route request to proper image interpolation method with Progress() as progress: if interpolation_method.lower() == "rife": task = progress.add_task("Interpolating with rife...") rife(img_paths, output_dir / "frames", exp=np.log2(n).astype(int), update_fn=partial(progress.update, task)) else: raise NotImplementedError("Requested interpolation method is not supported at this time.")
[docs] def poses_and_frames_to_json(transforms, new_poses, output_dir: str | os.PathLike, file_name: str = "transforms.json"): """Combines interpolated poses (matrices) and frames into a new transforms.json file Args: transforms: Original transforms JSON file new_poses: List of interpolated pose matrices output_dir (pathlib.Path): Path to directory containing interpolated frames file_name: Name of new transforms JSON file """ output_dir = Path(output_dir).resolve() output_dir.mkdir(parents=True, exist_ok=True) # NOTE: We only output png files in frames dir new_paths = natsorted(output_dir.glob("frames/*.png")) if len(new_paths) != len(new_poses): raise RuntimeError(f"Image and pose mismatch! Found {len(new_poses)} new poses and {len(new_paths)} new images.") new_frames = [ {"file_path": str(path.relative_to(output_dir)), "transform_matrix": pose.tolist()} for path, pose in zip(new_paths, new_poses) ] transforms["frames"] = new_frames with (output_dir / file_name).open("w") as f: json.dump(transforms, f, indent=2)