Skip to content

Instantly share code, notes, and snippets.

@gphg
Forked from Zulko/makegifs.py
Last active December 27, 2022 18:13
Show Gist options
  • Save gphg/60852c868bb38328e7003ad4bfcd69b7 to your computer and use it in GitHub Desktop.
Save gphg/60852c868bb38328e7003ad4bfcd69b7 to your computer and use it in GitHub Desktop.
My own custom of makegifs.py.
#!/usr/bin/env python
# Taken from http://zulko.github.io/blog/2015/02/01/extracting-perfectly-looping-gifs-from-videos-with-python-and-moviepy/
import os
import sys
import moviepy.editor as mp
from moviepy.video.tools.cuts import FramesMatches
# Extends FramesMatches, the code is copied from source file then modified
def _write_mp4s(self, clip, mp4s_dir, **kwargs):
for (start, end, _, _) in self:
name = "%s/%08d_%08d.mp4" % (mp4s_dir, 100 * start, 100 * end)
clip.subclip(start, end).write_videofile(name, **kwargs)
FramesMatches.write_mp4s = _write_mp4s
if len(sys.argv) != 2:
print("makegifs.py <video file>")
sys.exit(1)
clip = mp.VideoFileClip(sys.argv[1])
name = os.path.splitext(os.path.basename(sys.argv[1]))[0]
if not os.path.isdir(name): os.mkdir(name)
# Skip matches, just load existed file if exists
if os.path.isfile(name+"/matches.txt"):
# load the scenes from a previous time to avoid recomputing them
scenes = FramesMatches.load(name+"/matches.txt")
else:
# Compute matches (slow; take more time)
# clip.resize() set with video's scaled-down dimenions in integer for best fits.
scenes = FramesMatches.from_clip(clip.resize(width=160), 5, 3) # I increase 2 to 3 to get more gifs
scenes.save(name+"/matches.txt")
"""
That's the hard part that you must tweak. Not that the whole method is
very sloppy so maybe you can just redefine/customize this function if
you have the time
The last number "0.5" says that you want at least 0.5 second between the start of each clip.
If you really want to avoid having multiple times the same scene with just a few different frames,
increase this to e.g. 1.5
"""
selected_scenes = scenes.select_scenes(2, 0.5, 0, 0.5)
if not os.path.isdir(name+"/mp4"): os.mkdir(name+"/mp4")
selected_scenes.write_mp4s(clip.without_audio(), name+"/mp4") # save mp4 as it, without reencode (fast)
if not os.path.isdir(name+"/gif"): os.mkdir(name+"/gif")
gif_clip_width = clip.w / 2
selected_scenes.write_gifs(clip.resize(width=gif_clip_width), name+"/gif") # save gif as it, resized by half
@gphg
Copy link
Author

gphg commented Jul 9, 2022

Changed width=120 into height=144 for less accuracy.
https://gist.github.com/gphg/60852c868bb38328e7003ad4bfcd69b7#file-makegifs-py-L18

Update:
I changed the clip into smaller dimension and that third param from 3 into 2 was to expect less misses (video with no loop) and less duplicates. The ongoing results are a lot of misses.

@gphg
Copy link
Author

gphg commented Jul 9, 2022

Screen shakes and inconsistent lighting are most factors cause the misses. Can't be helped.

@gphg
Copy link
Author

gphg commented Jul 9, 2022

Bonus: change the write_gifs onto write_videofile on this line
https://gist.github.com/gphg/60852c868bb38328e7003ad4bfcd69b7#file-makegifs-py-L35
to save the loop as video instead gif.

Edit:

Traceback (most recent call last):
  File "C:\Users\pnesi\Videos\Output\makegifs.py", line 35, in <module>
    selected_scenes.write_videofile(clip, name) # save gif as it, without resize
AttributeError: 'FramesMatches' object has no attribute 'write_videofile'

I'll figured out how to fix it.

Update: I figured it and made some videos.
https://gfycat.com/@garett/collections/iiOjSrfe/delicious-party-precure
I'll post how to reproduce it later.

@gphg
Copy link
Author

gphg commented Dec 27, 2022

Some notes based on looking up the source code and do several tries:

from_clip(clip, x, y)

At the moment I have no idea what is this. I tried to tweak the number up and down, either it ended up losing or unexpected result. Keep it as it is: x=5 and y=3 for best.

select_scenes(2, 0.5, 0, 0.5)

  1. Keep the first param as smallest possible for best seamless loop. You can set higher, eg 9 or so for more availability, but the loop kinda glitchy and doesn't seamless.
  2. Keep the second param as it.
  3. Third param is the distance between gifs. The suggested value as the original code base is 4, but in my case--a short video under 2 minutes, I set it at 0.
  4. Forth param, consider follow as the comment mentioned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment