文章目录
一、关于 MoviePy
- Github : https://zulko.github.io/moviepy/
相关教程
- moviepy_guide>
https://github.com/makelove/moviepy_guide - 中文手册
https://github.com/APhun/moviepy-cn
https://moviepy-cn.readthedocs.io/zh/latest/ - ucsheep:MoviePy实战应用 专栏
https://blog.csdn.net/ucsheep/category_9283153.html - play4fun/moviepy_chinese_font
https://hub.docker.com/r/play4fun/moviepy_chinese_font
教程和文档有很多,但还是喜欢整理成符合我个人阅读习惯的资料,方便后续查阅。
如果你和我阅读、写代码习惯相似,欢迎点赞、继续关注~
安装
pip install moviepy
工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wdF8iDDk-1660361039556)(images/moviepy.jpeg)]
MoviePy 的核心对象是 剪辑
剪辑使用类:AudioClips, VideoClips。基本类:VideoClip。
剪辑可被修改(剪切、降低速度、变暗等)或与其他剪辑混合组成新剪辑。
剪辑可被预览(使用PyGame或IPython Notebook)
剪辑也可生成文件(如MP4文件、GIF文件、MP3文件等)。
二、音频
获取音频信息
你可以使用 AudioFileClip 获得音频对象,读取对象信息,操作此对象。
还可以通过更深的 FFMPEG_AudioReader 类,查看更深的信息。
from moviepy.editor import *
mp3_path = '/Users/xx/Music/00.mp3'
audio_f = AudioFileClip(mp3_path)
print('filename : ', audio_f.filename) # /Users/xx/Music/00.mp3
print('duration : ', audio_f.duration) # 7.03
print('buffersize : ', audio_f.buffersize) # 200000
print('nchannels : ', audio_f.nchannels) # 2 双声道
print('fps : ', audio_f.fps) # 44100 采样率
print('\n------ reader ------')
print('reader : ', audio_f.reader) # <moviepy.audio.io.readers.FFMPEG_AudioReader object at 0x7fddaf6b2210>
print('infos : ', audio_f.reader.infos) # {'duration': 7.03, 'video_found': False, 'audio_found': True, 'audio_fps': 44100}
print('buffersize : ', audio_f.reader.buffersize) # 200000
print('nbytes : ', audio_f.reader.nbytes) # 2
print('acodec : ', audio_f.reader.acodec) # pcm_s16le
print('nframes : ', audio_f.reader.nframes) # 310023
切割
src_path = '/Users/xx/Music/05.mp3'
save_path = '/Users/xx/Music/05_01.mp3'
audio_f = AudioFileClip(src_path)
audio_f = audio_f.subclip(0, 6)
audio_f.write_audiofile(save_path)
拼接
src_path1 = '/Users/xx/Music/05_01.mp3'
src_path2 = '/Users/xx/Music/05_02.mp3'
save_path = '/Users/xx/Music/05_03.mp3'
audio_files = [src_path1, src_path2]
# audio_clips = [AudioFileClip(src_path1), AudioFileClip(src_path2)]
audio_clips = [AudioFileClip(audio_path) for audio_path in audio_files]
audio = concatenate_audioclips(audio_clips)
audio.write_audiofile(save_path)
音频叠加
音频循环播放
格式转换
视频提取音频
file_path = '/Users/xx/Music/00.mov'
# file_path = '/Users/xx/Music/01.mp4'
save_audio_path = '/Users/xx/Music/00.mp3'
save_video_path = '/Users/xx/Music/001.mp4'
video_f = VideoFileClip(file_path).subclip(5,12) # 裁剪 5-12s 之间的数据
# video = VideoFileClip(file_path) 获取全部
print('-- video : ', video_f) # <moviepy.video.io.VideoFileClip.VideoFileClip object at 0x7f9b07f09f90>
video_f = video_f.volumex(0.8) # 设置音量为原来的 0.8
video_f.audio.write_audiofile(save_audio_path) # 保存音频
video_f.write_videofile(save_video_path) # 保存视频
三、视频
获取视频信息
file_path = '/Users/xx/Music/00.mov'
video_c = VideoFileClip(file_path)
print('-- filename : ', video_c.filename) # /Users/xx/Music/00.mov
print('-- duration : ', video_c.duration) # 13.39
print('-- fps : ', video_c.fps) # 59.0
print('-- size : ', video_c.size) # [960, 720]
print('\n---- reader')
print('-- duration : ', video_c.reader.infos) # {'duration': 13.39, 'video_found': True, 'video_size': [960, 720], 'video_fps': 59.0, 'video_nframes': 791, 'video_duration': 13.39, 'video_rotation': 0, 'audio_found': True, 'audio_fps': 44100}
print('-- nframes : ', video_c.reader.nframes) # 791
print('-- depth : ', video_c.reader.depth) # 3
print('-- bufsize : ', video_c.reader.bufsize) # 2073700
预览视频
# video_c.preview() # 预览视频,默认 fps=15
video_c.preview(fps=60) #
# video_c.preview(fps=15, audio=False) # 不播放音乐
# my_audio_clip.preview(fps=22000)
视频/Gif 提取图片
file_path = '/Users/xx/Music/00.mov'
# file_path = "/Users/xx/Pictures/beauty/lyf.gif"
video_c = VideoFileClip(file_path)
video_c.save_frame("frame.png", t=2) # 查看 2s 处的图片, t 默认为0
# video_c.show(3) # 显示 3s 处的图片,显示一秒后自动关闭
video_c.show(3, interactive = True) # 需要手动关闭
my_clip.preview() # preview with default fps=15
my_clip.preview(fps=25)
my_clip.preview(fps=15, audio=False) # don't generate/play the audio.
my_audio_clip.preview(fps=22000)
图片生成视频 / gif
import os
from moviepy.editor import *
from moviepy.editor import ImageSequenceClip
imgs_dir = '/Users/xx/Pictures/beauty/lyf/01'
video_path = '/Users/xx/Pictures/beauty/lyf.mp4'
video_path2 = '/Users/xx/Pictures/beauty/lyf2.mp4'
gif_path = '/Users/xx/Pictures/beauty/lyf.gif'
imgs_name = sorted(os.listdir(imgs_dir))
imgs_path = [os.path.join(imgs_dir , image_name) for image_name in imgs_name]
video_c = ImageSequenceClip(imgs_path, fps=2) # 1s 显示2张图
# 写入视频
video_c.write_videofile(video_path, codec='libx264')
# 写入 gif
video_c.write_gif(gif_path)
图片 + 音乐合成视频
# 添加音乐写入视频
mp3_path = '/Users/xx/Music/00.mp3'
audio_c = AudioFileClip(mp3_path)
# audio_c = afx.audio_loop(audio_c, duration=video_c.duration)
print(id(video_c))
video_c = video_c.set_audio(audio_c) # 需要重新赋值,因为已经不是一个对象;原对象没有声音
print(id(video_c))
video_c.write_videofile(video_path2, codec='libx264')
视频提取 gif
from moviepy.editor import *
video_path = '/Users/xx/Music/00.mov'
gif_path = '/Users/xx/Music/00.gif'
clip=(VideoFileClip(video_path).subclip(0, 5).resize(0.2))
# 使用subclip截取视频片段
# resize缩放为原来的20%
# 预览(播放)截取片段
# clip.preview()
#转为gif
clip.write_gif(gif_path)
如果报错:clip.preview requires Pygame installed
, 安装 Pygame 即可:
pip install pygame
视频添加文字
from moviepy.editor import *
video_c = VideoFileClip("001.mp4")
# 指定文字大小、颜色和位置
txt_c = TextClip("2020",fontsize=70,color='red')
# 设置水印位置,持续时间
txt_c = txt_c.set_position('center').set_duration(video.duration)
# 在视频上覆盖文本
video_c2 = CompositeVideoClip([video_c, txt_c])
# fps:视频文件中每秒的帧数
video_c2.write_videofile("002.mp4",fps=25)
视频拼接 concatenate_videoclips
from moviepy.editor import VideoFileClip, concatenate_videoclips
clip1 = VideoFileClip("myvideo.mp4")
clip2 = VideoFileClip("myvideo2.mp4").subclip(50,60)
clip3 = VideoFileClip("myvideo3.mp4")
final_clip = concatenate_videoclips([clip1,clip2,clip3])
final_clip.write_videofile("my_concatenation.mp4")
- 如果各剪辑尺寸不同,那么它们将被居中播放,而画面大小足够包含最大的剪辑;
- 而且你可以选择一种颜色来填充边界部分。
- 你还有许多其它可选项(详见函数的文档),例如你可以通过
transition=my_clip
选项来在剪辑之间加一个过场。
堆叠 clips_array
from moviepy.editor import VideoFileClip, clips_array, vfx
clip1 = VideoFileClip("myvideo.mp4").margin(10) # add 10px contour
clip2 = clip1.fx( vfx.mirror_x)
clip3 = clip1.fx( vfx.mirror_y)
clip4 = clip1.resize(0.60) # downsize 60%
final_clip = clips_array([[clip1, clip2],
[clip3, clip4]])
final_clip.resize(width=480).write_videofile("my_stack.mp4")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rl2HsVyN-1660361039557)(MoviePy.assets/stacked.jpeg)]
合成 CompositeVideoClip
video = CompositeVideoClip([clip1,clip2,clip3])
# 剪辑在更大的合成视频里浮动,需要特意修改最终合成视频的尺寸
# video = CompositeVideoClip([clip1,clip2,clip3], size=(720,460))
- 当前
video
播放clip1
clip2
在clip1
的上层,而clip3
在clip1
和clip2
的上层。- 举例来说,如果
clip2
与clip3
和clip1
有同样的尺寸,那么只有在顶层的clip3
能在视频中可见,除非clip3
和clip2
被遮罩隐藏了一部分。 - 注意,在默认情况下,合成的视频和第一个剪辑的尺寸相同(因为它通常是一个背景)。
设置起始和终止时间 set_start
clip1 = clip1.set_start(5) # start after 5 seconds
# 设置各视频的入场时间
video = CompositeVideoClip([clip1, # starts at t=0
clip2.set_start(5), # start at t=5s
clip3.set_start(9)]) # start at t=9s
video = CompositeVideoClip([clip1, # starts at t=0
clip2.set_start(5).crossfadein(1),
clip3.set_start(9).crossfadein(1.5)])
clip2
可能先于clip1
的结束时间开始,这种情况可以给clip2
添加一个1秒钟的淡入效果
video = CompositeVideoClip([clip1, # starts at t=0
clip2.set_start(5).crossfadein(1),
clip3.set_start(9).crossfadein(1.5)])
设置视频的位置 set_pos
指定了剪辑左上角像素的坐标:
video = CompositeVideoClip([clip1,
clip2.set_pos((45,150)),
clip3.set_pos((90,100))])
clip2.set_pos((45,150)) # x=45, y=150 , in pixels
clip2.set_pos("center") # automatically centered
# clip2 is horizontally centered, and at the top of the picture
clip2.set_pos(("center","top"))
# clip2 is vertically centered, at the left of the picture
clip2.set_pos(("left","center"))
# clip2 is at 40% of the width, 70% of the height of the screen:
clip2.set_pos((0.4,0.7), relative=True)
# clip2's position is horizontally centered, and moving down !
clip2.set_pos(lambda t: ('center', 50+t) )
坐标系 y
坐标的0位置在图片的最上方
音视频合成
当你将视频剪辑混合在一起时,MoviePy将会把它们各自的音轨自动合成为最终剪辑的音轨。
如果你想使用许多音频源来自定义音轨,可以使用CompositeAudioClip
和concatenate_audioclips
来进行混音。
from moviepy.editor import *
# ... make some audio clips aclip1, aclip2, aclip3
concat = concatenate_audioclips([aclip1, aclip2, aclip3])
compo = CompositeAudioClip([aclip1.volumex(1.2),
aclip2.set_start(5), # start at t=5s
aclip3.set_start(9)])
设置音频 set_audio
videoclip2 = videoclip.set_audio(my_audioclip)
四、其他使用示例
使用MoviePy生成的剪辑基本类型
# VIDEO CLIPS
clip = VideoClip(make_frame, duration=4) # for custom animations (see below)
clip = VideoFileClip("my_video_file.mp4") # or .avi, .webm, .gif ...
clip = ImageSequenceClip(['image_file1.jpeg', ...], fps=24)
clip = ImageClip("my_picture.png") # or .jpeg, .tiff, ...
clip = TextClip("Hello !", font="Amiri-Bold", fontsize=70, color="black")
clip = ColorClip(size=(460,380), color=[R,G,B])
# AUDIO CLIPS
clip = AudioFileClip("my_audiofile.mp3") # or .ogg, .wav... or a video !
clip = AudioArrayClip(numpy_array, fps=44100) # from a numerical array
clip = AudioClip(make_frame, duration=3) # uses a function make_frame(t)
VideoClip
import gizeh
import moviepy.editor as mpy
def make_frame(t):
surface = gizeh.Surface(128,128) # width, height
radius = W*(1+ (t*(2-t))**2 )/6 # the radius varies over time
circle = gizeh.circle(radius, xy = (64,64), fill=(1,0,0))
circle.draw(surface)
return surface.get_npimage() # returns a 8-bit RGB array
clip = mpy.VideoClip(make_frame, duration=2) # 2 seconds
clip.write_gif("circle.gif",fps=15)
ImageSequenceClip
clip = ImageSequenceClip(images_list, fps=25)
模糊视频中的所有帧
from skimage.filter import gaussian_filter
from moviepy.editor import VideoFileClip
def blur(image):
""" Returns a blurred (radius=2 pixels) version of the image """
return gaussian_filter(image.astype(float), sigma=2)
clip = VideoFileClip("my_video.mp4")
clip_blurred = clip.fl_image( blur )
clip_blurred.write_videofile("blurred_video.mp4")
2022-08-13(六) 伊织
本文含有隐藏内容,请 开通VIP 后查看