I'm trying to make a video from a list of images using moviepy. I have issues using moviepy.editor since it does not like being frozen using PyInstaller, so I'm using moviepy.video.VideoClip.ImageClip for the images and moviepy.video.compositing.CompositeVideoClip.CompositeVideoClip for the clip. I have a list of .jpg images in a list called images:
from moviepy.video.VideoClip import ImageClip
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
clips = [ImageClip(m).set_duration(1) for m in images]
concat_clip = CompositeVideoClip(clips))
concat_clip.write_videofile('VIDEO.mp4', fps=1)
It successfully makes an .mp4 but the video is only one second long and the last image in the list of images. I can check clips and it has the ~30 images that should be in the video. I can do this from using methods from moviepy.editor following this SO question and answer, but there doesn't seem to be an analogous parameter in CompositeVideoClip for method='compose' which is where I think the issue is.
using concatenate_videoclips might help. I use the code below and it works just fine.
clips = [ImageClip(m).set_duration(1/25)
for m in file_list_sorted]
concat_clip = concatenate_videoclips(clips, method="compose")
concat_clip.write_videofile("test.mp4", fps=25)
Related
How to extract multiple smaller video clips from a long video using some python package, I need it as part of my video preprocessing for my project.
ffmpeg is a method but its too complex.
Any other method would be really helpful.
I tried using moviepy, but the documentation is not that clear, so I could only extract one video at a time and not multiple.
Using the Moviepy package, we can extract multiple smaller clips from a large video.
from moviepy.video.io.VideoFileClip import VideoFileClip
def extract_clips(video_file, clip_duration, clip_start_times):
clip_list = []
with VideoFileClip(video_file) as video:
for start_time in clip_start_times:
clip = video.subclip(start_time, start_time + clip_duration)
clip_list.append(clip)
return clip_list
video_file = "path/to/your/video.mp4"
clip_duration = 5 # duration of each clip in seconds
clip_start_times = [0, 10, 20] # start times of each clip in seconds
clips = extract_clips(video_file, clip_duration, clip_start_times)
# save the clips to disk
for i, clip in enumerate(clips):
clip.write_videofile("clip_{}.mp4".format(i))
The function takes in parameters, the video file, the total play time of the clip and the start times of wherever you want it to cut at.
You can use the write_videofile() to save these clips into the desired location in your system.
I hope this gives some more clarity on how to perform this operation.
So I wrote a script that creates a video out of a series of images. I have the images stored in a folder called "TEMP". The part of the script that takes a very long time is the following:
from moviepy.editor import *
def createVideoFromImages(genre, audio):
#export name
timeStamp = str(time.time()).split(".")[0]
exportFolderName = f"./finished/{genre}{timeStamp}"
exportFileName = f"{exportFolderName}/video.mp4"
images = os.listdir("TEMP")
clips = [ImageClip(f"./TEMP/{m}").set_duration(10).crossfadein(1.0) for m in images]
concat_clip = concatenate_videoclips(clips, method="compose")
audio_duration = audio.duration
videoclip = concat_clip.set_duration(audio_duration)
exportClip = videoclip.set_audio(audio)
#create folder and save video there
os.mkdir(exportFolderName)
exportClip.write_videofile(exportFileName, fps=60, preset="ultrafast")
return exportFolderName
I tried a couple of things, like changing the concatenation of videoclips to method="chain" but that broke and I got a glitchy video, where only the first image was properly showing.
I also tried to add the preset="ultrafast" as I found somewhere online, but I find it to slow things down rather than speed things up.
I suspect the script to run this slow(it takes 8-9 hours for an ~300 second video) because it takes almost all the RAM of my computer.
Is there a way to speed up this script, preferrably with a minimal sacrifice of quality?
I have collected a large-scale datasets of gifs(more than 100k) from the Internet, and I meet some rare strange GIFs when I try to extract frames of GIFs with python. Three common used packages(moviepy, PIL, imageio) provide totally different results of such rare strange gifs.
moviepy>=1.0.3 will block in VideoFileClip.iter_frames() loop at the second frame forever, and the code won't throw an exception.
from moviepy.video.io.VideoFileClip import VideoFileClip
video = VideoFileClip(path)
frame_iterator = video.iter_frames()
PIL>=7.1.2 will output multiple frames as same as the first frame.
from PIL import Image, ImageSequence
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
video = Image.open(path)
frame_iterator = ImageSequence.Iterator(video)
imageio>=2.6.1 can extract the frames correctly while the output frames are strange.
import imageio
frame_iterator = imageio.get_reader(path)
Then you can dump the frames from frame_iterator provided by these packages above:
def dump_video_frames(video_frames):
root = 'data/frames'
if os.path.exists(root):
shutil.rmtree(root)
os.makedirs(root)
for i, frame in enumerate(video_frames):
frame.save(os.path.join(root, '%d.jpg' % i))
frames = []
for frame in frame_iterator:
if isinstance(frame, np.ndarray):
frame = Image.fromarray(np.uint8(frame))
frames.append(frame.convert('RGB'))
dump_video_frames(frames)
Here is an example:
Original GIF:
The output of PIL:
The output of imageio:
You can see PIL only get the first frame without any black area which is quite different with the output of imageio.
So my question is how to detect such a strange gif in python? Since I use moviepy first for its good performance in other gifs, I need to detect such a kind of GIF before the code use moviepy to extract its frames in order to avoid the infinite loop in VideoFileClip.iter_frames() which won't throw any exception. I can't get any information about such a rare gif from Google.
I will provide 2 more example GIFs below:
I am trying to create and save a GIF from a set of PNG files.
pics=[]
for plot_path in plot_paths:
img = Image.open(plot_path)
pics.append(img)
pics[0].save(save_dir+'/truestrain.gif', format='gif', save_all=True, append_images=pics[1:], duration=10, loop=0)
The output is a gif file, of the correct name, but only using the first PNG file, and 10 seconds long.
save_all=True should prompt it to use all of the images in append_images=pics[1:], but that doesn't seem to be working.
duration=10 should set the duration between frames as 10ms, seems to be interpreted as total time 10s (contrary to the Pillow documentation?)
I have seen a relevant previous post, which agrees with the method I am using, but still having problems (Saving an animated GIF in Pillow). I've also checked this follows the documentation (https://pillow.readthedocs.io/en/3.1.x/handbook/image-file-formats.html).
So it turns out, the GIF is being created correctly. Neither windows Photos or VLC was able to play it for some reason. I downloaded an alternative GIF viewer and the file is as expected.
Your code works for me with your version 6.2.0. I just tested different settings for duration and can definitely see the difference:
from PIL import Image
assert Image.__version__ == "6.2.0"
frames = [Image.open(f"frame_{i:0>3}.png") for i in range(44)]
for duration in [10, 100, 1000]:
frames[0].save(f"result_{duration}.gif", format="gif", save_all=True, append_images=frames[1:], duration=duration, loop=0)
There is a limit to duration precision of the GIF format itself. But it should be one hundredth of a second, so your duration=10 should be fine.
I've been writing a script using MoviePy. So far I've been able to import videos, clip them, add text, replace the audio and write a new file. It's been a great learning experience. My question is this:
The movie that I'm editing has audio attached. I'd like to be able to import an audio track and add it to the movie without replacing the original audio. In other words, I'd like to mix the new audio file with the audio that's attached to the video so both can be heard.
Does anyone know how to do this?
Thanks in advance!
I wrote my own version, but then I found this here:
new_audioclip = CompositeAudioClip([videoclip.audio, audioclip])
videoclip.audio = new_audioclip
So, create a CompositeAudioClip with the audio of the video clip and the new audio clip, then set the old videoclip's audio to the composite audio track.
Full working code:
from moviepy.editor import *
videoclip = VideoFileClip("filename.mp4")
audioclip = AudioFileClip("audioname.mp3")
new_audioclip = CompositeAudioClip([videoclip.audio, audioclip])
videoclip.audio = new_audioclip
videoclip.write_videofile("new_filename.mp4")
If you want to change an individual audioclip's volume, refer to audio.fx.volumex.
Documentation
Source Code