处理 gif 动画
这篇教学会介绍使用 OpenCV 搭配 Pillow 函数库,针对 gif 动画图档进行打开、编辑与储存的动作,并进一步将多张图片组合成 gif 动画 ( 包含背景透明的 gif 图档 ),或将影片转换成 gif 动画。
快速导览:
因为程序中的 OpenCV 会需要使用镜头或 GPU,所以请使用本机环境 ( 参考:使用 Python 虚拟环境 ) 或使用 Anaconda Jupyter 进行实作 ( 参考:使用 Anaconda ) ,并安装 OpenCV 函数库 ( 参考:OpenCV 函数库 )。
安装 Pillow
由于 OpenCV 无法直接处理 gif 图档,所以使用 Pillow 打开 gif 动画,输入下面的指令,安装 Pillow 函数库。
pip install Pillow
打开 gif 动画,将每一格储存为 jpg
使用 Image.open 打开 gif 动画后,透过 ImageSequence.Iterator 搭配 for 循环,取出动画里每一个影格,最后使用 save 的方法将每一个影格储存为 jpg 静态图档 ( 因为 gif 的颜色模式为 P mode,如果要储存为 jpg 必须要先转换为 RGB,参考 Convert PIL.Image to OpenCV )。
- 范例图档:gif 动画
from PIL import Image,ImageSequence
gif = Image.open('dot.gif') # 讀取動畫圖檔
i = 0 # 設定編號變數
for frame in ImageSequence.Iterator(gif):
frame = frame.convert('RGB') # 取出每一格轉換成 RGB
frame.save(f'frame{i}.jpg', quality=65, subsampling=0) # 儲存為 jpg
i = i + 1 # 編號增加 1
OpenCV 预览 gif 动画
使用 PIL 读取 gif 动画图档后,搭配 NumPy 将图档内容转换成 OpenCV 可以读取的 NumPy 数组,就能透过 OpenCV 显示动画的每一个影格。
from PIL import Image,ImageSequence
import cv2
import numpy as np
gif = Image.open('dot.gif')
img_list = [] # 建立儲存影格的空串列
for frame in ImageSequence.Iterator(gif):
frame = frame.convert('RGBA') # 轉換成 RGBA
opencv_img = np.array(frame, dtype=np.uint8) # 轉換成 numpy 陣列
opencv_img = cv2.cvtColor(opencv_img, cv2.COLOR_RGBA2BGRA) # 顏色從 RGBA 轉換為 BGRA
img_list.append(opencv_img) # 利用串列儲存該圖片資訊
loop = True # 設定 loop 為 True
while loop:
for i in img_list:
cv2.imshow('oxxostudio', i) # 不斷讀取並顯示串列中的圖片內容
if cv2.waitKey(200) == ord('q'):
loop = False # 停止時同時也將 while 迴圈停止
break
cv2.destroyAllWindows()
编辑并储存 gif 动画
打开 gif 动画后,可以利用 OpenCV 编辑,再透过 Pillow 的 save 方法储存为新的 gif,如果要储存为“gif 动画”,需要按照下方规则设定 save:
frame1.save("oxxostudio.gif", save_all=True, append_images=frame_list, duration=200, disposal=2)
# frame1:gif 動畫第一個影格
# save_all:設定 True 表示儲存全部影格,否則只有第一個
# append_images:要添加到 frame1 影格的其他影格,串列格式,通常會用 frame[1:] 來添加除了第一個影格之後的所有影格
# duration:每個影格之間的毫秒數,支援串列格式
# disposal:添加模式,預設 0,如果背景透明,則設定為 2 避免影格彼此覆蓋覆蓋
下方的程序执行后,会先打开 gif 动画,然后在每一个影格的中间加上黑色区块与文字,最后再组合成新的 gif 动画。
from PIL import Image,ImageSequence
import cv2
import numpy as np
gif = Image.open('dot.gif')
img_list = []
for frame in ImageSequence.Iterator(gif):
frame = frame.convert('RGBA')
opencv_img = np.array(frame, dtype=np.uint8)
opencv_img = cv2.cvtColor(opencv_img, cv2.COLOR_RGBA2BGRA)
# 在圖形中間繪製黑色方塊
cv2.rectangle(opencv_img,(100,120),(300,180),(0,0,0),-1)
# 在黑色方塊上方加入文字
text = 'oxxo.studio'
org = (110,160)
fontFace = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 1
color = (255,255,255)
thickness = 2
lineType = cv2.LINE_AA
cv2.putText(opencv_img, text, org, fontFace, fontScale, color, thickness, lineType)
img_list.append(opencv_img)
loop = True
while loop:
for i in img_list:
cv2.imshow('oxxostudio', i)
if cv2.waitKey(200) == ord('q'):
loop = False
break
# 建立要輸出的影格串列
output = []
for i in img_list:
img = i
img = cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA) # 因為 OpenCV 為 BGRA,要轉換成 RGBA
img = Image.fromarray(img) # 轉換成 PIL 格式
img = img.convert('RGB') # 轉換成 RGB ( 如果是 RGBA 會自動將黑色白色變成透明色 )
output.append(img) # 加入 output
# 儲存為 gif 動畫圖檔
output[0].save("oxxostudio.gif", save_all=True, append_images=output[1:], duration=200, loop=0, disposal=0)
cv2.destroyAllWindows()
静态图片组合成 gif 动画
单纯使用 PIL 依序打开静态图片,将图片依序加入串列中,最后再透过 save 方法储存为 gif。
from PIL import Image,ImageSequence
gif = []
for i in range(4):
img = Image.open(f'frame{i}.jpg') # 開啟圖片
gif.append(img) # 加入串列
# 儲存為 gif
gif[0].save("oxxostudio.gif", save_all=True, append_images=gif[1:], duration=200, loop=0, disposal=0)
影片转换为 gif 动画
运用上方所介绍的原理,就能将影片自动转换成 gif 动画,转换过程中设定 cv2.waitKey(ms) 的数值,就可以定义每多少毫秒截取一张图片,也可以避免 gif 文件大小过大的问题 ( 通常会将 cv2.waitKey(ms) 和储存时的 duration 参数设定为相同的数值 )。
from PIL import Image,ImageSequence
import cv2
import numpy as np
output = [] # 建立輸出的空串列
cap = cv2.VideoCapture(0) # 從攝影鏡頭取得影像
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img, (450,240)) # 調整影片大小
# 加上黑色區塊
cv2.rectangle(img,(10,10),(200,42),(0,0,0),-1)
# 加上文字
text = 'oxxo.studio'
org = (15,35)
fontFace = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 1
color = (255,255,255)
thickness = 2
lineType = cv2.LINE_AA
cv2.putText(img, text, org, fontFace, fontScale, color, thickness, lineType)
gif = cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA) # 轉換顏色
gif = Image.fromarray(gif) # 轉換成 PIL 格式
gif = gif.convert('RGB') # 轉換顏色
output.append(gif) # 添加到 output
cv2.imshow('oxxostudio', img)
if cv2.waitKey(250) == ord('q'):
break
cap.release()
# 儲存為 gif 動畫
output[0].save("test2.gif", save_all=True, append_images=output[1:], duration=250, loop=0, disposal=2)
cv2.destroyAllWindows()
微信扫码关注
抖音扫码关注