Zulko / moviepy

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

Adding Blend with pillow ops #2060

Open Goyet-Christopher opened 8 months ago

Goyet-Christopher commented 8 months ago

I'm using a modified version of the 'blit_on' function to use PIL's overlay modes (Channel Operations Module) between different video layers in CompositeVideoClip. I understand that this feature has been considered (added/removed) several times in the project. I hope you can consider this addition as valuable:

Modifications to the VideoClip.py file:

    @outplace
    def with_mode(self, mode, scale=1.0, offset=0):
        """Set the clip's layer mode in compositions.
        Using Blend Modes of Pillow images called ImagesChops ("Channel Operations").

        Note: Only has effect when the clip is used in a CompositeVideoClip.
        """
        mode_without_option=["normal", "add_modulo", "darker", "difference", "lighter",
        "logical_and", "logical_or", "logical_xor", "multiply", "soft_light", "hard_light",
        "overlay", "screen",  "substract_modulo"]
        mode_with_scale_offset=["add", "substract"]

        if isinstance(mode, str):
            if mode in mode_with_scale_offset:
                self.mode_scale = scale
                self.mode_offset = offset
                if self.mask is not None:
                    self.mask.mode_scale = scale
                    self.mask.mode_offset = offset
            else:
                if mode not in mode_without_option:
                    raise ValueError("Unknown mode in Pillow ImageChops")
            self.mode = mode
            if self.mask is not None:
                self.mask.mode = mode
        else:
            raise ValueError("Select mode into available Pillow ImageChops :", mode_without_option+mode_with_scale_offset)
def blit_on(self, picture, t):
        """Returns the result of the blit of the clip's frame at time `t`
        on the given `picture`, the position of the clip being given
        by the clip's ``pos`` attribute. Meant for compositing.
        """
.
.
.
.

        # Blend Channel Mode
        if self.mode not in ["normal"]:
            if picture.size != im_img.size:
                im_pic_bg = picture.crop((pos[0], pos[1], pos[0]+wi, pos[1]+hi))
            else:
                im_pic_bg = picture
            modeFct = getattr(ImageChops, self.mode)
            if self.mode in ["add", "substract"]:
                im_img = modeFct(im_pic_bg, im_img, scale=self.mode_scale, offset=self.mode_offset)
            else:
                im_img = modeFct(im_pic_bg, im_img)
        picture.paste(im_img, pos, im_mask)
        return picture

Example of use :

myvideo = VideoFileClip("clip.mp4")
txt = TextClip(text="Text", method="caption", font="Montserrat-Black-Italic", font_size=60, color="#76C0FF")
txt = txt.with_duration(myvideo.duration)\
         .with_position("center")\
         .with_opacity(1.0)\
         .with_mode("screen")
CVP = CompositeVideoClip([myvideo, txt])
CVP.write_videofile("withtxt.mp4", fps=25)

withtxt

Mode "add modulo": add_modulo

Mode "difference": difference

Mode "soft_light": soft_light

SohamTilekar commented 6 months ago

Tell the example usages.