Cameo项目,使每一步都设计成该应用的一个组件(component),以使应用具有扩展性和重用性。项目实现视频播放、内容截图、关闭调用等功能。
1. 设计方法
创建CaptureManager类和WindowManager类作为高级的I/O流接口。在应用程序代码中可以使用CaptureManager来读取新的帧,并能将帧分配到一个或多个输出中,这些输出包括静止的图像文件、视频文件以及窗口(可通过WindowManager类来实现),WindowManager类使应用程序代码能以面向对象的形式处理窗口和事件。
备注:
Python没有私有成员变量的概念,通常在变量前面添加单/双下划线(_)来表示私有变量。通常在Python中,以单下划线开始的成员变量称为保护变量(即只有类对象和子类对象能访问这些变量),而以双下划线开始的变量称为私有成员变量(即只有类对象自己能访问,子类对象不能访问这个变量)
2. 实现源码
(1) managers.py内容:#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Apr 1 10:18:20 2018
@author: lu
"""
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
@property
def channel(self):
return self._channel
@channel.setter
def channel(self,value):
if self._channel != value:
self._channel = value
self._frame = None
@property
def frame(self):
if self._enteredFrame and self._frame is None:
_, self._frame = self._capture.retrieve()
return self._frame
@property
def isWritingImage(self):
return self._imageFilename is not None
@property
def isWritingVideo(self):
return self._videoFilename is not None
def enterFrame(self):
""" capture the next frame """
assert not self._enteredFrame,'previous enterFrame() had no matching exitFrame()'
if self._capture is not None:
self._enteredFrame = self._capture.grab()
def exitFrame(self):
""" Draw to the window. Write to files. Release the frame"""
#check whether any grabbed frame is retrievable
if self.frame is None:
self._enteredFrame = False
return
#update the FPS estimate and related variables
if self._framesElapsed == 0:
self._startTime = time.time()
else:
timeElapsed = time.time() - self._startTime
self._fpsEstimate = self._framesElapsed / timeElapsed
self._framesElapsed += 1
#draw to the window, if any.
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)
#write to the image file, if any
if self.isWritingImage:
cv2.imwrite(self._imageFilename,self._frame)
self._imageFilename = None
#write to the video file, if any
self._writeVideoFrame()
#release the frame
self._frame = None
self._enteredFrame = False;
def writeImage(self, filename):
"""write the next exited frame to an image file"""
self._imageFilename = filename
"""stop writing exited frame to a video file"""
def startWritingVideo(self, filename,
encoding = cv2.VideoWriter_fourcc('I','4','2','0')):
self._videoFilename = filename
self._videoEncoding = encoding
def stopWritingVideo(self):
"""stop writing exited frames to a video file """
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:
#the capture's FPS is unknow so use an estimate
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)
class WindowManager(object):
def __init__(self, windowName, keypresscallback = None):
self.keypressCallback = keypresscallback
self._windowName = windowName
self._isWindowCreated = False
@property
def isWindowCreated(self):
return self._isWindowCreated
def createWindow(self):
cv2.namedWindow(self._windowName)
self._isWindowCreated = True
def show(self, frame):
cv2.imshow(self._windowName,frame)
def destroyWindow(self):
cv2.destroyWindow(self._windowName)
self._isWindowCreated = False
def processEvents(self):
keycode = cv2.waitKey(1)
if self.keypressCallback is not None and keycode != -1:
keycode &= 0xFF
self.keypressCallback(keycode)
(2) main.py内容import numpy as np
import cv2
from managers import WindowManager, CaptureManager
class Cameo(object):
def __init__(self):
self._windowManager = WindowManager('Camo', self.onKeypress)
self._captureManager = CaptureManager(cv2.VideoCapture(0), self._windowManager, True)
def run(self):
self._windowManager.createWindow()
while self._windowManager.isWindowCreated:
self._captureManager.enterFrame()
frame = self._captureManager.frame
self._captureManager.exitFrame()
self._windowManager.processEvents()
def onKeypress(self, keycode):
"""
space -> Take a screenshot
tab -> start/stop recording a creencast
escape -> quit
"""
if keycode == 32: #space
self._captureManager.writeImage('screenshot.png')
elif keycode == 9: #tab
if not self._captureManager.isWritingVideo:
self._captureManager.startWritingVideo('screencast.avi')
else:
self._captureManager.stopWritingVideo()
elif keycode == 27: #escape
self._windowManager.destroyWindow()
if __name__=="__main__":
Cameo().run()
3. 实现效果图:
注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处。