Cameo項目介紹: 1、實時捕獲並顯示攝像頭幀。 2、具備截圖、保存視頻和退出三個功能鍵。 要求存在文件:manager.py 和 cameo.py 一、manager.py 兩個類:CaptureManager、WindowManager CaptureManager負責攝像頭幀的捕獲,編解碼得 ...
Cameo項目介紹:
1、實時捕獲並顯示攝像頭幀。
2、具備截圖、保存視頻和退出三個功能鍵。
要求存在文件:manager.py 和 cameo.py
一、manager.py
兩個類:CaptureManager、WindowManager
CaptureManager負責攝像頭幀的捕獲,編解碼得到實際幀,當前幀保存為圖片、一段時間內的幀保存為視頻這四個核心功能。
CaptureManager負責視窗的創建、視窗展示當前畫面、三個功能鍵的交互、關閉視窗釋放資源這四個個功能
二、cameo.py
程式入口,關聯調用CaptureManager和CaptureManager,並定義三個功能鍵
詳細方法實現參照下述代碼和註釋
manager.py
1 from __future__ import annotations 2 import cv2 3 import numpy 4 import time 5 6 ''' 7 1、允許同一文件下不同類之間的類型提示,py3.7以上特性 8 2、cv2——獲取攝像頭和展示 9 3、numpy——對展示畫面進行左右翻轉(fliplr) 10 4、time——精確獲取時間間隔,然後計算幀率估值 11 ''' 12 13 14 class CaptureManager: 15 def __init__(self, capture: cv2.VideoCapture, 16 previewWindowManager: WindowManager = None, 17 shouldMirrorPreview: bool = False): 18 self.previewWindowManager = previewWindowManager 19 self.shouldMirrorPreview = shouldMirrorPreview 20 21 self._capture = capture 22 self._channel = 0 23 self._enteredFrame = False 24 self._frame = None 25 26 self._imageFilename = None 27 self._videoFilename = None 28 self._videoEncoding = None 29 self._videoWriter = None 30 31 self._startTime = None 32 self._framesElapsed = 0 33 self._fpsEstimate = None 34 35 @property 36 def channel(self): 37 return self._channel 38 39 @channel.setter 40 def channel(self, value): 41 if self._channel != value: 42 self._channel = value 43 self._frame = None # 對通道賦值時需要將當前幀置空,否則可能出現通道為未更改現象 44 45 # 如果進入幀,且當前幀為None則對已捕獲的幀進行解碼和獲取實際圖像幀 46 @property 47 def frame(self): 48 if self._enteredFrame and self._frame is None: 49 _, self._frame = self._capture.retrieve(self._frame, self.channel) 50 return self._frame 51 52 @property 53 def isWritingImage(self): 54 return self._imageFilename is not None 55 56 @property 57 def isWritingVideo(self): 58 return self._videoFilename is not None 59 60 def enterFrame(self): 61 # 檢查上一幀是否被處理完,如未處理完,則會被下一幀覆蓋,最終導致實時畫面或者保存的視頻不連續 62 assert not self._enteredFrame, 'previous enterFrame() had no matching exitFrame()' 63 64 if self._capture is not None: 65 self._enteredFrame = self._capture.grab() 66 67 def exitFrame(self): 68 # 如果當前幀為None,表示未成功捕獲有效幀或視頻流已處理完成,此時則直接結束此方法 69 if self.frame is None: 70 self._enteredFrame = False 71 return 72 73 # 更新FPS估值 74 if self._framesElapsed == 0: 75 self._startTime = time.perf_counter() 76 else: 77 timeElapsed = time.perf_counter() - self._startTime 78 self._fpsEstimate = self._framesElapsed / timeElapsed 79 self._framesElapsed += 1 80 81 # 是否水平反轉畫面並展示 82 if self.previewWindowManager is not None: 83 if self.shouldMirrorPreview: 84 mirroredFrame = numpy.fliplr(self._frame) 85 self.previewWindowManager.show(mirroredFrame) 86 else: 87 self.previewWindowManager.show(self._frame) 88 89 # 將當前幀保存為圖片 90 if self.isWritingImage: 91 cv2.imwrite(self._imageFilename, self._frame) 92 self._imageFilename = None 93 94 # 將當前幀寫入視頻 95 self._writeVideoFrame() 96 97 # 釋放並退出當前幀 98 self._frame = None 99 self._enteredFrame = False 100 101 def writeImage(self, filename): 102 self._imageFilename = filename 103 104 def startWritingVideo(self, filename, encoding=cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')): 105 self._videoFilename = filename 106 self._videoEncoding = encoding 107 108 def stopWritingVideo(self): 109 self._videoFilename = None 110 self._videoEncoding = None 111 self._videoWriter = None 112 113 def _writeVideoFrame(self): 114 if not self.isWritingVideo: 115 return 116 117 # 檢查是否創建了VideoWriter對象 118 if self._videoWriter is None: 119 # 獲取攝像頭幀率 120 fps = self._capture.get(cv2.CAP_PROP_FPS) 121 # 如果幀率獲取失敗則使用估計值 122 if numpy.isnan(fps) or fps <= 0.0: 123 # 獲取更多的以處理幀數,以求得更穩定的幀率 124 if self._framesElapsed < 20: 125 return 126 else: 127 fps = self._fpsEstimate 128 size = (int(self._capture.get( 129 cv2.CAP_PROP_FRAME_WIDTH)), 130 int(self._capture.get( 131 cv2.CAP_PROP_FRAME_HEIGHT))) 132 self._videoWriter = cv2.VideoWriter( 133 self._videoFilename, self._videoEncoding, 134 fps, size) 135 136 self._videoWriter.write(self._frame) 137 138 139 class WindowManager(object): 140 141 def __init__(self, windowName: str 142 , keypressCallback=None): 143 self.keypressCallback = keypressCallback 144 145 self._windowName = windowName 146 self._isWindowCreated = False 147 148 @property 149 def isWindowCreated(self): 150 return self._isWindowCreated 151 152 def createWindow(self): 153 cv2.namedWindow(self._windowName) 154 self._isWindowCreated = True 155 156 def show(self, frame): 157 cv2.imshow(self._windowName, frame) 158 159 def destroyWindow(self): 160 cv2.destroyWindow(self._windowName) 161 self._isWindowCreated = False 162 163 def processEvents(self): 164 keycode = cv2.waitKey(1) 165 if self.keypressCallback is not None and keycode != -1: 166 self.keypressCallback(keycode)View Code
cameo.py
1 import cv2 2 from managers import WindowManager, CaptureManager 3 4 5 class Cameo: 6 def __init__(self): 7 self._windowManager = WindowManager('Cameo', 8 self.onKeypress) 9 self._captureManager = CaptureManager( 10 cv2.VideoCapture(0), self._windowManager, True) 11 12 def run(self): 13 # 創建視窗 14 self._windowManager.createWindow() 15 while self._windowManager.isWindowCreated: 16 # 開始捕獲攝像頭幀 17 self._captureManager.enterFrame() 18 # 解碼並獲取實際幀 19 frame = self._captureManager.frame 20 # 預留後續新功能 21 if frame is not None: 22 pass 23 # 寫入圖片文件或者視頻文件並釋放幀資源 24 self._captureManager.exitFrame() 25 # 檢查並返回鍵盤狀態 26 self._windowManager.processEvents() 27 28 def onKeypress(self, keycode): 29 if keycode == 32: # space 30 self._captureManager.writeImage(r'screenshot.png') 31 elif keycode == 9: # tab 32 if not self._captureManager.isWritingVideo: 33 self._captureManager.startWritingVideo('screencast.avi') 34 else: 35 self._captureManager.stopWritingVideo() 36 elif keycode == 27: # escape 37 self._windowManager.destroyWindow() 38 39 40 if __name__ == "__main__": 41 Cameo().run()View Code