0

I am trying to accelerate and/or decelerate a movie clip with the help of Python's moviepy module, but I can't seem to work it out properly. The script runs quite smoothly, and without any errors, but I do not see any effects. Might be my script is wrong and I can't detect the problem. Looking for help/tips from you. I do not need a complete solution, any hints will be of great help. I have been working on this solution for sometime and I think I should post my problem here. Any help, tips, guidance will be greatly appreciated. Thank you.

from moviepy.editor import *
from moviepy.video.tools.drawing import color_split
import os

dir = os.path.split(os.path.realpath(__file__))[0]
img = os.path.join('tmp', 'stock.jpg')
folder = 'tmp'


def f_accel_decel(t, old_d, new_d, abruptness=1, soonness=1.0):
    """
    abruptness
      negative abruptness (>-1): speed up down up
      zero abruptness : no effect
      positive abruptness: speed down up down

    soonness
      for positive abruptness, determines how soon the
      speedup occurs (0<soonness < inf)
    """

    a = 1.0+abruptness
    def _f(t):
        f1 = lambda t: (0.5)**(1-a)*(t**a)
        f2 = lambda t: (1-f1(1-t))
        return (t<.5)*f1(t) + (t>=.5)*f2(t) 

    return old_d*_f((t/new_d)**soonness)

def accel_decel(clip, new_duration=None, abruptness=1.0, soonness=1.0):
    """
    new_duration
      If None, will be that of the current clip.
    abruptness
      negative abruptness (>-1): speed up down up
      zero abruptness : no effect
      positive abruptness: speed down up down

    soonness
      for positive abruptness, determines how soon the
      speedup occurs (0<soonness < inf)
    """

    if new_duration is None:
        new_duration = clip.duration

    fl = lambda t : f_accel_decel(t, clip.duration, new_duration,
                                   abruptness, soonness)

    return clip.fl_time(fl).set_duration(new_duration)



duration = 30

main_clip = ImageClip(img, duration=30)
W,H = main_clip.size
print W,H



clip1 = (main_clip
             .subclip(0,duration)
             .set_pos(lambda t:(max((0), (int(W-0.5*W*t))), "center"))
             )


modifiedClip1 = accel_decel(clip1, abruptness=5, soonness=1.3)


cc = CompositeVideoClip([modifiedClip1], size=(1920,1080), bg_color=(232,54,18)).resize(0.5)
cc.preview(fps=24)
#cc.write_videofile("mask.avi", fps=25, codec="libx264", bitrate="1000K", threads=3)
Anay Bose
  • 880
  • 1
  • 14
  • 24
  • Your code is relatively difficult to follow since it has no useful comments explaining what you're doing - perhaps you might want to spend some time making it clearer to understand. This also isn't really my domain, so maybe it's clear to someone who's familiar with this library. However, I do have a question - is it possible that you're only changing the duration of individual frames, and then ignoring that when you play the clip at a fixed frame rate with `cc.preview(fps=24)`? – Random Davis Oct 18 '16 at 15:46

1 Answers1

5

I think the best way of accelerating and decelerating clip objects is using easing functions.

Some reference sites:

Here's part of a script I made when trying to understand these functions. Maybe you can use some of this concepts to solve your issue.

from __future__ import division
from moviepy.editor import TextClip, CompositeVideoClip
import math


def efunc(x0, x1, dur, func='linear', **kwargs):
    # Return an easing function.
    # It will control a single dimention of the clip movement.
    # http://www.gizma.com/easing/

    def linear(t):
        return c*t/d + b

    def out_quad(t):
        t = t/d
        return -c * t*(t-2) + b

    def in_out_sine(t):
        return -c/2 * (math.cos(math.pi*t/d) - 1) + b

    def in_quint(t):
        t = t/d
        return c*t*t*t*t*t + b

    def in_out_circ(t):
        t /= d/2;
        if t < 1:
            return -c/2 * (math.sqrt(1 - t*t) - 1) + b
        t -= 2;
        return c/2 * (math.sqrt(1 - t*t) + 1) + b;

    def out_bounce(t):
        # http://gsgd.co.uk/sandbox/jquery/easing/jquery.easing.1.3.js
        t = t/d
        if t < 1/2.75:
            return c*(7.5625*t*t) + b
        elif t < 2/2.75:
            t -= 1.5/2.75
            return c*(7.5625*t*t + .75) + b
        elif t < 2.5/2.75:
            t -= 2.25/2.75
            return c*(7.5625*t*t + .9375) + b
        else:
            t -= 2.625/2.75
            return c*(7.5625*t*t + .984375) + b

    # Kept the (t, b, c, d) notation found everywhere.
    b = x0
    c = x1 - x0
    d = dur
    return locals()[func]


def particle(x0, x1, y0, y1, d, func='linear', color='black', **kwargs):
    # Dummy clip for testing.

    def pos(t):
        return efunc(x0, x1, d, func=func)(t), efunc(y0, y1, d, func=func)(t)

    return (
        TextClip('*', fontsize=80, color=color)
        .set_position(pos)
        .set_duration(d)
        )

# Make a gif to visualize the behaviour of the functions:

easing_functions = [
    ('linear', 'red'),
    ('in_out_sine', 'green'),
    ('in_out_circ', 'violet'),
    ('out_quad', 'blue'),
    ('out_bounce', 'brown'),
    ('in_quint', 'black'),
    ]

d = 4
x0, x1 = 0, 370
clips = []
for i, (func, c) in enumerate(easing_functions):
    y = 40*i
    clips.append(particle(x0, x1, y, y, d=d, func=func, color=c))
    clips.append(particle(x1, x0, y, y, d=d, func=func, color=c).set_start(d))

clip = CompositeVideoClip(clips, size=(400,250), bg_color=(255,255,255))
clip.write_gif('easing.gif', fps=12)


The output of the script:

Easing functions demo

feqwix
  • 1,362
  • 14
  • 16