網(wǎng)站首頁 編程語言 正文
讀視頻,提取幀
接口函數(shù):cv2.VideoCapture()
通過video_capture = cv2.VideoCapture(video_path)可以獲取讀取視頻的句柄。而后再通過flag, frame = video_capture.read()可以讀取當(dāng)前幀,flag表示讀取是否成功,讀取成功后,句柄會(huì)自動(dòng)移動(dòng)到下一幀的位置。讀取結(jié)束后使用video_capture.release()釋放句柄。
一個(gè)簡(jiǎn)單的逐幀讀取的程序如下:
import cv2
video_capture = cv2.VideoCapture(video_path)
while True:
flag, frame = video_capture.read()
if not flag:
break
# do something with frame
video_capture.release()
獲取視頻信息
為了能更好更靈活地了解并讀取視頻,我們有時(shí)候需要獲取視頻的一些信息,比如幀率,總幀數(shù)等等。獲取這些信息的方法是調(diào)用video_capture.get(PROP_ID)方法,其中PROP_ID是OpenCV定義的一些常量。
常用的信息及示例如下:
import cv2
video_path = r'D:\peppa\Muddy_Puddles.mp4'
video_capture = cv2.VideoCapture(video_path)
frame_num = video_capture.get(cv2.CAP_PROP_FRAME_COUNT) # ==> 總幀數(shù)
fps = video_capture.get(cv2.CAP_PROP_FPS) # ==> 幀率
width = video_capture.get(cv2.CAP_PROP_FRAME_WIDTH) # ==> 視頻寬度
height = video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT) # ==> 視頻高度
pos = video_capture.get(cv2.CAP_PROP_POS_FRAMES) # ==> 句柄位置
video_capture.set(cv2.CAP_PROP_POS_FRAMES, 1000) # ==> 設(shè)置句柄位置
pos = video_capture.get(cv2.CAP_PROP_POS_FRAMES) # ==> 此時(shí) pos = 1000.0
video_capture.release()
句柄位置指的是下一次調(diào)用read()方法讀取到的幀號(hào),幀號(hào)索引從0開始。
使用set(cv2.CAP_PROP_POS_FRAMES)讀取指定幀
從上面代碼中可以看到我們使用了set方法來設(shè)置句柄的位置,這個(gè)功能在讀取指定幀時(shí)很有用,這樣我們不必非要使用read()遍歷到指定位置。
但問題來了,這種方式讀取到的內(nèi)容和read()遍歷讀取到的內(nèi)容是否完全相同?
做個(gè)簡(jiǎn)單的實(shí)驗(yàn),下面用兩種方法分別讀取同一個(gè)視頻的[100, 200)幀,然后檢查讀取的內(nèi)容是否完全相同,結(jié)果是True。
import cv2
import numpy as np
video_path = r'D:\peppa\Muddy_Puddles.mp4'
video_capture = cv2.VideoCapture(video_path)
cnt = -1
frames1 = []
while True:
cnt += 1
flag, frame = video_capture.read()
assert flag
if 100 <= cnt < 200:
frames1.append(frame)
if cnt >= 200:
break
video_capture.release()
video_capture = cv2.VideoCapture(video_path)
frames2 = []
for i in range(100, 200):
video_capture.set(cv2.CAP_PROP_POS_FRAMES, i)
flag, frame = video_capture.read()
assert flag
frames2.append(frame)
video_capture.release()
frames1 = np.array(frames1)
frames2 = np.array(frames2)
print(np.all(frames1 == frames2)) # ==> check whether frames1 is same as frames2, result is True
接下來看看利用set讀取的效率。還是利用小豬佩奇第一集做實(shí)驗(yàn),這個(gè)視頻共7788幀,下面分別用兩種方法遍歷讀取視頻中所有幀。第二種方法明顯比第一種慢得多,所以這就很苦逼了。。。如果幀間隔比較小的話,單純用read()進(jìn)行遍歷效率高;如果幀間隔比較大的話,用set()設(shè)置位置,然后read()讀取效率高。
(如果給第二種方法加個(gè)判斷,每隔n幀讀取一次,那么效率確實(shí)會(huì)提高n倍,可以自行嘗試)
import cv2
import numpy as np
import time
video_path = r'D:\peppa\Muddy_Puddles.mp4'
video_capture = cv2.VideoCapture(video_path)
t0 = time.time()
while True:
flag, frame = video_capture.read()
if not flag:
break
t1 = time.time()
video_capture.release()
video_capture = cv2.VideoCapture(video_path)
t2 = time.time()
frame_num = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
for i in range(frame_num):
video_capture.set(cv2.CAP_PROP_POS_FRAMES, i)
flag, frame = video_capture.read()
assert flag
t3 = time.time()
video_capture.release()
print(t1 - t0) # ==> 76.3 s
print(t3 - t2) # ==> 345.1 s
讀取函數(shù)(重點(diǎn))
上面我們使用了兩種方法讀取視頻幀,第一種是使用read()進(jìn)行暴力遍歷,第二種是使用set()設(shè)置幀號(hào),再使用read()讀取。兩種方法讀取到的結(jié)果完全一樣,但是效率在不同的情況下各有優(yōu)勢(shì),所以為了最大化發(fā)揮兩者的優(yōu)勢(shì),在寫讀取幀函數(shù)時(shí),就要把兩種方式都寫進(jìn)去,由參數(shù)來決定使用哪種模式,這樣用戶可以針對(duì)電腦的硬件做一些簡(jiǎn)單實(shí)驗(yàn)后自行決定。
# -*- coding: utf-8 -*-
import os
import cv2
def _extract_frame_mode_1(video_capture, frame_list, root_folder, ext='png'):
"""
extract video frames and save them to disk. this method will go through all
the frames using video_capture.read()
Parameters:
-----------
video_capture: obtained by cv2.VideoCapture()
frame_list: list
list of frame numbers
root_folder: str
root folder to save frames
ext: str
extension of filename
"""
frame_list = sorted(frame_list)
video_capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
cnt = -1
index = 0
while True:
cnt += 1
flag, frame = video_capture.read()
if not flag:
break
if cnt == frame_list[index]:
filename = os.path.join(root_folder, str(cnt) + '.' + ext)
cv2.imwrite(filename, frame)
index += 1
def _extract_frame_mode_2(video_capture, frame_list, root_folder, ext='png'):
"""
extract video frames and save them to disk. this method will use
video_capture.set() to locate the frame position and then use
video_capture.read() to read
Parameters:
-----------
video_capture: obtained by cv2.VideoCapture()
frame_list: list
list of frame numbers
root_folder: str
root folder to save frames
ext: str
extension of image filename
"""
for i in frame_list:
video_capture.set(cv2.CAP_PROP_POS_FRAMES, i)
flag, frame = video_capture.read()
assert flag
filename = os.path.join(root_folder, str(i) + '.' + ext)
cv2.imwrite(filename, frame)
def extract_frame(video_path, increment=None, frame_list=None,
mode=1, ext='png'):
"""
extract video frames and save them to disk. the root folder to save frames
is same as video_path (without extension)
Parameters:
-----------
video_path: str
video path
increment: int of 'fps'
increment of frame indexes
frame_list: list
list of frame numbers
mode: int, 1 or 2
1: go through all the frames using video_capture.read()
2: use video_capture.set() to locate the frame position and then use
video_capture.read() to read
ext: str
extension of image filename
"""
video_capture = cv2.VideoCapture(video_path)
frame_num = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
if increment is None:
increment = 1
elif increment == 'fps':
fps = video_capture.get(cv2.CAP_PROP_FPS)
increment = round(fps)
if frame_list is None:
frame_list = [i for i in range(0, frame_num, increment)]
if frame_num // len(frame_list) > 5 and mode == 1:
print("the frames to be extracted is too sparse, "
"please consider setting mode = 2 to accelerate")
root_folder = os.path.splitext(video_path)[0]
os.makedirs(root_folder, exist_ok=True)
if mode == 1:
_extract_frame_mode_1(video_capture, frame_list, root_folder, ext)
elif mode == 2:
_extract_frame_mode_2(video_capture, frame_list, root_folder, ext)
video_capture.release()
if __name__ == '__main__':
video_path = r'D:\peppa\Muddy_Puddles.mp4'
extract_frame(video_path, increment=30, mode=2)
將圖像寫為視頻
寫視頻沒有那么多需要注意的地方,主要使用的接口函數(shù)是cv2.VideoWriter(video_path, fourcc, fps, size),該函數(shù)的主要注意點(diǎn)是入?yún)⒌脑O(shè)置,video_path是輸出視頻的文件名,fps是幀率,size是視頻的寬高,待寫入視頻的圖像的尺寸必需與size一致。其中不太容易理解的是與視頻編碼相關(guān)的fourcc,該參數(shù)的設(shè)置需要使用另外一個(gè)接口函數(shù):cv2.VideoWriter_fourcc(c1, c2, c3, c4),c1-c4分別是四個(gè)字符。
示例
因?yàn)楂@取圖像的方式多種多樣,而寫視頻又比較簡(jiǎn)單,所以不太適合將這部分寫成函數(shù),下面以一個(gè)例子呈現(xiàn)。
video_path = r'D:\peppa\Muddy_Puddles.avi'
root_folder = r'D:\peppa\Muddy_Puddles'
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
fps = 25
size = (1920, 1080)
video_writer = cv2.VideoWriter(video_path, fourcc, fps, size)
for i in range(0, 7788, 30):
filename = os.path.join(root_folder, str(i) + '.png')
image = cv2.imread(filename)
video_writer.write(image)
video_writer.release()
fourcc
fourcc有時(shí)候需要多嘗試一下,因?yàn)椴煌娔X里安裝的編解碼器可能不太一樣,不見得隨便設(shè)置一個(gè)參數(shù)就一定能成功,fourcc有非常多,比如:
paramters | codec | extension |
---|---|---|
(‘P’,‘I’,‘M’,‘1’) | MPEG-1 | avi |
(‘M’,‘J’,‘P’,‘G’) | motion-jpeg | mp4 |
(‘M’,‘P’,‘4’,‘V’) | MPEG-4 | mp4 |
(‘X’,‘2’,‘6’,‘4’) | H.264 | mp4 |
(‘M’, ‘P’, ‘4’, ‘2’) | MPEG-4.2 | ? |
(‘D’, ‘I’, ‘V’, ‘3’)? | MPEG-4.3 | ? |
(‘D’, ‘I’, ‘V’, ‘X’) | MPEG-4 | avi |
(‘U’, ‘2’, ‘6’, ‘3’) | H263 | ? |
(‘I’, ‘2’, ‘6’, ‘3’)? | H263I | flv |
(‘F’, ‘L’, ‘V’, ‘1’)? | FLV1 | ? |
(‘X’,‘V’,‘I’,‘D’)? | MPEG-4 | avi |
(‘I’,‘4’,‘2’,‘0’)? | YUV | avi |
上表中的后綴名似乎并不需要嚴(yán)格遵守。
原文鏈接:https://blog.csdn.net/bby1987/article/details/108923361
相關(guān)推薦
- 2022-08-08 Android實(shí)現(xiàn)頁面跳轉(zhuǎn)_Android
- 2023-05-14 opencv繪制矩形和圓的實(shí)現(xiàn)_python
- 2022-04-16 pycharm三個(gè)有引號(hào)不能自動(dòng)生成函數(shù)注釋的問題_python
- 2022-12-04 Flutter之可滾動(dòng)組件實(shí)例詳解_IOS
- 2022-03-27 Python?提速器numba_python
- 2023-07-22 使用log4j2為日志增加代碼行號(hào)
- 2023-01-02 Android?Map數(shù)據(jù)結(jié)構(gòu)全面總結(jié)分析_Android
- 2022-11-17 React通過classnames庫(kù)添加類的方法_React
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支