天天看點

10萬+的短視訊被批量生産了,Python表示不服

1

目 标 場 景

做過自媒體的朋友應該都知道,「GIF動畫視訊」有段時間在各大自媒體平台很受歡迎。

前期有些自媒體大 V 靠搬運一些搞笑、好玩的 GIF,然後利用剪輯軟體合成一段視訊,再添加一個節奏感強的 BGM 後,上傳各大自媒體平台後,能帶來不錯的閱讀量和收益。

本篇文章的目的是帶大家利用 Python 實作制作 GIF 動畫視訊,批量制作短視訊這一騷操作。

2

準 備 工 作

首先,對視訊和背景音樂的剪輯,這裡用到了「moviepy」庫,通過 pip3 安裝到虛拟環境中。

# moviepy 用于視訊剪輯和背景音樂的合成、剪輯

pip3 install moviepy

另外,項目中需要利用「PIL」庫來分析、擷取 GIF 動畫中的所有幀圖檔。

# 将GIF圖檔轉為幀,需要對GIF進行分析

pip3 install ffmpeg

編寫腳本之前,我們需要提前準備一些 GIF 動畫素材。當然,你也可以去爬取一些搞笑、好玩的 GIF 動畫。

另外再準備一段 BGM 作為視訊的背景音樂。

3

編  寫  腳  本

第一步,我們需要把每一個 GIF 動畫轉為一段視訊。

由于 GIF 動畫已經是一段包含很多幀的視訊了,沒法直接通過 moviepy 庫轉為一段普通視訊。

是以,這裡需要對 GIF 動畫進行分析,将動畫轉為「靜态幀圖檔」。

def get_gif_frames(gif_path, temp_path):

    """

    擷取一段GIf圖檔下的所有靜态幀

    get_gif_frames('./../gifs/3.gif', './../gif_temp/')

    :return:

    # 分析gif圖檔

    mode = analyseImage(gif_path)['mode']

    im = Image.open(gif_path)

    i = 1

    p = im.getpalette()

    last_frame = im.convert('RGBA')

    try:

        while True:

            # print("saving %s (%s) frame %d, %s %s" % (gif_path, mode, i, im.size, im.tile))

            '''

            If the GIF uses local colour tables, each frame will have its own palette.

            If not, we need to apply the global palette to the new frame.

            if not im.getpalette():

                im.putpalette(p)

            new_frame = Image.new('RGBA', im.size)

            Is this file a "partial"-mode GIF where frames update a region of a different size to the entire image?

            If so, we need to construct the new frame by pasting it on top of the preceding frames.

            if mode == 'partial':

                new_frame.paste(last_frame)

            new_frame.paste(im, (0, 0), im.convert('RGBA'))

            new_frame.save(temp_path + '/%s-%d.png' % (''.join(os.path.basename(gif_path).split('.')[:-1]), i), 'PNG')

            i += 1

            last_frame = new_frame

            im.seek(im.tell() + 1)

    except EOFError:

        # print('産生EOFError!!!')

        pass

另外,我們下載下傳的 GIF 動畫的靜态幀圖檔分辨率大機率是不一緻的,是以對圖檔批量修改分辨率「修改分辨率」變的很有必要。

這裡将所有圖檔的分辨率統一修改為 720*1080,在轉換的過程中,如果存在空白部分,就使用黑色進行填充。

def resize_image(target_image_path, target_size):

    調整圖檔大小,缺失的部分用黑色填充

    :param target_image_path: 圖檔路徑

    :param target_size: 分辨率大小

    image = Image.open(target_image_path)

    iw, ih = image.size  # 原始圖像的尺寸

    w, h = target_size  # 目标圖像的尺寸

    scale = min(w / iw, h / ih)  # 轉換的最小比例

    # 保證長或寬,至少一個符合目标圖像的尺寸

    nw = int(iw * scale)

    nh = int(ih * scale)

    image = image.resize((nw, nh), Image.BICUBIC)  # 縮小圖像

    # image.show()

    new_image = Image.new('RGB', target_size, (0, 0, 0, 0))  # 生成黑色圖像

    # // 為整數除法,計算圖像的位置

    new_image.paste(image, ((w - nw) // 2, (h - nh) // 2))  # 将圖像填充為中間圖像,兩側為灰色的樣式

    # new_image.show()

    # 覆寫原圖檔

    new_image.save(target_image_path)

然後,将統一分辨率後的靜态幀圖檔轉換為一段普通視訊。

在轉換為視訊之前,我們需要提供一個「合理的轉換幀率」來保證視訊播放的流暢性。由于最後需要将多段視訊合成為一段視訊,這裡預設指定幀率為 10幀/s。

GIF 動畫原始的幀率、播放時長等動畫檔案屬性值可以利用「imgpy」擷取到。

def get_gif_info(gif_path):

    擷取gif檔案的詳細資訊

    每一個gif的幀率不一樣,有的<10fps;有的>10fps

    :param gif_path:

    with Img(fp=gif_path) as im:

        # 1.有多少幀

        frame_count = im.frame_count

        # 2.圖檔資訊

        # {'version': b'GIF89a', 'background': 31, 'duration': 70, 'extension': (b'NETSCAPE2.0', 795), 'loop': 0}

        duration_pre = im.info.get('duration')

        # 根據規律,除以7位實際的播放時長

        duration = duration_pre / 7

        # 6.color palette

        # print(im.mode_desc)

        # print((frame_count, duration))

        # 傳回幀率和時長

        return (frame_count / duration), duration

最後,我們利用 moviepy 庫中的「ImageSequenceClip」類将這些圖檔寫入到一個視訊檔案中。

def pics_to_video(pics_path, output_path, fps, duration):

    圖檔轉為視訊

    pics_to_video('./../gif_temp/', './../video_temp/temp1.mp4', 20)

    :param pics_path:

    :param output_path:

    image_paths = list(map(lambda x: pics_path + x, os.listdir(pics_path)))

    # 注意:這裡必須進行一次排序,保證所有幀的順序是一緻

    image_paths = sort_strings_with_emb_numbers(image_paths)

    # 過濾掉非圖檔

    image_paths = list(filter(lambda image_path: image_path.endswith('.png'), image_paths))

    # 圖檔剪輯類

    clip = ImageSequenceClip(image_paths,

                             fps=fps)

    # 寫成視訊之前,需要把gif都轉成同一個分辨率

    clip.write_videofile(output_path)

循環上面的操作,就可以将所有的 GIF 動畫轉換為一個普通視訊檔案。

第二步是将所有的視訊檔案進行剪輯,寫入一個單獨的檔案中。利用 moviepy 庫下面的 「 VideoFileClip 」可以非常快捷友善地完成這一操作。

def compound_a_video(self, videos_path):

        """

        合成一個視訊

        :param videos_output:視訊集合的完整目錄

        :return:

        # 定義一個數組

        L = []

        for video_path in videos_path:

            # 載入視訊

            video = VideoFileClip(video_path)

            # 添加到數組

            L.append(video)

        # 拼接視訊

        final_clip = concatenate_videoclips(L)

        # 生成目标視訊檔案

        final_clip.to_videofile(self.video_output_temp, fps=self.fps, remove_temp=False)

