93

I currently try to make a movie out of images, but i could not find anything helpful .

Here is my code so far:

import time

from PIL import  ImageGrab

x =0

while True:
    try:
        x+= 1
        ImageGrab().grab().save('img{}.png'.format(str(x))
    except:
        movie = #Idontknow
        for _ in range(x):
            movie.save("img{}.png".format(str(_)))

movie.save()
Amir
  • 10,600
  • 9
  • 48
  • 75
Victor
  • 1,607
  • 1
  • 10
  • 10

9 Answers9

159

You could consider using an external tool like ffmpeg to merge the images into a movie (see answer here) or you could try to use OpenCv to combine the images into a movie like the example here.

I'm attaching below a code snipped I used to combine all png files from a folder called "images" into a video.

import cv2
import os

image_folder = 'images'
video_name = 'video.avi'

images = [img for img in os.listdir(image_folder) if img.endswith(".png")]
frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape

video = cv2.VideoWriter(video_name, 0, 1, (width,height))

for image in images:
    video.write(cv2.imread(os.path.join(image_folder, image)))

cv2.destroyAllWindows()
video.release()

It seems that the most commented section of this answer is the use of VideoWriter. You can look up it's documentation in the link of this answer (static) or you can do a bit of digging of your own. The first parameter is the filename, followed by an integer (fourcc in the documentation, the codec used), the FPS count and a tuple of the dimensions of the frame. If you really like digging in that can of worms, here's the fourcc video codecs list.

BoboDarph
  • 2,751
  • 1
  • 10
  • 15
  • 4
    This actually not always does work for ubuntu https://github.com/ContinuumIO/anaconda-issues/issues/223 – Marat Zakirov Dec 19 '17 at 15:49
  • Thank you for posting this solution! It's getting me past loads of problems that I had using other solutions. – Mike from PSG Feb 08 '18 at 00:36
  • 13
    It worked on Ubuntu when i created like video = cv2.VideoWriter(video_name, cv2.VideoWriter_fourcc(*'XVID'), 30, (width,height)) But didn't work when: video = cv2.VideoWriter(video_name, -1, 1, (width,height)) – Отгонтөгс Миймаа Oct 11 '18 at 09:54
  • 1
    FWIW, on my Window 10 with Python 3.6 and opencv 3.3.1, I got a ffmpeg conversion error. Changing the "fourcc" argument, which handles video codecs (2nd argument) of cv2.VideoWriter from -1 to 0 solved it. – Soltius Jan 07 '19 at 14:56
  • i had tried this, it shows a problem on codec error while it playing...i want to know how we can mention codec here? can anyone help me? – Kumar S Feb 14 '19 at 08:13
  • Where do you get `cv2` package from? I've installed `opencv 4.1.1` at `python 3.6`, but I cannot import `cv2` – icemtel Sep 13 '19 at 10:22
  • 1
    how can you precise the framerate ? – nassim Nov 24 '19 at 20:10
  • 8
    @nassim `cv2.VideoWriter(output_filename, fourcc, fps, self._window_shape)` found [Here](https://www.programcreek.com/python/example/72134/cv2.VideoWriter) – Bolli Nov 19 '20 at 13:34
  • 2
    Thanks for the good answer. I would suggest as an improvement to explain and link to the documentation of the VideoWriter (the relevant part). This would make it more easy to get to know which datatypes are needed and what the individual parameters do. – Osmosis D. Jones May 21 '21 at 09:49
  • 1
    Just wanted to highlight that `cv2.VideoWriter` expects the dims in WIDTH then HEIGHT which is different than most the other `cv2` functions – pellucidcoder Dec 03 '21 at 20:11
  • @KumarS, 'VLC media player' did the job on Ubuntu 20.04.5 LTS. Another problem is that the loaded images are in an arbitrary order, so you might also want to sort them. – TopCoder2000 Oct 27 '22 at 12:17
  • Only thing I would change is to add `images = sorted(images)` to make sure the images are in the right order when processed. – Watchdog101 Feb 17 '23 at 15:21
60

Thanks , but i found an alternative solution using ffmpeg:

def save():
    os.system("ffmpeg -r 1 -i img%01d.png -vcodec mpeg4 -y movie.mp4")

But thank you for your help :)

Victor
  • 1,607
  • 1
  • 10
  • 10
34

Here is a minimal example using moviepy. For me this was the easiest solution.

import os
import moviepy.video.io.ImageSequenceClip
image_folder='folder_with_images'
fps=1

image_files = [os.path.join(image_folder,img)
               for img in os.listdir(image_folder)
               if img.endswith(".png")]
clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile('my_video.mp4')
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
trygvrad
  • 577
  • 5
  • 6
  • 2
    Moviepy https://zulko.github.io/moviepy/examples/examples.html is a great library! – meduz Sep 30 '20 at 11:54
  • I got "OSError: broken data stream when reading image file", which could be resolved with : https://stackoverflow.com/a/47958486/10794682 You can sort your images with "..in sorted(os.listdir(image_folder))..". Note: if your images are numbered, you can either use "natural sorting" (https://stackoverflow.com/a/5967539/10794682) or write a loop: image_files = [] for img_number in range(1,20): image_files.append(path_to_images + 'image_folder/image_' + str(img_number) + '.png') – ConZZito May 12 '21 at 14:41
  • @ConZZito Could you please write the entire code for us? It is not very helpful if we still need to refer to other sources after reading your suggestions. – Wei Shan Lee Aug 28 '22 at 03:18
19

I use the ffmpeg-python binding. You can find more information here.

import ffmpeg
(
    ffmpeg
    .input('/path/to/jpegs/*.jpg', pattern_type='glob', framerate=25)
    .output('movie.mp4')
    .run()
)
jiashenC
  • 1,812
  • 2
  • 16
  • 31
1

When using moviepy's ImageSequenceClip it is important that the images are in an ordered sequence.

While the documentation states that the frames can be ordered alphanumerically under the hood, I found this not to be the case.

So, if you are having problems, make sure to manually order the frames first.

Bendemann
  • 735
  • 11
  • 31
1

@Wei Shan Lee (and others): Sure, my whole code looks like this

import os
import moviepy.video.io.ImageSequenceClip
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

image_files = []

for img_number in range(1,20): 
    image_files.append(path_to_images + 'image_folder/image_' + str(img_number) + '.png') 

fps = 30

clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile(path_to_videos + 'my_new_video.mp4')
ConZZito
  • 325
  • 2
  • 5
  • This works for me, just have to remember to also define path_to_images and path_to_videos. Thank you for sharing. – most200 Aug 04 '23 at 00:46
0

I've created a function to do this. Similar to the first answer (using opencv) but wanted to add that for me, ".mp4" format did not work. That's why I use the raise within the function.

import cv2
import typing

def write_video(video_path_out:str,
                frames_sequence:typing.Tuple[np.ndarray,...]):
  
  if ".mp4" in video_path_out: raise ValueError("[ERROR] This method does not support .mp4; try .avi instead")
  height, width, _ = frames_sequence[0].shape
  # 0 means no preprocesing
  # 1 means  each image will be played with 1 sec delay (1fps)
  out = cv2.VideoWriter(video_path_out,0, 1,(width,height))
  for frame in frames_sequence:
      out.write(frame)
  out.release()

# you can use as much images as you need, I just use 3 for this example
# put your img1_path,img2_path, img3_path

img1 = cv2.imread(img1_path)
img2 = cv2.imread(img2_path)
img3 = cv2.imread(img3_path)
# img1 can be cv2.imread out; which is a np.ndarray; you can also se PIL
# if you'd like to.
frames_sequence = [img1,img2,img3]

write_video(video_path_out = "mypath_outvideo.avi",
frames_sequence = frames_sequence
)

Hope it's useful!

Tom
  • 496
  • 8
  • 16
0

Little hacky but avoids creating the file and just lets you watch it in real time.

import glob 
from PIL import Image
import cv2 
import numpy as np 
import time 
####### PARAMS 

imgs_path = "/Users/user/Desktop/lidar_rig/ouster_data_wide_angle_cam_v9/imgs/*"

cur_img_index = 0
ds_st_index = 0
ds_en_index = -1

fps = 35 # tweak this 


###### PARAMS 

def cnvt_pil_to_cv2(pil_img):
    open_cv_image = np.array(pil_img) 
    # Convert RGB to BGR 
    open_cv_image = open_cv_image[:, :, ::-1].copy()     
    return open_cv_image




img_files = sorted(glob.glob(imgs_path), key = lambda x: int(x.split('/')[-1].split('.')[0]))[ds_st_index:ds_en_index][cur_img_index:]
cnt = 0 

for img_pth in img_files:
    if not cnt %50: ## DDD -- avoid mem overflow  
        cv2.destroyAllWindows()
    img = Image.open(img_pth).resize((750,750))
    cv2.imshow(img_pth.split("/")[-1], cnvt_pil_to_cv2(img))
    time.sleep(float(1.0/float(fps)))
    cnt+=1 
Brett Young
  • 168
  • 2
  • 8
0

As an additional contribution, if anyone struggles to get the loaded images to be sorted alphabetically, here’s a version that uses the built-in sorted(os.listdir(image_folder)) function.

import os
import moviepy.video.io.ImageSequenceClip
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

image_folder='frames'
fps=10


image_files = [os.path.join(image_folder,img)
               for img in sorted(os.listdir(image_folder))
               if img.endswith(".png")]
print(image_files)

clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile('myvideo.mp4')

However, note that it is filesystem/filename dependant and will sort numbers wrong (1,10,2,3…). Below is another solution with a sorted_alphanumeric(data) function that solves that (from another thread)

import os
import moviepy.video.io.ImageSequenceClip
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

image_folder='frames'
fps=10

import re
def sorted_alphanumeric(data):
    convert = lambda text: int(text) if text.isdigit() else text.lower()
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] 
    return sorted(data, key=alphanum_key)

image_files = [os.path.join(image_folder,img)
               for img in sorted_alphanumeric(os.listdir(image_folder))
               if img.endswith(".png")]
print(image_files)

clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile('myvideo.mp4')
toyota Supra
  • 3,181
  • 4
  • 15
  • 19