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 動畫進而批量做成視訊,上傳各大自媒體平台,應該也能擷取到不錯的閱讀量。