Zulko / moviepy

Video editing with Python
https://zulko.github.io/moviepy/
MIT License
12.36k stars 1.55k forks source link

Masking fail when using VideoClip.subclip() #2215

Open feicccccccc opened 3 weeks ago

feicccccccc commented 3 weeks ago

Hi All, I am trying to create a custom class for VideoClip to generate some animation and txt effects. I notice that once I create a subclip on my custom class, the masking fail.

To be exact:

import gizeh

from moviepy.editor import *
from moviepy.config import change_settings

from PIL import Image, ImageDraw, ImageFont
import numpy as np

# Use local ffmpeg
change_settings({"FFMPEG_BINARY":"ffmpeg"})

start_time = '00:10'
end_time = '00:12'
duration = 10
ncircles = 20
W,H = 1280, 720

# Demo video
def make_frame(t):
    surface = gizeh.Surface(W,H)
    for i in range(ncircles):
        angle = 2*np.pi*(1.0*i/ncircles+t/duration)
        center = W*( 0.5+ gizeh.polar2cart(0.1,angle))
        circle = gizeh.circle(r= W*(1.0-1.0*i/ncircles),
                              xy= center, fill= (i%2,i%2,i%2))
        circle.draw(surface)

    return surface.get_npimage()

class CustomClip(VideoClip):
    def __init__(self, size, font_path='font/TangYuan.ttf'):

        self.size = size
        self.font = ImageFont.truetype(font_path, 40)

        # Call the parent constructor. Duration must be set or alpha does not work
        VideoClip.__init__(self, duration=10)

    def make_frame(self, t):
        img = Image.new('RGBA', self.size)
        draw = ImageDraw.Draw(img)

        txt = f"Current sec: {t}"

        draw.text(
            (10,120),
            txt,
            fill='blue',
            font=self.font,
            stroke_width=4,
            stroke_fill='white',
            align='center',
        )

        np_img = np.array(img)
        self.mask = ImageClip(1.0 * np_img[:, :, 3] / 255, ismask=True)

        return np_img[:, :, :3]

clip = VideoClip(make_frame, duration=duration).subclip(start_time, end_time)

# subclip does not work
# custom_clip = CustomClip(size=video.size)
custom_clip = CustomClip(size=clip.size).subclip(start_time, end_time)

result = CompositeVideoClip([clip, custom_clip])

result.write_videofile("assets/test.mp4", fps=30, codec='h264_nvenc', bitrate="5M")

Expected Behavior

image

Actual Behavior

image

Steps to Reproduce the Problem

Specifications

feicccccccc commented 3 weeks ago

This is quite difficult to debug because of the decorator outplace.

While inside the CompositeVideoClip, the CustomClip object is at

image

The same class call self.get_frame(ct) and then call self.make_frame(t), it call some lamda, which is a different CustomClip object.

image

That why the mask is not updated since they are different objects.