最後一步是往視訊中添加背景音樂。

首先是通過 AudioFileClip 和 VideoFlieClip 擷取到視訊檔案和音頻檔案的播放時長,對播放時長較長的檔案進行截取處理。

# 1.音頻檔案

audioclip = AudioFileClip(self.bgm_path)

# 2.視訊檔案

 videoclip = VideoFileClip(self.video_output_temp)

# 3.擷取視訊和音頻的時長

video_time = videoclip.duration

audio_time = audioclip.duration

print('視訊時長:%f,音頻時長:%f' % (video_time, audio_time))

# 4.對視訊或者音頻進行裁剪

if video_time > audio_time:

      # 視訊時長>音頻時長,對視訊進行截取

      ideoclip_new = videoclip.subclip(0, audio_time)

      audioclip_new = audioclip

else:

      # 音頻時長>視訊時長,對音頻進行截取

      videoclip_new = videoclip

      audioclip_new = audioclip.subclip(0, video_time)

然後把音頻檔案通過 set_audio() 添加到視訊操作類中,最後重新寫入到一個新的視訊檔案當中。

# 5.視訊中加入音頻

video_with_new_audio = videoclip_new.set_audio(audioclip_new)

# 6.寫入到新的視訊檔案中

video_with_new_audio.write_videofile("mp4_with_audio.mp4",

                                             codec='libx264',

                                             audio_codec='aac',

                                             temp_audiofile='temp-audio.m4a',

                                             remove_temp=True

                                             )

4

結 果 結 論

以上的腳本會對指定檔案夾的的 GIF 動畫檔案分别生成一段普通視訊,然後把所有的視訊合成一段視訊,然後再添加一段 BGM 背景音樂,最後寫入到一個新的視訊檔案中,如此,就完成了制作一個 GIF 視訊的操作。

當然,本文隻是提供一個思路,讓 Python 爬取一些有趣好玩的 GIF 動畫進而批量做成視訊,上傳各大自媒體平台,應該也能擷取到不錯的閱讀量。