Source code for veditor.utils.video_utils

# coding: utf-8
import math
import os
from typing import List, Optional, Tuple, Union

import cv2
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from tqdm import tqdm

from ._colorings import toGREEN
from .generic_utils import now_str


[docs]def createVideoWritor( H: int, W: int, fps: float, codec: str = "avc1", out_path: Optional[str] = None ) -> Tuple[cv2.VideoWriter, str]: """Create an instance of ``cv2.VideoWritor``. Args: H (int) : Height of the output video. W (int) : Width of the output video. fps (float) : Frame rate of the output video. codec (str, optional) : Video codec for the output video. Defaults to ``"avc1"``. out_path (Optional[str], optional) : [description]. Defaults to ``None``. Returns: Tuple[cv2.VideoWriter, str]: Tuple of ``cv2.VideoWriter`` and path to output video. """ if out_path is None: out_path = now_str() + ".mp4" out = cv2.VideoWriter(out_path, fourcc, fps, (W, H)) return (out, out_path)
[docs]def capture2writor( cap: cv2.VideoCapture, out_path: Optional[str] = None, codec: str = "avc1", H: Optional[int] = None, W: Optional[int] = None, fps: Optional[float] = None, ) -> Tuple[cv2.VideoWriter, str]: """Create a suitable ``cv2.VideoWriter`` for input ``cv2.VideoCapture``. Args: cap (cv2.VideoCapture) : An instance of ``cv2.VideoCaputure``. out_path (Optional[str], optional) : Path to the output video. Defaults to ``None``. codec (str, optional) : Video codec for the output video. Defaults to ``"avc1"``. H (Optional[int], optional) : Height of the output video. Defaults to ``None``. W (Optional[int], optional) : Width of the output video. Defaults to ``None``. fps (Optional[float], optional) : Frame rate of the output video. Defaults to ``None``. Returns: Tuple[str,cv2.VideoWriter]: Tuple of ``cv2.VideoWriter`` and path to output video. Examples: >>> import cv2 >>> from veditor.utils import capture2writor, SampleData >>> cap = cv2.VideoCapture(SampleData().VIDEO_PATH) >>> out, out_path = capture2writor(cap) >>> isinstance(out, cv2.VideoWriter) and isinstance(out_path, str) True """ return createVideoWritor( H=H or int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)), W=W or int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), fps=fps or cap.get(cv2.CAP_PROP_FPS), fourcc=cv2.VideoWriter_fourcc(*codec), out_path=out_path, )
[docs]def show_frames( video: Union[str, cv2.VideoCapture], start: int = 0, end: Optional[int] = None, step: int = 1, ncols: int = 6, nframes: Optional[int] = None, figsize: Optional[Tuple[int, int]] = None, fig: Optional[Figure] = None, ) -> Figure: """Cut out frames from the ``video`` and plot them. Args: video (Union[str, cv2.VideoCapture]) : Path to video or an instance of ``cv2.VideoCaputure``. start (int, optional) : Draw subsequent frames from ``start``. Defaults to ``0``. end (Optional[int], optional) : Draw up to ``end``-th frame. If not specified, draw to the end. Defaults to ``None``. ncols (int, optional) : Number of images lined up side by side (number of columns). Defaults to ``6``. figsize (Optional[Tuple[int, int]], optional) : Size of one image. Defaults to ``None``. fig (Optional[Figure], optional) : Figure instance you want to draw in. Defaults to ``None``. Returns: Figure: Figure where frames from ``start`` to ``end`` are drawn. Raises: ValueError: When ``video`` is ``cv2.VideoCapture`` and is not Opened. .. plot:: :class: popup-img >>> from veditor.utils import show_frames, SampleData >>> fig = show_frames(video=SampleData().VIDEO_PATH, step=300, ncols=2) >>> fig.show() """ if isinstance(video, cv2.VideoCapture): if not video.isOpened(): raise ValueError( f"{toGREEN('video')} is not opened. Please reinitialize the {toGREEN('cv2.VideoCapture')} instance." ) cap = video else: cap = cv2.VideoCapture(video) count = nframes or int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) digit = len(str(count)) end = min(end or count, count) nfigs = math.ceil((end - start + 1) / step) nrows = (nfigs - 1) // ncols + 1 # Calculate the appropriate figure size. if figsize is None: w = cap.get(cv2.CAP_PROP_FRAME_WIDTH) h = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) if w < h: figsize = (4, 4 * (h / w)) else: figsize = (4 * (w / h), 4) if fig is None: fig = plt.figure(figsize=(int(figsize[0] * ncols), int(figsize[1] * nrows))) counter = 0 for i in tqdm(range(count), desc=f"step:{step}"): ret, frame = cap.read() if (not ret) or (frame is None): break if start <= i <= end: if counter % step == 0: msec = cap.get(cv2.CAP_PROP_POS_MSEC) ax = fig.add_subplot(nrows, ncols, (i - start) // step + 1) ax.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) ax.axis("off") ax.set_title(f"No.{i:>0{digit}}/{count}\n{msec/1000:.2f}[s]") counter += 1 elif i > end: break cap.release() return fig
[docs]def save_frames( video: Union[str, cv2.VideoCapture], positions: Union[int, List[int]], fmt: str = "{pos}.png", ) -> None: """Cut out frames from the ``video`` and save them. Args: video (Union[str, cv2.VideoCapture]) : Path to video or an instance of ``cv2.VideoCaputure``. positions (Union[int, List[int]]) : Which position(s) to save the frame. fmt (str, optional) : File name format. Must include ``"{pos}"``. Defaults to ``"{pos}.png"``. Raises: ValueError: When ``video`` is ``cv2.VideoCapture`` and is not Opened, or ``fmt`` DO NOT include ``"{pos}"``. """ if isinstance(video, cv2.VideoCapture): if not video.isOpened(): raise ValueError( f"{toGREEN('video')} is not opened. Please reinitialize the {toGREEN('cv2.VideoCapture')} instance." ) cap = video else: cap = cv2.VideoCapture(video) if isinstance(positions, int): positions = [positions] if "{pos}" not in fmt: raise ValueError(f"{toGREEN('fmt')} must include " + '"{pos}"') max_pos = max(positions) counter = 0 with tqdm(range(max_pos + 1)) as pbar: for i in pbar: pbar.set_description(f"saved {counter} frames") ret, frame = cap.read() if (not ret) or (frame is None): break if i in positions: cv2.imwrite(fmt.format(pos=i), frame) counter += 1 cap.release()
[docs]def vcodec2ext(*codec) -> str: """Convert video codec to video extension. Args: codec (Union[tuple, str]) : Video Codec. Returns: str: Ideal file extension. Examples: >>> from pycharmers.opencv import vcodec2ext >>> vcodec2ext("MP4V") '.mp4' >>> vcodec2ext("mp4v") '.mov' >>> vcodec2ext("VP80") '.webm' >>> vcodec2ext("XVID") '.avi >>> vcodec2ext("☺️") '.mp4' Raises: KeyError: When the file extension cannot be inferred from the video ``codec``. """ if len(codec) == 1 and isinstance(codec[0], str): codec = codec[0] else: codec = "".join(codec) codec2ext = { "VP80": ".webm", "MP4S": ".mp4", "MP4V": ".mp4", "mp4v": ".mov", "H264": ".mp4", "X264": ".mp4", "DIV3": ".avi", "DIVX": ".avi", "IYUV": ".avi", "MJPG": ".avi", "XVID": ".avi", "THEO": ".ogg", "H263": ".wmv", "avc1": ".mp4", } handleKeyError(lst=list(codec2ext.keys()), codec=codec) return codec2ext[codec]