Last active
April 30, 2024 11:27
-
-
Save uwezi/faec101ed5d7c20222b33eee4b6c7d63 to your computer and use it in GitHub Desktop.
[video inclusion in Manim] Include video objects picture-in-picture. #manim #animate #video #opencv #videomobject
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cv2 | |
from PIL import Image, ImageOps | |
from dataclasses import dataclass | |
@dataclass | |
class VideoStatus: | |
time: float = 0 | |
videoObject: cv2.VideoCapture = None | |
def __deepcopy__(self, memo): | |
return self | |
class VideoMobject(ImageMobject): | |
''' | |
Following a discussion on Discord about animated GIF images. | |
Modified for videos | |
Parameters | |
---------- | |
filename | |
the filename of the video file | |
imageops | |
(optional) possibility to include a PIL.ImageOps operation, e.g. | |
PIL.ImageOps.mirror | |
speed | |
(optional) speed-up/slow-down the playback | |
loop | |
(optional) replay the video from the start in an endless loop | |
https://discord.com/channels/581738731934056449/1126245755607339250/1126245755607339250 | |
2023-07-06 Uwe Zimmermann & Abulafia | |
2024-03-09 Uwe Zimmermann | |
''' | |
def __init__(self, filename=None, imageops=None, speed=1.0, loop=False, **kwargs): | |
self.filename = filename | |
self.imageops = imageops | |
self.speed = speed | |
self.loop = loop | |
self._id = id(self) | |
self.status = VideoStatus() | |
self.status.videoObject = cv2.VideoCapture(filename) | |
self.status.videoObject.set(cv2.CAP_PROP_POS_FRAMES, 1) | |
ret, frame = self.status.videoObject.read() | |
if ret: | |
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
img = Image.fromarray(frame) | |
if imageops != None: | |
img = imageops(img) | |
else: | |
img = Image.fromarray(np.uint8([[63, 0, 0, 0], | |
[0, 127, 0, 0], | |
[0, 0, 191, 0], | |
[0, 0, 0, 255] | |
])) | |
super().__init__(img, **kwargs) | |
if ret: | |
self.add_updater(self.videoUpdater) | |
def videoUpdater(self, mobj, dt): | |
if dt == 0: | |
return | |
status = self.status | |
status.time += 1000*dt*mobj.speed | |
self.status.videoObject.set(cv2.CAP_PROP_POS_MSEC, status.time) | |
ret, frame = self.status.videoObject.read() | |
if (ret == False) and self.loop: | |
status.time = 0 | |
self.status.videoObject.set(cv2.CAP_PROP_POS_MSEC, status.time) | |
ret, frame = self.status.videoObject.read() | |
if ret: | |
img = Image.fromarray(frame) | |
if mobj.imageops != None: | |
img = mobj.imageops(img) | |
mobj.pixel_array = change_to_rgba_array( | |
np.asarray(img), mobj.pixel_array_dtype | |
) | |
class test(Scene): | |
def construct(self): | |
video1 = VideoMobject( | |
filename=r"D:\Programming\Python\manim\discord\media\videos\20240304_01\480p15\Countdown.mp4", | |
speed=1.0 | |
).scale_to_fit_width(5).to_corner(UL) | |
video2 = VideoMobject( | |
filename=r"D:\Programming\Python\manim\discord\media\videos\20240304_01\480p15\Countdown.mp4", | |
speed=3.0, | |
loop=True, | |
imageops=ImageOps.mirror | |
).scale_to_fit_width(5).to_corner(UR) | |
v1 = Group(video1, SurroundingRectangle(video1)) | |
v2 = Group(video2, SurroundingRectangle(video2)) | |
self.add(v1,v2) | |
self.wait(2) | |
self.play(v2.animate.shift(3*DL), run_time=6) | |
self.wait(2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment