Python - MoviePy 处理音视频

发布于:2023-01-19 ⋅ 阅读:(808) ⋅ 点赞:(0)

在这里插入图片描述


一、关于 MoviePy


相关教程

教程和文档有很多,但还是喜欢整理成符合我个人阅读习惯的资料,方便后续查阅。

如果你和我阅读、写代码习惯相似,欢迎点赞、继续关注~


安装

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
  • clip2clip1的上层,而clip3clip1clip2的上层。
  • 举例来说,如果clip2clip3clip1有同样的尺寸,那么只有在顶层的clip3能在视频中可见,除非clip3clip2被遮罩隐藏了一部分。
  • 注意,在默认情况下,合成的视频和第一个剪辑的尺寸相同(因为它通常是一个背景)。

设置起始和终止时间 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将会把它们各自的音轨自动合成为最终剪辑的音轨。

如果你想使用许多音频源来自定义音轨,可以使用CompositeAudioClipconcatenate_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 后查看

网站公告

今日签到

点亮在社区的每一天
去签到