Skip to content

Instantly share code, notes, and snippets.

@uwezi
Last active November 6, 2024 10:59
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 # needs opencv-python https://pypi.org/project/opencv-python/
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:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # needed here?
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)
@uwezi
Copy link
Author

uwezi commented Nov 3, 2024

How to play a GIF with alpha channel? I have no experience with OpenCV and I am a newbie in Manim.

For GIF images I have another code using PIL rather than OpenCV:
https://gist.github.com/uwezi/87a65edf71adbe7a6767a67f01434625/revisions
it should handle transparent GIF images just find.

Even though my individual GISTs don't have individual licenses attached you can see all my stuff on Github as MIT licensed
https://github.com/uwezi/manim-related?tab=MIT-1-ov-file#readme

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment