Python+OpenCV讀寫(xiě)視頻的方法詳解
讀視頻,提取幀
接口函數(shù):cv2.VideoCapture()
通過(guò)video_capture = cv2.VideoCapture(video_path)可以獲取讀取視頻的句柄。而后再通過(guò)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開(kāi)始。
使用set(cv2.CAP_PROP_POS_FRAMES)讀取指定幀
從上面代碼中可以看到我們使用了set方法來(lái)設(shè)置句柄的位置,這個(gè)功能在讀取指定幀時(shí)很有用,這樣我們不必非要使用read()遍歷到指定位置。
但問(wèn)題來(lái)了,這種方式讀取到的內(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
接下來(lái)看看利用set讀取的效率。還是利用小豬佩奇第一集做實(shí)驗(yàn),這個(gè)視頻共7788幀,下面分別用兩種方法遍歷讀取視頻中所有幀。第二種方法明顯比第一種慢得多,所以這就很苦逼了。。。如果幀間隔比較小的話(huà),單純用read()進(jìn)行遍歷效率高;如果幀間隔比較大的話(huà),用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ì),在寫(xiě)讀取幀函數(shù)時(shí),就要把兩種方式都寫(xiě)進(jìn)去,由參數(shù)來(lái)決定使用哪種模式,這樣用戶(hù)可以針對(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)
將圖像寫(xiě)為視頻
寫(xiě)視頻沒(méi)有那么多需要注意的地方,主要使用的接口函數(shù)是cv2.VideoWriter(video_path, fourcc, fps, size),該函數(shù)的主要注意點(diǎn)是入?yún)⒌脑O(shè)置,video_path是輸出視頻的文件名,fps是幀率,size是視頻的寬高,待寫(xiě)入視頻的圖像的尺寸必需與size一致。其中不太容易理解的是與視頻編碼相關(guān)的fourcc,該參數(shù)的設(shè)置需要使用另外一個(gè)接口函數(shù):cv2.VideoWriter_fourcc(c1, c2, c3, c4),c1-c4分別是四個(gè)字符。
示例
因?yàn)楂@取圖像的方式多種多樣,而寫(xiě)視頻又比較簡(jiǎn)單,所以不太適合將這部分寫(xiě)成函數(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里安裝的編解碼器可能不太一樣,不見(jiàn)得隨便設(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)格遵守。
以上就是Python+OpenCV讀寫(xiě)視頻的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Python OpenCV讀寫(xiě)視頻的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python調(diào)用Prometheus監(jiān)控?cái)?shù)據(jù)并計(jì)算
Prometheus是一套開(kāi)源監(jiān)控系統(tǒng)和告警為一體,由go語(yǔ)言(golang)開(kāi)發(fā),是監(jiān)控+報(bào)警+時(shí)間序列數(shù)據(jù)庫(kù)的組合。本文將介紹Python如何調(diào)用Prometheus實(shí)現(xiàn)數(shù)據(jù)的監(jiān)控與計(jì)算,需要的可以參考一下2021-12-12NumPy統(tǒng)計(jì)函數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了NumPy統(tǒng)計(jì)函數(shù)的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01Python+Dlib+Opencv實(shí)現(xiàn)人臉采集并表情判別功能的代碼
這篇文章主要介紹了Python+Dlib+Opencv實(shí)現(xiàn)人臉采集并表情判別,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07python 通過(guò)麥克風(fēng)錄音 生成wav文件的方法
今天小編就為大家分享一篇python 通過(guò)麥克風(fēng)錄音 生成wav文件的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01使用pyscript在網(wǎng)頁(yè)中撰寫(xiě)Python程式的方法
本文主要介紹了使用pyscript在網(wǎng)頁(yè)中撰寫(xiě)Python程式的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05使用?OpenCV?開(kāi)發(fā)虛擬鍵盤(pán)的方法
OpenCV是一個(gè)強(qiáng)大的圖像處理工具,用于機(jī)器學(xué)習(xí)、圖像處理等的跨平臺(tái)開(kāi)源庫(kù),用于開(kāi)發(fā)實(shí)時(shí)計(jì)算機(jī)視覺(jué)應(yīng)用程序,本文重點(diǎn)給大家介紹使用?OpenCV?開(kāi)發(fā)虛擬鍵盤(pán)的方法,感興趣的朋友一起看看吧2021-11-11