<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
通過video_capture = cv2.VideoCapture(video_path)可以獲取讀取視訊的控制程式碼。而後再通過flag, frame = video_capture.read()可以讀取當前幀,flag表示讀取是否成功,讀取成功後,控制程式碼會自動移動到下一幀的位置。讀取結束後使用video_capture.release()釋放控制程式碼。
一個簡單的逐幀讀取的程式如下:
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()
為了能更好更靈活地瞭解並讀取視訊,我們有時候需要獲取視訊的一些資訊,比如影格率,總幀數等等。獲取這些資訊的方法是呼叫video_capture.get(PROP_ID)方法,其中PROP_ID是OpenCV定義的一些常數。
常用的資訊及範例如下:
import cv2 video_path = r'D:peppaMuddy_Puddles.mp4' video_capture = cv2.VideoCapture(video_path) frame_num = video_capture.get(cv2.CAP_PROP_FRAME_COUNT) # ==> 總幀數 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) # ==> 設定控制程式碼位置 pos = video_capture.get(cv2.CAP_PROP_POS_FRAMES) # ==> 此時 pos = 1000.0 video_capture.release()
控制程式碼位置指的是下一次呼叫read()方法讀取到的幀號,幀號索引從0開始。
從上面程式碼中可以看到我們使用了set方法來設定控制程式碼的位置,這個功能在讀取指定幀時很有用,這樣我們不必非要使用read()遍歷到指定位置。
但問題來了,這種方式讀取到的內容和read()遍歷讀取到的內容是否完全相同?
做個簡單的實驗,下面用兩種方法分別讀取同一個視訊的[100, 200)幀,然後檢查讀取的內容是否完全相同,結果是True。
import cv2 import numpy as np video_path = r'D:peppaMuddy_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讀取的效率。還是利用小豬佩奇第一集做實驗,這個視訊共7788幀,下面分別用兩種方法遍歷讀取視訊中所有幀。第二種方法明顯比第一種慢得多,所以這就很苦逼了。。。如果幀間隔比較小的話,單純用read()進行遍歷效率高;如果幀間隔比較大的話,用set()設定位置,然後read()讀取效率高。
(如果給第二種方法加個判斷,每隔n幀讀取一次,那麼效率確實會提高n倍,可以自行嘗試)
import cv2 import numpy as np import time video_path = r'D:peppaMuddy_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
上面我們使用了兩種方法讀取視訊幀,第一種是使用read()進行暴力遍歷,第二種是使用set()設定幀號,再使用read()讀取。兩種方法讀取到的結果完全一樣,但是效率在不同的情況下各有優勢,所以為了最大化發揮兩者的優勢,在寫讀取幀函數時,就要把兩種方式都寫進去,由引數來決定使用哪種模式,這樣使用者可以針對電腦的硬體做一些簡單實驗後自行決定。
# -*- 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:peppaMuddy_Puddles.mp4' extract_frame(video_path, increment=30, mode=2)
寫視訊沒有那麼多需要注意的地方,主要使用的介面函數是cv2.VideoWriter(video_path, fourcc, fps, size),該函數的主要注意點是入參的設定,video_path是輸出視訊的檔名,fps是影格率,size是視訊的寬高,待寫入視訊的影象的尺寸必需與size一致。其中不太容易理解的是與視訊編碼相關的fourcc,該引數的設定需要使用另外一個介面函數:cv2.VideoWriter_fourcc(c1, c2, c3, c4),c1-c4分別是四個字元。
因為獲取影象的方式多種多樣,而寫視訊又比較簡單,所以不太適合將這部分寫成函數,下面以一個例子呈現。
video_path = r'D:peppaMuddy_Puddles.avi' root_folder = r'D:peppaMuddy_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有非常多,比如:
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 |
上表中的字尾名似乎並不需要嚴格遵守。
以上就是Python+OpenCV讀寫視訊的方法詳解的詳細內容,更多關於Python OpenCV讀寫視訊的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45