如何使用Python将图片制作成电影

94

我目前正在尝试将图片制作成电影,但我找不到有用的东西。

这是我迄今为止的代码:

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()
9个回答

160

您可以考虑使用外部工具,如ffmpeg将图像合并成视频(请参见答案此处),或者尝试使用OpenCv将图像组合成视频,例如此处示例。

我附上了一段代码片段,用于将名为“images”的文件夹中的所有png文件合并成视频。

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()

看起来这篇回答中最受关注的部分是使用VideoWriter。你可以在这篇回答的链接(静态)中查阅它的文档,或者自己稍微搜索一下。第一个参数是文件名,后面跟着一个整数(fourcc在文档中表示编解码器),FPS计数和帧的尺寸元组。如果你真的喜欢挖掘这罐虫子,这里是fourcc视频编解码器列表


4
这并非在Ubuntu上总是奏效 https://github.com/ContinuumIO/anaconda-issues/issues/223 - Marat Zakirov
13
当我在Ubuntu上使用如下语句创建视频时:video = cv2.VideoWriter(video_name, cv2.VideoWriter_fourcc(*'XVID'), 30, (width,height)),它起作用了。 但是当我使用以下语句时:video = cv2.VideoWriter(video_name, -1, 1, (width,height)),它没有起作用。 - Отгонтөгс Миймаа
8
这是一段Python代码,它使用OpenCV库中的cv2.VideoWriter函数来创建一个视频编写器对象并将视频文件写入磁盘。具体地,函数需要四个参数:输出文件名、视频编解码器(fourcc)、帧率(fps)和窗口形状(self._window_shape)。 - Bolli
2
谢谢您的好回答。我建议改进一下,解释并链接到VideoWriter(相关部分)的文档。这将使了解需要哪些数据类型以及各个参数的作用变得更加容易。 - Osmosis D. Jones
1
只是想强调一下,cv2.VideoWriter 期望的尺寸顺序是宽度然后是高度,这与大多数其他 cv2 函数不同。 - pellucidcoder
显示剩余7条评论

60

谢谢,但我找到了一种使用FFmpeg的替代方案:

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

但是感谢你的帮助 :)


1
如何在合并所有图像时定义帧率 - Aman Jain
6
FPS 是使用 "-r" 标记定义的,因此 "-r 1" 表示 1 FPS,"-r 30" 表示 30 FPS,依此类推。 - Robbie Barrat
82
这不是Python,你只是通过Python运行FFmpeg命令! - Azim
3
为什么帧率提高到60或更高后,图像质量会变得非常糟糕? - Farshid Rayhan
1
使用-vb选项可以增加比特率。"ffmpeg -r 1 -i img%01d.png -vcodec mpeg4 -y -vb 40M movie.mp4" - Jariani

35

这是一个使用moviepy的最简示例。对我来说,这是最容易的解决方案。

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')

2
Moviepy是一个非常棒的库!https://zulko.github.io/moviepy/examples/examples.html - meduz
我遇到了“OSError:读取图像文件时的损坏数据流”,可以通过以下方式解决:https://dev59.com/UVgQ5IYBdhLWcg3wolao#47958486您可以使用“..in sorted(os.listdir(image_folder))..”对图像进行排序。注意:如果您的图像已编号,则可以使用“自然排序”(https://dev59.com/SG025IYBdhLWcg3wf2OE#5967539)或编写循环:image_files = []for img_number in range(1,20):image_files.append(path_to_images + 'image_folder/image_' + str(img_number) + '.png') - ConZZito
@ConZZito,您能否为我们编写完整的代码?如果在阅读您的建议后仍需要参考其他来源,那么这并没有太大的帮助。 - Wei Shan Lee

19

我使用 ffmpeg-python 绑定。你可以在这里找到更多信息。

import ffmpeg
(
    ffmpeg
    .input('/path/to/jpegs/*.jpg', pattern_type='glob', framerate=25)
    .output('movie.mp4')
    .run()
)

它可以运行,但是出现了一些照片旋转的问题。有什么解决办法吗? - Heisenberg
@heisenberg:根据这里,尝试插入'.hflip()'或类似的内容。 - pyano
@pyano 谢谢你的回复,但我无法确认它是否正确,因为我是一年前写的那条评论。 - Heisenberg

2

@Wei Shan Lee(和其他人):当然,我的整个代码看起来像这样

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')

这对我有用,只需记住还要定义path_to_images和path_to_videos。谢谢分享。 - most200

1
使用moviepy的ImageSequenceClip时,重要的是图像按顺序排列。虽然文档documentation中指出帧可以在幕后按字母数字顺序排序,但我发现情况并非如此。因此,如果您遇到问题,请确保首先手动对帧进行排序。

0

有点hacky,但避免了创建文件,只让你实时观看它。

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 

0
我已经创建了一个函数来完成这个任务。类似于第一个答案(使用opencv),但是我想要补充一点,对我来说,“.mp4”格式不起作用。这就是为什么我在函数内使用raise的原因。
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
)

希望这很有用!


0

如果有人在排序加载的图像时遇到困难,以下是一个使用内置的sorted(os.listdir(image_folder))函数的版本,作为额外的贡献。

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')

然而,请注意它依赖于文件系统/文件名,并且会错误地排序数字(1,10,2,3…)。下面是另一种解决方案,使用一个名为sorted_alphanumeric(data)的函数来解决这个问题(来自另一个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')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接