from src.DataManager.base import BaseDataManager from src.DataManager.utils import imwrite_rgb import cv2 import numpy as np from pathlib import Path import shutil from typing import Optional, Union from moviepy.editor import AudioFileClip, VideoFileClip from moviepy.video.io.ImageSequenceClip import ImageSequenceClip class VideoDataManager(BaseDataManager): def __init__(self, src_data: Path, output_dir: Path, clean_work_dir: bool = False): self.video_handle: Optional[cv2.VideoCapture] = None self.audio_handle: Optional[AudioFileClip] = None self.output_dir = output_dir self.output_img_dir = output_dir / "img" self.output_dir.mkdir(exist_ok=True) self.output_img_dir.mkdir(exist_ok=True) self.video_name = None self.clean_work_dir = clean_work_dir if src_data.is_file(): self.video_name = "swap_" + src_data.name if VideoFileClip(str(src_data)).audio is not None: self.audio_handle = AudioFileClip(str(src_data)) self.video_handle = cv2.VideoCapture(str(src_data)) self.video_handle.set(cv2.CAP_PROP_POS_FRAMES, 0) self.frame_count = int(self.video_handle.get(cv2.CAP_PROP_FRAME_COUNT)) self.fps = self.video_handle.get(cv2.CAP_PROP_FPS) self.last_idx = -1 assert self.video_handle, "Video file must be specified!" def __len__(self): return self.frame_count def get(self) -> np.ndarray: img: Union[None, np.ndarray] = None while img is None and self.last_idx < self.frame_count: status, img = self.video_handle.read() self.last_idx += 1 if img is not None: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) return img def save(self, img: np.ndarray): filename = "frame_{:0>7d}.jpg".format(self.last_idx) imwrite_rgb(self.output_img_dir / filename, img) if (self.frame_count - 1) == self.last_idx: self._close() def _close(self): image_filenames = [str(x) for x in sorted(self.output_img_dir.glob("*.jpg"))] clip = ImageSequenceClip(image_filenames, fps=self.fps) if self.audio_handle is not None: clip = clip.set_audio(self.audio_handle) clip.write_videofile(str(self.output_dir / self.video_name)) if self.clean_work_dir: shutil.rmtree(self.output_img_dir, ignore_errors=True)