Skip to content

Instantly share code, notes, and snippets.

@uwezi
Last active April 30, 2024 11:27
Show Gist options
  • Save uwezi/faec101ed5d7c20222b33eee4b6c7d63 to your computer and use it in GitHub Desktop.
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
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