2019年2月13日 星期三

用 Python 剪輯影片

先用 Python 寫支程式到 YouTube 下載李飛飛 TED 演講的影片(How we teach computers to understand pictures)。將下載的影片命名為 Video.mp4 (影片解析度為 1280x720,長度為 18:02),並儲存於工作目錄。


安裝 ffmpeg 套件 (此時版本為 4.1)

    conda install ffmpeg -c conda-forge

安裝 moviepy 套件 (此時版本為 0.2.3.5)

    pip install moviepy

第一版程式-使用 ffmpeg_extract_subclip 剪輯

    from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip

    t1 = 50
    t2 = 100
    ffmpeg_extract_subclip('C:\Temp\Video.mp4', t1, t2, targetname='C:\Temp\Video Clip 1.mp4')

    t1 = 100
    t2 = 150
    ffmpeg_extract_subclip('C:\Temp\Video.mp4', t1, t2, targetname='C:\Temp\Video Clip 2.mp4')

程式執行後會將下載的 Video.mp4 剪輯出 Video Clip 1.mp4 (50s-100s) 與 Video Clip 2.mp4 (100s-150s) 兩段影片。

Video Clip 1.mp4 (50s-100s)


Video Clip 2.mp4 (100s-150s)


**********************************************************************************************************************

第二版程式-使用 VideoFileClip 剪輯

    from moviepy.video.io.VideoFileClip import VideoFileClip

    t1 = 50
    t2 = 100
    input_video = 'C:\Temp\Video.mp4'
    output_video = 'C:\Temp\Video Clip 1.mp4'
    with VideoFileClip(input_video) as video:
        video_clip = video.subclip(t1, t2)
        video_clip.write_videofile(output_video, audio_codec='aac')

    t1 = 100
    t2 = 150
    input_video = 'C:\Temp\Video.mp4'
    output_video = 'C:\Temp\Video Clip 2.mp4'
    with VideoFileClip(input_video) as video:
        video_clip = video.subclip(t1, t2)
        video_clip.write_videofile(output_video, audio_codec='aac')

程式執行後一樣會將下載的 Video.mp4 剪輯出 Video Clip 1.mp4 (50s-100s) 與 Video Clip 2.mp4 (100s-150s) 兩段影片。

Video Clip 1.mp4 (50s-100s)


Video Clip 2.mp4 (100s-150s)


從畫面截圖可以看出兩個版本的程式會有些微的時間差。仔細比對,第二版的程式時間比較準;可是第一版的執行速度非常快。

**********************************************************************************************************************

有時候只是想聽演講內容,而不需要畫面,這時用 mp3 會方便很多。

第三版程式-使用 ffmpeg_extract_audio 擷取聲音

    from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_audio
    ffmpeg_extract_audio('C:\Temp\Video.mp4', 'C:\Temp\Audio.mp3', bitrate=3000, fps=44100)

**********************************************************************************************************************

第四版程式-使用 AudioFileClip 擷取聲音

    from moviepy.audio.io.AudioFileClip import AudioFileClip
    audio_clip = AudioFileClip('C:\Temp\Video.mp4')
    audio_clip.write_audiofile('C:\Temp\Audio.mp3')
    # 預設 fps=44100,bitrate='3000k'

原始長度為 18:02 的影片,在相同的 bit rate 與 fps 下,第三版的程式擷取出來的聲音檔長度為 18:02,與影片長度一致;但第四版的程式擷取出來的聲音檔長度卻為 18:30,還好聲音聽起來感覺不出延遲。

**********************************************************************************************************************

第五版程式-使用 concatenate_videoclips 串接影片

    from moviepy.editor import VideoFileClip, concatenate_videoclips
    clip1 = VideoFileClip('C:\Temp\Video.mp4').subclip(50, 100)
    clip2 = VideoFileClip('C:\Temp\Video.mp4').subclip(100, 150)
    final_clip = concatenate_videoclips([clip1, clip2])
    final_clip.write_videofile('C:\Temp\Video Clip.mp4')
    # 如未指定 write_videofile 的 audio_codec 參數,預設是 mp3

