影片转透明 gif 动画
这篇教学延伸“将指定的颜色变透明”和“处理 gif 动画”教学,透过 OpenCV 读取影片,将特定颜色的背景去除后 ( 去背 ),输出成背景透明的 gif 动画。
因为程序中的 OpenCV 会需要使用镜头或 GPU,所以请使用本机环境 ( 参考:使用 Python 虚拟环境 ) 或使用 Anaconda Jupyter 进行实作 ( 参考:使用 Anaconda ) ,并安装 OpenCV 函数库 ( 参考:OpenCV 函数库 )。
如何转存背景透明的 gif 动画
在“处理 gif 动画”文章里有介绍 PIL 的 save 方法,这个方法能够将多张图片合并储存为 gif 动画,但如果是背景透明的动画,则常常会发生存档后背景还是不透明的状况,以下图为例,原本在 png 都是背景透明的图片,透过 save 组合成 gif 动画后,有些影格看起来虽然去背,但去背的区域却被填满了某些特定颜色。
为什么会这样呢?因为 png 和 gif 拥有的色彩数量不同,gif 属于索引色模式,最多只有 256 个颜色,而透明色也包含在这 256 个颜色里,当读取 png 后,虽然透过了 convert 的方式转换为 RGBA,但每张图片的透明色,却不一定放在颜色空间中的同样位置,当再次使用 save 方法时,会以“第一个影格”透明色的位置为基准,所以就导致有些影格透明,有些影格不透明的状况。
为了避免这种情况发生,可以先将要转换的影格输出为 gif,然后再次读取这些 gif 并转换为 RGBA,转换后会产生透明色固定在最后方的索引色版,最后转存时设定 disposal=2,就能产生背景透明的 gif 动画 ( disposal 若设为 0,表示会与第一个重叠,因此背景透明时会发生重叠残影的状况 )。
n = 0
for i in source: # source 為要轉存的所有圖片陣列 ( opencv 格式,色彩為 RGBA )
img = Image.fromarray(i) # 轉換成 PIL 格式
img.save(f'temp/gif{n}.gif') # 儲存為 gif
n = n + 1 # 改變儲存的檔名編號
output = [] # 建立空串列
for i in range(n):
img = Image.open(f'temp/gif{i}.gif') # 依序開啟每張 gif
img = img.convert("RGBA") # 轉換為 RGBA
output.append(img) # 記錄每張圖片內容
# 轉存為 gif 動畫,設定 disposal=2
output[0].save("oxxostudio.gif", save_all=True, append_images=output[1:], duration=100, loop=0, disposal=2)
影片转透明背景 gif 动画
下面的程序码,会先读取一段影片,为了避免 gif 动画文件太大,以影片 30 格取一格作为动画的影格,接着再透过循环,修改特定颜色范围内像素的透明度,将透明度改为 0,最后再套用上方所介绍的方法,转存为背景透明 gif 动画。
from PIL import Image,ImageSequence
import cv2
import numpy as np
cap = cv2.VideoCapture('video.mov') # 開啟影片
source = [] # 建立 source 空串列,記錄影格內容
frame = 0 # frame 從 0 開始
print('loading...')
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
if frame%30 == 0: # 每 30 格取一格
img = cv2.resize(img, (400,300)) # 改變尺寸,加快處理效率
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA) # 修改顏色為 RGBA
source.append(img) # 記錄該圖片
frame = frame + 1
if cv2.waitKey(1) == ord('q'):
break # 按下 q 鍵停止
cap.release()
print('start...')
for i in range(len(source)):
for x in range(400):
for y in range(300):
r = source[i][y,x,0] # 該像素的紅色數值
g = source[i][y,x,1] # 該像素的綠色數值
b = source[i][y,x,2] # 該像素的藍色數值
if r>35 and r<100 and g>110 and g<200 and b>60 and b< 130:
source[i][y,x,3] = 0 # 如果在顏色範圍內,將透明度設為 0
print('export single frame to gif...')
n = 0
for i in source:
img = Image.fromarray(i)
img.save(f'temp/gif{n}.gif')
n = n + 1
print('loading gifs...')
output = []
for i in range(n):
img = Image.open(f'temp/gif{i}.gif')
img = img.convert("RGBA")
output.append(img)
output[0].save("test2.gif", save_all=True, append_images=output[1:], duration=100, loop=0, disposal=2)
print('ok...')
cv2.destroyAllWindows()
微信扫码关注
抖音扫码关注