4

Hello I just finished to write a code that compute the orbitals of the hydrogen atom. I wrote a for loop to create 300 pictures using the command

plt.savefig("image{i}.png".format(i=i))

Now I wanted to ask what is the easiest way to create a high quality .mp4 or .gif file out of the pictures using python. I saw several tutorials that didn't helped me because the gif was messed up or the quality was too low.

Thank you for your support

Phicalc
  • 75
  • 1
  • 6
  • bit broad - what did you try? I successfully did animations using GIMP 2 but to an animated gif. There are plenty of aproaches using ffmpeg: https://stackoverflow.com/questions/24961127/how-to-create-a-video-from-images-with-ffmpeg and threads for python like this: [python-make-a-video-using-several-png-images](https://stackoverflow.com/questions/13590976/python-make-a-video-using-several-png-images) – Patrick Artner May 10 '20 at 17:37

3 Answers3

2

The easiest I know is to use imageio's mimwrite.

import imageio
ims = [imageio.imread(f) for f in list_of_im_paths]
imageio.mimwrite(path_to_save_gif, ims)

There are obvious options such as duration, number of loops, etc.
And some other options you can read about in the documentation by using imageio.help('gif').

Hope that helps.

ShlomiF
  • 2,686
  • 1
  • 14
  • 19
  • 1
    I changed list_of_im_paths to "~/Plot" the directory where the .png are saved but I get a syntax error. – Phicalc May 10 '20 at 17:59
  • Um, that's because you need a list of paths, not just a path. How would the list comprehension know that you want to use a list of pngs in that directory?? Try using glob or os.listdir. – ShlomiF May 10 '20 at 18:29
  • @Phicalc, did this answer your question? If so it's customary to accept, so as to benefit the contributor as well as the community as a whole. – ShlomiF May 10 '20 at 20:40
  • @Phicalc , you just need to have a ``plt.savefig("image{i}.png".format(i=i))`` with a ``list_of_im_paths.append("/your_path_to_fig_folder/image{i}.png".format(i=i))`` inside your loop (that creates your figures). This way, you create a list with paths to each figure. (And also, I think you will need to define an empty list ``list_of_im_paths=[]`` just before your loop). – Diving May 21 '22 at 01:43
2

The faster way is to use imageio as in @ShlomiF's answer, but you can do the same thing with pure matplotlib if you so prefer:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

nframes = 25
plt.subplots_adjust(top=1, bottom=0, left=0, right=1)

def animate(i):
    im = plt.imread('image'+str(i)+'.png')
    plt.imshow(im)

anim = FuncAnimation(plt.gcf(), animate, frames=nframes, 
                     interval=(2000.0/nframes))
anim.save('output.gif', writer='imagemagick')

But if your first priority is the quality of the output you may want to consider using ffmpeg and convert directly,

ffmpeg -f image2 -i image%d.png output.mp4
ffmpeg -i output.mp4 -vf "fps=10,scale=320:-1:flags=lanczos" -c:v pam -f \ 
    image2pipe - | convert -delay 10 - -loop 0 -layers optimize output.gif

Changing the scale argument as needed to control the size of the final output, scale=-1:-1 leaves the size unchanged.

William Miller
  • 9,839
  • 3
  • 25
  • 46
0

I too found gif's to be too poor quality, so sought a solution to make an mp4 with higher resolution and playback control. I haven't found an mp4 solution, but I have been able to write .avi movie files using the Python cv2 library. Here's an example:

import cv2 

# Create avi movie from static plots created of each time slice
image_folder = 'path_to_png_files'
video_name = '/mov-{}.avi'.format('optional label')

# Store individual frames into list
images = [img for img in os.listdir(image_folder) if img.endswith(".png")]

# Create frame dimension and store its shape dimensions
frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape

# cv2's VideoWriter object will create a frame 
video = cv2.VideoWriter(avi_path + video_name, 0, 1, (width,height))

# Create the video from individual images using for loop
for image in images:
    video.write(cv2.imread(os.path.join(image_folder, image)))

# Close all the frames
cv2.destroyAllWindows()

# Release the video write object
video.release()