Effects

Effects can be applied to layers by decorating layer methods within sequence classes. Layers can be combined with an arbitrary number of effects. From a functional point of view, effects post-process images generated by layer tasks. There is a set of readily available effects in bewegung. However, new custom effects can be easily added as well.

Available Effects

class bewegung.FadeInEffect(blend_time)

Fade-in effect. Decorator for layers.

Parameters

blend_time (TimeABC) – Duration of effect relative to the beginning of the parent sequence

class bewegung.FadeOutEffect(blend_time)

Fade-out effect. Decorator for layers.

Parameters

blend_time (TimeABC) – Duration of effect relative to the beginning of the parent sequence

Defining Custom Effects

Custom effects are implemented by deriving from bewegung.EffectBase. Once derived, they can be directly applied to layers.

The EffectBase Class

class bewegung.EffectBase

Base for all effect classes - not an effect on its own. Derive new effect classes from this class. Decorator for layers.

Mutable.

If the orginal cunstructor method is overridden, it must be called from the child class.

__call__(layer)

Decorator function, decorating bewegung.core.layer.Layer objects (wrapping user-defined layer methods). The effect is registered via bewegung.core.layer.Layer.register_effect() and returns the otherwise unchanged layer object.

Do not override!

Parameters

layer (LayerABC) – Layer to which the effect is applied.

Return type

LayerABC

apply(cvs)

Applies effect to image.

Must be reimplemented!

Parameters

cvs (Image) – Input Pillow Image object

apply_(cvs, video, sequence, time)

Internal interface for layer objects. Applies the effect to a Pillow Image object and returns the modified image. The function automatically determines what arguments the apply method of an actual effect requests other than cvs. Possible options are:

  • video: Parent video object

  • sequence: Parent sequence object

  • time: Time within parent video

  • reltime: Relative time within parent sequence

Do not override!

Parameters
  • cvs (Image) – Input Pillow Image object

  • video (VideoABC) – Parent video object

  • sequence (SequenceABC) – Parent sequence object

  • time (TimeABC) – Time within parent video

Return type

Image

Note

Similar to prepare task methods and layer task methods, the apply method in classes derived from bewegung.EffectBase can request parameters on demand. See bewegung.EffectBase.apply_() for details.

A Minimal Effect based on ImageFilter

The following example illustrates how to build a custom effect around a filter function from the ImageFilter module (part of pillow).

from PIL import Image, ImageFilter
from bewegung import Video, Vector2D, Color, EffectBase

class BlurEffect(EffectBase): # derive from EffectBase

    def __init__(self, radius: float):

        if radius < 0:
            raise ValueError('radius must be positive or zero')

        super().__init__()
        self._filter = ImageFilter.GaussianBlur(radius) # setup filter

    def apply(self, cvs: Image.Image) -> Image.Image: # request nothing but the image

        return cvs.filter(self._filter) # apply filter to image and return

v = Video(width = 480, height = 270, seconds = 1.0)

@v.sequence()
class Foo:

    @v.layer()
    def lineA(self, canvas): # layer without effect

        canvas.draw_polygon(
            Vector2D(5, 5), Vector2D(v.width - 5, v.height - 5),
            line_width = 3,
            line_color = Color(255, 0, 0, 255),
        )

        return canvas

    @BlurEffect(radius = 2.0) # decorate layer with filter
    @v.layer()
    def lineB(self, canvas): # layer with effect

        canvas.draw_polygon(
            Vector2D(v.width - 5, 5), Vector2D(5, v.height - 5),
            line_width = 3,
            line_color = Color(255, 0, 0, 255),
        )

        return canvas

v.reset()
v.render_frame(v.time(0))
Blur effect output

The bewegung.EffectBase.apply() method is basically the only method that must be re-implemented by a custom effect. However, the constructor of this class can optionally be re-implemented if required to add parameters to an effect decorator object. In the latter case, one must not forget to call the constructor of the original underlying class (super().__init__()).