Zulko / moviepy

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

Assigning an ImageClip as the mask to a VideoFileClip inverts colors of unmasked area #2120

Closed JasonChoate closed 4 months ago

JasonChoate commented 4 months ago

Expected Behavior

Creating an image mask using PIL, assigning it to an ImageClip, then assigning that ImageClip as the mask to a VideoFileClip should result in the VideoFileClip having a mask applied, hiding the masked part of the VideoFileClip.

Actual Behavior

Creating an image mask using PIL, assigning it to an ImageClip, then assigning that ImageClip as the mask to a VideoFileClip does result in the VideoFileClip being correctly masked in regards to the transparency, but also inverts the colors of the unmasked part of the VideoFileClip.

Steps to Reproduce the Problem

# process_video.py
from moviepy.editor import VideoFileClip, ImageClip, CompositeVideoClip
import numpy as np
from PIL import Image, ImageDraw

def round_corners(clip, radius):
    # Create a mask image with the same dimensions as the clip, filled with 255 (opaque)
    mask = Image.new("L", clip.size, "black")
    draw = ImageDraw.Draw(mask)

    # Dimensions of the clip
    w, h = clip.size

    # Draw four rectangles to cover the corners outside the rounded area
    draw.rectangle([radius, 0, w-radius, h], fill="white")
    draw.rectangle([0, radius, w, h-radius], fill="white")
    # Draw four circles at the corners for the rounded effect
    draw.ellipse([0, 0, 2*radius, 2*radius], fill="white")  # Top-left
    draw.ellipse([w-2*radius, 0, w, 2*radius], fill="white")  # Top-right
    draw.ellipse([0, h-2*radius, 2*radius, h], fill="white")  # Bottom-left
    draw.ellipse([w-2*radius, h-2*radius, w, h], fill="white")  # Bottom-right

    mask_clip = ImageClip(np.array(mask), ismask=True)

    # Apply the mask to the clip
    return clip.set_mask(mask_clip)

def process_video():
    # Load the outro video
    outro_path = 'example_file.mp4'
    outroClip = VideoFileClip(outro_path)

    outroClip = round_corners(outroClip, radius=50)

    # CompositeVideoClip because in my actual usecase, I'm compositing more stuff on top
    outroClip = CompositeVideoClip([outroClip])

    outroClip.preview()

Specifications

JasonChoate commented 4 months ago

I apologize for the confusion. After further investigation, I realized that the issue was on my end and have corrected it.

Having PIL create an "RGBA" image instead of single-channel greyscale "L" worked.

I'm unsure if this is intended behavior. If it is, please consider closing this issue. Thank you.

keikoro commented 4 months ago

If your issue isnt an issue anymore, please close it yourself! We don't have the capacity to follow up on potential non-issues; if it turns out to not be a non-issue, other users will still be able to find it via search, in any case.