使用managers.CaptureManager提取视频流
CaptureManager类能对异性差异进行抽象,并提供了更加高级的接口从获取流中分配图像,再将图像分配到一个或多个输出中(如图像文件、视频文件或窗口)。
在VideoCapture类中初始化CaptureManager类,在应用程序主循环的每一次迭代中通常应调用CaptureManager中的enterFrame()和exitFrame()函数。在调用enterFrame()和exitFrame()函数之间,应用程序可能会设定通道属性并获取帧属性。通道属性的初始值为0.只有在多台摄像头的情况下,通道属性的初始值非0,。帧属性是调用enterFrame()函数时与当前通道状态对应的图像。
可能会经常调用CapTureManager类的writeImage(),startWritingVideo()和stopWritingVideo()函数。在调用exitFrame()函数之前,会有延迟写入文件。并且,在调用existFrame()函数过程中,帧属性可能会在窗口中显示,这取决于应用程序代码是将WindowManager类作为CaptureManager的构造函数参数还是设置previewWindowManager属性。
如果应用程序代码处理了帧属性,那么在记录文件和窗口中会有所体现。CaptureManager类有一个称为shouldMirrorPreview的构造函数参数和属性,如果想要帧在窗口中镜像,但不记录在文件中,可将shouldMirrorPreciew设置为True。
Opencv不能为摄像头提供准确的帧速率。解决这个问题的方法是通过帧计数器和Python标准的time.time()函数来估计帧数率。
创建一个名为managers.py,该文件包含了CaptureM的实现,这各实现结果很长分成几段,先导入包、构造函数和属性:
import cv2
import numpy
import time
class CaptureManager(object):
def __init_(self,capture,previewWindowManager=None,shouldMirrorPreview=False):
self.previewWindowManager=previewWindowManager
self.shouldMirrorPreview=shouldMirrorPreview
self._capture=capture
self._channel=0
self._enteredFrame=False
self._frame=None
self._imageFilename=None
self._videoFilename=None
self._videoEncoding=None
self._videoWriter=None
self._startTime=None
self._framesElapsed=numpy.long(0)
self._fpsEstimate=None
def channel(self):
return self._channel
def frame(self):
if self._enteredFrame and self._frame is None:
_,self._frame=self._capture.retyieve()
return self._frame
def isWritingImage(self):
return self._imageFilename is not None
def isWritingVideo(self):
return self._videoFilename is not None
大多数成员变量为非公有变量,这类变量名前会加一个下划线进行标识。这些非公有变量与当前帧的状态以及文件写入操作有关。
Python没有石油成员变量的概念,通常在变量前面加单/双下划线来表示私有变量。通常在Python中,以单下划线开始的成员变量称为保护变量,而异双下划线开始的变量称为私有成员变量。
def enterFrame(self):
assert not self._enteredFrame
if self._capture is not None:
self._enteredFrame=self._capture.grab()
def exitFrame(self):
if self.frame is None:
self._enteredFrame=False
return
if self._enteredFrame==0:
self._startTime=time.time()
else:
timeElapsed=time.time()-self._startTime
self._fpsEstimate=self._framesElapsed/timeElapsed
self._framesElapsed+=1
if self.previewWindowManager is not None:
if self.shouldMirrorPreview:
mirroredFrame=numpy.fliplr(self._frame).copy()
self.previewWindowManager.show(mirroredFrame)
else:
self.previewWindowManager.show(self._frame)
if self.isWritingImage:
cv2.imwrite(self._imageFilename,self._frame)
self._imageFilename=None
self._videoWriter()
self._frame=None
self._enteredFrame=False
enterFrame()的实现只能(同步)获取一帧,而且会推出从一个通道获取,以便随后能从变量frame中读取。exitFrame()函数可以从当前通道获取图像、估计帧速率、通过窗口管理器显示图像,执行暂停的请求,从而向文件中写入图像。
def writeImage(self,fileneme):
self._imageFilename=fileneme
def startWritingVideo(self,filename,encoding=cv2.VideoWriter_fourcc('I','4','2','0')):
self._videoFilename=filename
self._videoEncoding=encoding
def stopWritingVideo(self):
self._videoFilename=None
self._videoEncoding=None
self._videoWriter=None
def _writeVideoFrame(self):
if not self.isWritingVideo:
return
if self._videoWriter is None:
fps=self._capture.get(cv2.CAP_PROP_FPS)
if fps==0.0:
if self._framesElapsed<20:
return
else:
fps=self._fpsEstimate
size=(int(self._capture.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(self._capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
self._videoWriter=cv2.VideoWriter(
self._videoFilename,self._videoEncoding,fps,size
)
self._videoWriter.write(self._frame)
writeImage(),startWritingVideo()和stopWritingVideo()是公有函数,简单地记录了文件的写入操作参数,实际写入会推迟到下一下exitFrame()函数。