Source code for sign_language_translator.vision.video.display

from itertools import islice
from os.path import isfile
from time import sleep, time
from typing import Iterable
from warnings import warn

import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import Animation, ArtistAnimation
from numpy import uint8
from numpy.typing import NDArray

from sign_language_translator.utils import in_jupyter_notebook


[docs] class VideoDisplay:
[docs] @staticmethod def display_ipython_video_in_jupyter( path: str, html_attributes="loop autoplay controls preload muted", width=640, height=480, ) -> None: if not in_jupyter_notebook(): warn(f"Attempted to use IPython.core.display.Video outside jupyter: {path}") return from IPython.display import Video, display # type: ignore if isfile(path): display( Video(path, width=width, height=height, html_attributes=html_attributes) ) else: raise FileNotFoundError(f"No video file found at {path}")
[docs] @staticmethod def frames_to_matplotlib_animation( frames: Iterable[NDArray[uint8]], fps: float = 30.0, close_plot=True ) -> Animation: fig, ax = plt.subplots() fig.set_facecolor("black") ax.axis("off") fig.tight_layout(pad=0) ims = [] for frame in frames: im = ax.imshow(frame, animated=True) ims.append([im]) animation = ArtistAnimation(fig, ims, interval=(1000 / fps), blit=True) if close_plot: plt.close() return animation
[docs] @staticmethod def display_frames( frames: Iterable[NDArray[uint8]], fps: float = 30.0, inline_player="jshtml" or "html5", max_frames=None, ) -> None: if isinstance(frames, np.ndarray) and frames.ndim == 3: frames = [frames] if max_frames is not None: frames = islice(frames, max_frames) if in_jupyter_notebook(): if (hasattr(frames, "__len__") and len(frames) == 1) or max_frames == 1: # type: ignore VideoDisplay.display_frames_as_ipython_image( [next(iter(frames))], fps=float("inf") ) else: from IPython.display import HTML, display # type: ignore animation = VideoDisplay.frames_to_matplotlib_animation( frames, fps, close_plot=True ) html = ( animation.to_html5_video() if inline_player == "html5" else animation.to_jshtml() ) display(HTML(html)) else: if (hasattr(frames, "__len__") and len(frames) == 1) or max_frames == 1: # type: ignore VideoDisplay.show_image_plot(next(iter(frames))) # type: ignore else: # TODO: Improve fps of the video player. # TODO: Add seek bar to the video player. animation = VideoDisplay.frames_to_matplotlib_animation( frames, fps, close_plot=False ) plt.show(block=False) # TODO: window name plt.pause((len(frames) / fps) * 1.02 if hasattr(frames, "__len__") else 10) # type: ignore plt.close()
[docs] @staticmethod def display_frames_as_ipython_image( frames: Iterable[NDArray[uint8]], fps: float = 30.0 ): # BUG: randomly prints None or shows Image from IPython.display import Image, display # type: ignore display_handle = display(None, display_id=True) for frame in frames: start_time = time() # RGB to BGR because opencv is weird _, frame = cv2.imencode(".jpeg", cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) display_handle.update(Image(data=frame.tobytes())) # type: ignore elapsed_time = (start_time - time()) if start_time is not None else 0 sleep(max(0, 1 / fps - elapsed_time))
[docs] @staticmethod def show_image_plot(frame: NDArray[uint8]) -> None: plt.figure(facecolor="black") plt.axis("off") plt.tight_layout(pad=0) plt.imshow(frame)