6.2.4 視頻功能
視頻中最常用的就是從視頻設(shè)備采集圖片或者視頻,或者讀取視頻文件并從中采樣。所以比較重要的也是兩個(gè)模塊,一個(gè)是VideoCapture,用于獲取相機(jī)設(shè)備并捕獲圖像和視頻,或是從文件中捕獲。還有一個(gè)VideoWriter,用于生成視頻。還是來(lái)看例子理解這兩個(gè)功能的用法,首先是一個(gè)制作延時(shí)攝影視頻的小例子:
import cv2
import time
interval = 60 # 捕獲圖像的間隔,單位:秒
num_frames = 500 # 捕獲圖像的總幀數(shù)
out_fps = 24 # 輸出文件的幀率
# VideoCapture(0)表示打開(kāi)默認(rèn)的相機(jī)
cap = cv2.VideoCapture(0)
# 獲取捕獲的分辨率
size =(int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# 設(shè)置要保存視頻的編碼,分辨率和幀率
video = cv2.VideoWriter(
"time_lapse.avi",
cv2.VideoWriter_fourcc('M','P','4','2'),
out_fps,
size
)
# 對(duì)于一些低畫(huà)質(zhì)的攝像頭,前面的幀可能不穩(wěn)定,略過(guò)
for i in range(42):
cap.read()
# 開(kāi)始捕獲,通過(guò)read()函數(shù)獲取捕獲的幀
try:
for i in range(num_frames):
_, frame = cap.read()
video.write(frame)
# 如果希望把每一幀也存成文件,比如制作GIF,則取消下面的注釋
# filename = '{:0>6d}.png'.format(i)
# cv2.imwrite(filename, frame)
print('Frame {} is captured.'.format(i))
time.sleep(interval)
except KeyboardInterrupt:
# 提前停止捕獲
print('Stopped! {}/{} frames captured!'.format(i, num_frames))
# 釋放資源并寫(xiě)入視頻文件
video.release()
cap.release()
這個(gè)例子實(shí)現(xiàn)了延時(shí)攝影的功能,把程序打開(kāi)并將攝像頭對(duì)準(zhǔn)一些緩慢變化的畫(huà)面,比如桌上緩慢蒸發(fā)的水,或者正在生長(zhǎng)的小草,就能制作出有趣的延時(shí)攝影作品。比如下面這個(gè)鏈接中的圖片就是用這段程序生成的:
程序的結(jié)構(gòu)非常清晰簡(jiǎn)單,注釋里也寫(xiě)清楚了每一步,所以流程就不解釋了。需要提一下的有兩點(diǎn):一個(gè)是VideoWriter中的一個(gè)函數(shù)cv2.VideoWriter_fourcc()。這個(gè)函數(shù)指定了視頻編碼的格式,比如例子中用的是MP42,也就是MPEG-4,更多編碼方式可以在下面的地址查詢(xún):
Video Codecs by FOURCC
還有一個(gè)是KeyboardInterrupt,這是一個(gè)常用的異常,用來(lái)獲取用戶(hù)Ctrl+C的中止,捕獲這個(gè)異常后直接結(jié)束循環(huán)并釋放VideoCapture和VideoWriter的資源,使已經(jīng)捕獲好的部分視頻可以順利生成。
從視頻中截取幀也是處理視頻時(shí)常見(jiàn)的任務(wù),下面代碼實(shí)現(xiàn)的是遍歷一個(gè)指定文件夾下的所有視頻并按照指定的間隔進(jìn)行截屏并保存:
import cv2
import os
import sys
# 第一個(gè)輸入參數(shù)是包含視頻片段的路徑
input_path = sys.argv[1]
# 第二個(gè)輸入?yún)?shù)是設(shè)定每隔多少幀截取一幀
frame_interval = int(sys.argv[2])
# 列出文件夾下所有的視頻文件
filenames = os.listdir(input_path)
# 獲取文件夾名稱(chēng)
video_prefix = input_path.split(os.sep)[-1]
# 建立一個(gè)新的文件夾,名稱(chēng)為原文件夾名稱(chēng)后加上_frames
frame_path = '{}_frames'.format(input_path)
if not os.path.exists(frame_path):
os.mkdir(frame_path)
# 初始化一個(gè)VideoCapture對(duì)象
cap = cv2.VideoCapture()
# 遍歷所有文件
for filename in filenames:
filepath = os.sep.join([input_path, filename])
# VideoCapture::open函數(shù)可以從文件獲取視頻
cap.open(filepath)
# 獲取視頻幀數(shù)
n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# 同樣為了避免視頻頭幾幀質(zhì)量低下,黑屏或者無(wú)關(guān)等
for i in range(42):
cap.read()
for i in range(n_frames):
ret, frame = cap.read()
# 每隔frame_interval幀進(jìn)行一次截屏操作
if i % frame_interval == 0:
imagename = '{}_{}_{:0>6d}.jpg'.format(video_prefix, filename.split('.')[0], i)
imagepath = os.sep.join([frame_path, imagename])
print('exported {}!'.format(imagepath))
cv2.imwrite(imagepath, frame)
# 執(zhí)行結(jié)束釋放資源
cap.release()
評(píng)論
查看更多