第二章
使用numpy.array访问图像数据
改变一个特定像素的值:
- numpy.array提供的item()
- item(x,y,id)
- id为索引,B G R
- itemset()
- itemset((x,y,id),val)
- (x,y,索引,要设定的值)
操作通道:将指定通道所有值置0
import cv2
import numpy as np
img = cv2.imread('1.png')
img[:,:,1] = 0
视频文件的读/写
- OpenCV提供了
VideoCapture
类和VideoWriter
类支持各种格式的视频文件(都支持AVI格式),到达视频文件末尾前,可通过read()获取新的帧,每一帧是一幅基于BGR格式的图像
import cv2
videoCapture = cv2.VideoCapture('2_1.avi')
fps = videoCapture.get(cv2.CAP_PROP_FPS)#帧速率
size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
videoWriter = cv2.VideoWriter('2_1_copy.avi',cv2.VideoWriter_fourcc('I','4','2','0'),fps,size)
sucess , frame = videoCapture.read()
while sucess:
videoWriter.write(frame)
sucess , frame = videoCapture.read()
其中编解码器
的可用性常用选项:
cv2.VideoWriter_fource('I','4','2','0')
:该选项是一个未压缩的YUV颜色编码,4:2:0色度子采样。这种编码有很好地兼容性,但会产生较大文件
,文件扩展名.avi
(例子视频145K,产生了12.4M文件)- cv2.VideoWriter_fource(‘P’,‘I’,‘M’,‘1’): 生成
.avi
- cv2.VideoWriter_fource(‘X’,‘V’,‘I’,‘D’):MPEG-4编码类型,生成
.avi
(若希望视频大小为平均值
,推荐使用) - cv2.VideoWriter_fource(‘F’,‘L’,‘V’,‘1’):该选项是一个Flash视频,扩展名
.flv
捕获摄像头的帧
import cv2
cameraCapture = cv2.VideoCapture(0)
#为了针对摄像头创建合适的VideoWriter类,
#要么对帧速率作出假设,
#要么使用计时器测量
fps = 30
size = (int(cameraCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(cameraCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
videoWriter = cv2.VideoWriter('2_1_catch_camera.avi',cv2.VideoWriter_fourcc('I','4','2','0'),fps,size)
success , frame = cameraCapture.read()
numFramesRemaining = 10 * fps - 1
while success and numFramesRemaining > 0 :
videoWriter.write(frame)
#需要同步一组摄像头,read()方法不适用,可用grab(),retrive()替代
success , frame = cameraCapture.read()
numFramesRemaining -= 1
cameraCapture.release()
imshow在窗口显示图像
import cv2
import numpy as np
img = cv2.imread('2_1.jpg')
cv2.imshow('2_1.jpg',img)
cv2.waitKey()#保证显示视频时窗口上帧可以一直更新
cv2.destroyAllWindows()
在窗口显示摄像头帧
- 任意窗口下都可以通过waitKey()获取键盘输入;
- 通过setMouseCallback()获取鼠标输入。
#实时显示摄像头帧
#OpenCV不提供任何处理窗口事件的方法,单击窗口关闭时,并不能关闭应用程序
import cv2
clicked = False
#鼠标响应函数
"""
flags: 代表鼠标的拖拽事件,以及键盘鼠标联合事件
param:函数指针 标识了所响应的事件函数,相当于自定义了一个OnMouseAction()函数的ID。
"""
def onMouse( event , x , y , flags , param ):
global clicked
if event == cv2.EVENT_LBUTTONUP:
clicked = True
cameraCapture = cv2.VideoCapture(0)
cv2.namedWindow('MyWindow')
#鼠标回调函数,param为可选参数
"""
C++中原型:
void setMouseCallback(const string& winname, //图像视窗名称
MouseCallback onMouse, //鼠标响应函数,监视到鼠标操作后调用并处理相应动作
void* userdata = 0 //鼠标响应处理函数的ID,识别号,默认0
);
"""
cv2.setMouseCallback('MyWindow',onMouse)
print('Showing camera feed. Click window or press any key to stop.')
sucess , frame = cameraCapture.read()
#waitKey()参数为等待键盘触发的时间,单位ms,返回值-1(表示没有被按下)
#某些系统waitKey()返回值可能比ASCII更大,可通过读取返回值最后一个字节
#保证只提取ASCII码:
# keycode = cv2.waitKey(1)
# if keycode != -1 :
# keycode &= 0xFF
while sucess and cv2.waitKey(1) == -1 and not clicked:
cv2.imshow('MyWindow',frame)
sucess , frame = cameraCapture.read()
cv2.destroyWindow('MyWindow')
cameraCapture.release()
Cameo-面向对象设计
- 使用多个I/O流
- 创建CaptureManager类和WindowManager类作为高级的I/O流接口
使用managers.CaptureManager提取视频流
使用windowManager抽象窗口和键盘
监听键盘和鼠标事件:进行截图,截屏
运行时,摄像头帧被镜像,存储图片也被镜像(默认设置镜像为True)
managers.py
#managers.CaptureManager提取视频流
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._mirroredframe = 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() #retrieve()是解码并返回一个帧,grab是指向下一个帧(不需要当前帧时可跳过),read是grab和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
#check previous frame was existed.
# 检查上一帧是否退出
assert not self._enteredFrame , \
'previous enterFrame() had no matching exitFrame()'
if self._capture is not None :
self._enteredFrame = self._capture.grab() #用grab()指向下一帧
def exitFrame(self):
#draw to the window ,write to files , release the frame.
if self.frame is None:
self._enteredFrame = False
return
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 self.previewWindowManager is not None:
if self.shouldMirrorPreview:
self._mirroredframe = numpy.fliplr(self._frame).copy()
self.previewWindowManager.show(self._mirroredframe)
else:
self.previewWindowManager.show(self._frame)
if self.isWritingImage:
if self.shouldMirrorPreview:
cv2.imwrite(self._imageFilename,self._mirroredframe)
else :
cv2.imwrite(self._imageFilename, self._frame)
self._imageFilename = None
self._writeVideoFrame()
self._frame = None
self._enteredFrame = False
def writeImage(self , filename):
self._imageFilename = filename
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)
#抽象窗口和键盘
class WindowManager(object):
def __init__(self,windowName,keypressCallback = None,mousepressCallback = None):
self.keypressCallback = keypressCallback
self.mousepressCallback = mousepressCallback #鼠标
self._windowName = windowName
self._isWindowCreated = False
@property
def isWindowCreated(self):
return self._isWindowCreated
def createWindow(self):
cv2.namedWindow(self._windowName)
self._isWindowCreated = True
cv2.setMouseCallback(self._windowName,self.mousepressCallback) #鼠标事件
def show(self , frame):
cv2.imshow(self._windowName,frame)
def destoryWindow(self):
cv2.destroyWindow(self._windowName)
self._isWindowCreated = False
def processEvent(self):
keycode = cv2.waitKey(1)
if self.keypressCallback is not None and keycode != -1:
keycode &= 0xFF
self.keypressCallback(keycode)
#python内置的@property装饰器就是负责把一个方法变成属性调用的
cameo.py
import cv2
from managers import WindowManager , CaptureManager
class Cameo(object):
def __init__(self):
self._windowManager = WindowManager('cameo',self.onKeypress,self.onMouse)
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() #draw to the window ,write to files , release the frame.
self._windowManager.processEvent() #处理键盘事件
def onKeypress(self , keycode): #键盘响应
"""
Handle a keypress.
space -> Take a screenshot
tab -> Start/Stop recording a screencast
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.destoryWindow()
def onMouse(self , event, x, y, flags, param): #鼠标响应函数
if event == cv2.EVENT_LBUTTONUP:
self._windowManager.destoryWindow()
if __name__=="__main__":
Cameo().run()