程式執行後會將 Video.mp4 (50s-100s) 與 Video.mp4 (100s-150s) 兩段影片串接成 Video Clip.mp4 (0s-100s)。

Video Clip.mp4 (0s)


Video Clip.mp4 (50s)


**********************************************************************************************************************

第六版程式-使用 clips_array 與 vfx 堆疊特效影片

    from moviepy.editor import VideoFileClip, clips_array, vfx
    clip1 = VideoFileClip('C:\Temp\Video.mp4').subclip(50, 100).margin(10)
    clip2 = clip1.fx(vfx.mirror_x)
    clip3 = clip1.fx(vfx.mirror_y)
    clip4 = clip1.resize(0.60)
    final_clip = clips_array([[clip1, clip2], [clip3, clip4]])
    final_clip.resize(width=1280).write_videofile('C:\Temp\Video vfx Clip.mp4')
    # clip1 增加 10 個像素的黑邊,clip2 對 x 軸鏡射,clip3 對 y 軸鏡射,clip4 縮小 60%
    # 調整輸出影片解析度寬為 1280

程式執行後會將 Video.mp4 (50s-100s) 影片經過四次 vfx 特效處理後,堆疊成 Video vfx Clip.mp4 (0s-50s)。

Video vfx Clip.mp4 (0s-50s)


堆疊影片的速度會隨著所做的特效而變慢非常多,如果不調整輸出影片解析度,堆疊後的影片解析度會變成 2600x1480 (寬與高要加上多出來的黑邊)。

**********************************************************************************************************************

第七版程式-製作 GIF 影片

    from moviepy.editor import VideoFileClip
    clip = VideoFileClip('C:\Temp\Video.mp4').subclip(50, 52)
    final_clip = clip.resize(0.50)
    final_clip.write_gif('C:\Temp\Video Clip.gif')

Video Clip.gif


**********************************************************************************************************************

在各個程式版本之間,我故意使用不同的語法與應用方式,做測試也留下筆記,尤其是在第二版中使用了 with-as statement。為了處理檔案讀取過程中發生的例外,並且確定最後檔案一定會關閉,一般會使用 try-except-finally statement,而在 Python 3 (或 2.6) 中,可以使用 wit-as statement 來簡化程式的撰寫。

使用 try-except-finally statement

    from moviepy.video.io.VideoFileClip import VideoFileClip
    t1 = 50
    t2 = 100
    input_video = 'C:\Temp\Video.mp4'
    output_video = 'C:\Temp\Video Clip 1.mp4'
    video = VideoFileClip(input_video)
    try:
        video_clip = video.subclip(t1, t2)
        video_clip.write_videofile(output_video, audio_codec='aac')
    except:
        print('讀取檔案發生錯誤')
    finally:
        video.close()

使用 wit-as statement

    from moviepy.video.io.VideoFileClip import VideoFileClip
    t1 = 50
    t2 = 100
    input_video = 'C:\Temp\Video.mp4'
    output_video = 'C:\Temp\Video Clip 1.mp4'
    with VideoFileClip(input_video) as video:
        video_clip = video.subclip(t1, t2)
        video_clip.write_videofile(output_video, audio_codec='aac')

with 之後的運算式傳回的物件,可以使用 as 指定給變數來參考,在上面的例子中,video 所參考到的物件,最後會被自動關閉,即使在 with-as 的區塊中發生了例外,最後還是一定會關閉 video 所參考的物件。

2 則留言:

  1. 您好,我在執行VideoFileClip的resize時候,他說VideoFileClip has no "resize" member, 不知道您是用哪一版的moviepy呢? 感謝

    回覆刪除
    回覆
    1. 不好意思,我看到您的版本了,我更新一下我的moviepy

      刪除