Turk Posted January 26, 2014 Share Posted January 26, 2014 By Zulko •Jan 23rd, 2014Sometimes producing a good animated GIF requires a few advanced tweaks, for which scripting can help. So I added a GIF export feature to MoviePy, a Python package originally written for video editing.For this demo we will make a few GIFs out of this trailer:Converting a video excerpt into a GIFIn what follows we import MoviePy, we open the video file, we select the part between 1’22.65 (1 minute 22.65 seconds) and 1’23.2, reduce its size (to 30% of the original) and save it as a GIF:1 from moviepy.editor import *23 VideoFileClip("./frozen_trailer.mp4").\4 subclip((1,22.65),(1,23.2)).\5 resize(0.3).\6 to_gif("use_your_head.gif")Cropping the imageFor my next GIF I will only keep the center of the screen. If you intend to use MoviePy, note that you can preview a clip with clip.preview(). During the preview clicking on a pixel will print its position, which is convenient for cropping with precision.1 kris_sven = VideoFileClip("./frozen_trailer.mp4").\2 subclip((1,13.4),(1,13.9)).\3 resize(0.5).\4 crop(x1=145,x2=400).\ # remove left-right borders5 to_gif("kris_sven.gif")Freezing a regionMany GIF makers like to freeze some parts of the GIF to reduce the file size and/or focus the attention on one part of the animation.In the next GIF we freeze the left part of the clip. To do so we take a snapshot of the clip at t=0.2 seconds, we crop this snapshot to only keep the left half, then we make a composite clip which superimposes the cropped snapshot on the original clip:1 anna_olaf = VideoFileClip("./frozen_trailer.mp4").\2 subclip(87.9,88.1).\3 speedx(0.5).\ # Play at half speed4 resize(.4)56 snapshot = anna_olaf.\7 crop(x2= anna_olaf.w/2).\ # remove right half8 to_ImageClip(0.2).\ # snapshot of the clip at t=0.2s9 set_duration(anna_olaf.duration)1011 CompositeVideoClip([anna_olaf, snapshot]).\12 to_gif('anna_olaf.gif', fps=15)Freezing a more complicated regionThis time we will apply a custom mask to the snapshot to specify where it will be transparent (and let the animated part appear) .1 import moviepy.video.tools.drawing as dw23 anna_kris = VideoFileClip("./frozen_trailer.mp4", audio=False).\4 subclip((1,38.15),(1,38.5)).\5 resize(.5)67 # coordinates p1,p2 define the edges of the mask8 mask = dw.color_split(anna_kris.size,9 p1=(445, 20), p2=(345, 275),10 grad_width=5) # blur the mask's edges1112 snapshot = anna_kris.to_ImageClip().\13 set_duration(anna_kris.duration).\14 set_mask(ImageClip(mask, ismask=True))1516 CompositeVideoClip([anna_kris,snapshot]).\17 speedx(0.2).\18 to_gif('anna_kris.gif', fps=15, fuzz=3) # fuzz= GIF compression19Time-symetrizationSurely you have noticed that in the previous GIFs, the end did not always look like the beginning. As a consequence, you could see a disruption every time the animation was restarted. A way to avoid this is to time-symetrize the clip, i.e. to make the clip play once forwards, then once backwards. This way the end of the clip really is the beginning of the clip. This creates a GIF that can loop fluidly, without a real beginning or end.1 def time_symetrize(clip):2 """ Returns the clip played forwards then backwards. In case3 you are wondering, vfx (short for Video FX) is loaded by4 >>> from moviepy.editor import * """5 return concatenate([clip, clip.fx( vfx.time_mirror )])67 VideoFileClip("./frozen_trailer.mp4", audio=False).\8 subclip(36.5,36.9).\9 resize(0.5).\10 crop(x1=189, x2=433).\11 fx( time_symetrize ).\12 to_gif('sven.gif', fps=15, fuzz=2)Ok, this might be a bad example of time symetrization,it makes the snow flakes go upwards in the second half of the animation.Adding some textIn the next GIF there will be a text clip superimposed on the video clip.1 olaf = VideoFileClip("./frozen_trailer.mp4", audio=False).\2 subclip((1,21.6),(1,22.1)).\3 resize(.5).\4 speedx(0.5).\6 fx( time_symetrize )67 # Many options are available for the text (requires ImageMagick)8 text = TextClip("In my nightmares\nI see rabbits.",9 fontsize=30, color='white',10 font='Amiri-Bold', interline=-25).\11 set_pos((20,190)).\12 set_duration(olaf.duration)1314 CompositeVideoClip( [olaf, text] ).\15 to_gif('olaf.gif', fps=10, fuzz=2)Making the gif loopableThe following GIF features a lot of snow falling. Therefore it cannot be made loopable using time-symetrization (or you will snow floating upwards !). So we will make this animation loopable by having the beginning of the animation appear progressively (fade in) just before the end of the clip. The montage here is a little complicated, I cannot explain it better than with this picture:1 castle = VideoFileClip("./frozen_trailer.mp4", audio=False).\2 subclip(22.8,23.2).\3 speedx(0.2).\4 resize(.4)56 d = castle.duration7 castle = castle.crossfadein(d/2)89 CompositeVideoClip([castle,10 castle.set_start(d/2),11 castle.set_start(d)]).\12 subclip(d/2, 3*d/2).\13 to_gif('castle.gif', fps=5,fuzz=5)Another example of a GIF made loopableThe next clip (from the movie Charade) was almost loopable: you can see Carry Grant smiling, then making a funny face, then coming back to normal. The problem is that at the end of the excerpt Cary is not exactly in the same position, and he is not smiling as he was at the beginning. To correct this, we take a snapshot of the first frame and we make it appear progressively at the end. This seems to do the trick.1 carry = VideoFileClip("../videos/charade.mp4", audio=False).\2 subclip((1,51,18.3),(1,51,20.6)).\3 crop(x1=102, y1=2, x2=297, y2=202)45 d = carry.duration6 snapshot = carry.to_ImageClip().\7 set_duration(d/6).\8 crossfadein(d/6).\9 set_start(5*d/6)1011 CompositeVideoClip([carry, snapshot]).\12 to_gif('carry.gif', fps=carry.fps, fuzz=3)Big finish: removing the backgroundLet us dive further into the scripting madness: we consider this video around 2’16:http://www.youtube.com/watch?v=_wZjCDXDNLIAnd we will remove the background to make this gif (with transparent background):The main difficulty was to find what the background of the scene is. To do so, the script gathers a few images in which the little pigs are are different positions (so that every part part of the background is visible on at least several (actually most) of the slides, then it takes the pixel-per-pixel median of these pictures, which gives the background.1 # Requires Scikit Images installed2 import numpy as np3 import skimage.morphology as skm4 import skimage.filter as skf56 from moviepy.editor import *78 ### LOAD THE CLIP910 pigsPolka = VideoFileClip("pigs_in_a_polka.mp4").\11 subclip((2,16.85),(2,35)).\12 resize(.5).\13 crop(x1=140, y1=41, x2=454, y2=314)141516 ### COMPUTE THE BACKGROUND17 # There is no single frame showing the background only (there18 # is always a little pig in the screen) so we use the median of19 # several carefully chosen frames to reconstitute the background.20 # I must have spent half an hour to find the right set of frames.2122 times = (list(np.linspace(2.3,4.2,30))+23 list(np.linspace(6.0,7.1,30))+24 8*[6.2])2526 frames_bg = [pigsPolka.get_frame(t) for t in times]27 background = np.percentile(np.array(frames_bg), 50,axis=0)282930 ### MASK GENERATION3132 def get_mask_frame(t):33 """ Computes the mask for the frame at time t """3435 # THRESHOLD THE PIXEL-TO-PIXEL DIFFERENCE36 # BETWEEN THE FRAME AND THE BACKGROUND37 im = pigsPolka.get_frame(t)38 mask = ((im-background)**2).sum(axis=2) > 15003940 # REMOVE SMALL OBJECTS41 mask = skm.remove_small_objects(mask)4243 # REMOVE SMALL HOLES (BY DILATIATION/EROSION)44 selem=np.array([[1,1,1],[1,1,1],[1,1,1]])45 for i in range(2):46 mask = skm.binary_dilation(mask,selem)47 for i in range(2):48 mask = skm.binary_erosion(mask,selem)4950 # BLUR THE MASK A LITTLE51 mask = skf.gaussian_filter(mask.astype(float),1.5)5253 return mask5455 mask = VideoClip(ismask=True).\56 set_get_frame(get_mask_frame).\57 set_duration(pigsPolka.duration)5859 ### LAST EFFECTS AND GIF GENERATION6061 pigsPolka.set_mask(mask).\62 subclip(12.95,15.9).\63 fx(vfx.blackwhite).\ # black & white effect !64 to_gif('pigs_polka.gif', fps=10,65 dispose=True, fuzz=10)Source Quote Link to comment Share on other sites More sharing options...
jackieo Posted January 26, 2014 Share Posted January 26, 2014 gifs are fascinating to me...is this all your work Turk? or are you just posting this ? Quote Link to comment Share on other sites More sharing options...
Turk Posted January 26, 2014 Author Share Posted January 26, 2014 (edited) gifs are fascinating to me...is this all your work Turk? or are you just posting this ?Jackieo, I am posting, please see the author at the top of the post and at the bottom of the source page. Edited January 26, 2014 by Turk Quote Link to comment Share on other sites More sharing options...
jackieo Posted January 27, 2014 Share Posted January 27, 2014 Just so I know who to direct questuons to, if I have one...thanks :) Quote Link to comment Share on other sites More sharing options...
iih1 Posted March 20, 2014 Share Posted March 20, 2014 interesting topic to be studied Quote Link to comment Share on other sites More sharing options...
iih1 Posted March 21, 2014 Share Posted March 21, 2014 (edited) @Turki'm really going to learn more about this could you tell me more how to start pythonin brief notes, i have visit the link as given but feel bit confused about python.Thx. Edited March 21, 2014 by iih1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.