学习OpenCV-Python——处理文件,摄像头,跟图形界面

读写图像文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import numpy,cv2
# 我们尝试创建一张3*3的全黑的图,每个像素都是8bit的整数(0~255)
>>> img = numpy.zeros((3,3), dtype=numpy.uint8)
>>> img
array([[0, 0, 0], [0, 0, 0], [0, 0, 0]], dtype=uint8)
# 现在我们开始创建BGR图
# 现在每个像素都被三个数组所表示,分别表示BGR
# 注:BGR和RGB是同一颜色空间,只不过reversed而已
>>> img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
>>> img
array([[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]], dtype=uint8)
>>> img.shape
(3,3,3)
# 把PNG转换为JPG
# imread()返回的是BGR格式,即使这个图像是灰度图
# 如果你是在Python IDLE中运行的。当前目录为Python的安装路径
image = cv2.imread('MyPic.png')
cv2.imwrite('MyPic.jpg', image)
# 我们可以自定义imread(),有如下枚举成员
# IMREAD_ANYCOLOR = 4
# IMREAD_ANYDEPTH = 2
# IMREAD_COLOR = 1
# IMREAD_GRAYSCALE = 0
# IMREAD_LOAD_GDAL = 8
# IMREAD_UNCHANGED = -1
grayImage = cv2.imread('MyPic.png', cv2.IMREAD_GRAYSCALE)
cv2.imwrite('MyPicGray.png', grayImage)

转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
'''
一个byte的整数(0~255)。现在很多图像应用都用一个byte代表一个channel,
OpenCV图像是2D或者3D的数组(.array type)。一个8bit的灰度图是二维数组的,一个24bitBGR图是三维数组的。
一个8bit左上角像素是白色的灰度图,image[0, 0] 是 255。
一个24bit左上角像素是蓝色的BGR图,image[0, 0] 是 [255, 0, 0]。
image.item((0, 0)) 或 image.setitem((0, 0), 128) 比 image[0, 0] 或 image[0, 0] = 128 有效率
'''
# 可以把每个channel 8bit 的图像转换为pyhton的内置对象byteArray
byteArray = bytearray(image)
# 我们可以通过 reshape 函数得到numpy数组
# 注意对于BGR图,height*width*3必须与原图相等;对于灰度图height*width必须与原图相等
grayImage = numpy.array(grayByteArray).reshape(height, width)
bgrImage = numpy.array(bgrByteArray).reshape(height, width, 3)
# 下面的例子随机生成一幅图像
import cv2
import numpy
import os
# 随机生成 120,000 bytes 的数组.
randomByteArray = bytearray(os.urandom(120000))
flatNumpyArray = numpy.array(randomByteArray)
# 转换数组为 400x300 的灰度图
image.grayImage = flatNumpyArray.reshape(300, 400)
cv2.imwrite('RandomGray.png', grayImage)
# 转换数组为 400x100 BGR图.
bgrImage = flatNumpyArray.reshape(100, 400, 3)
cv2.imwrite('RandomColor.png', bgrImage)

通过numpy.array访问图像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import cv2
import numpy as np
img = cv2.imread('MyPic.png')
img[0,0] = [255, 255, 255]
img[:, :, 1] = 0 #设置G维的值全部为0
# 前两个参数为x,y坐标。最后一个参数0,1,2分别代表BGR
print img.item(150, 120, 0)
img.itemset( (150, 120, 0), 255)
# prints 255
print img.item(150, 120, 0)
>>> print img.shape
(149, 320, 3)
#图像所有的像素数量
>>> print img.size
143040
#8 bit 无符号
>>> print img.dtype
uint8

读写视频文件

1
2
3
4
5
6
7
8
9
10
11
12
# OpenCV只支持avi的格式,而且生成的视频文件不能大于2GB,而且不能添加音频。
# 读取的每一帧都为BGR格式
import cv2
videoCapture = cv2.VideoCapture('MyInputVid.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( 'MyOutputVid.avi',cv2.VideoWriter_fourcc('I','4','2','0'), fps, size)
success, frame = videoCapture.read()
while success:
videoWriter.write(frame)
success, frame = videoCapture.read()

支持的格式

  • cv2.VideoWriter_fourcc(‘I’,’4’,’2’,’0’): This option is an uncompressed YUV encoding, 4:2:0 chroma subsampled. This encoding is widely compatible but produces large files. The file extension should be .avi.
  • cv2.VideoWriter_fourcc(‘P’,’I’,’M’,’1’): This option is MPEG-1 The file extension should be .avi.
  • cv2.VideoWriter_fourcc(‘X’,’V’,’I’,’D’): This option is MPEG-4 and a preferred option if you want the resulting video size to be average. The file extension should be .avi.
  • cv2.VideoWriter_fourcc(‘T’,’H’,’E’,’O’): This option is Ogg Vorbis. The file extension should be .ogv.
  • cv2.VideoWriter_fourcc(‘F’,’L’,’V’,’1’): This option is a Flash video. The file extension should be .flv.
  • cv2.VideoWriter_fourcc(‘U’,’2’,’6’,’3’): H263 codec
  • cv2.VideoWriter_fourcc(‘I’,’2’,’6’,’3’): H263I codec
  • cv2.VideoWriter_fourcc(‘D’,’I’,’V’,’X’): MPEG-4 codec
  • cv2.VideoWriter_fourcc(‘M’,’P’,’4’,’2’): MPEG-4.2 codec
  • cv2.VideoWriter_fourcc(‘D’,’I’,’V’,’3’): MPEG-4.3 codec

使用摄像头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cv2
cameraCapture = cv2.VideoCapture(0)
fps = 30 # an assumption
size = (int(cameraCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cameraCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
videoWriter = cv2.VideoWriter( 'MyOutputVid.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)
success, frame = cameraCapture.read()
numFramesRemaining -= 1
cameraCapture.release()
'''
在这里videoCapture的get方法不会返回fps,而是总会返回0。
官方文档解释:“When querying a property that is not supported by the backend used by the VideoCapture class, value 0 is returned.”
'''
# 我们也可以用grab() 和 retrieve()
success0 = cameraCapture0.grab()
success1 = cameraCapture1.grab()
if success0 and success1:
frame0 = cameraCapture0.retrieve()
frame1 = cameraCapture1.retrieve()

在窗口中显示图像

1
2
3
4
5
6
7
import cv2
import numpy as np
img = cv2.imread('MyPic.jpg')
cv2.imshow('my image', img)
cv2.waitKey()
# 销毁所有OpenCV创建的窗口
cv2.destroyAllWindows()

实战

将上面所学的知识,自己将OpenCV的API利用OOP的思想封装成更高层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# coding:utf-8
import time
import cv2
import numpy
class CaptureManager(object):
def __init__(self, capture, previewWindowManager=None, shouldMirrorPreview=False):
# 绘制窗口,bool
self.previewWindowManager = previewWindowManager
# 镜像旋转(在窗口中问不是在文件中),bool
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 = long(0)
# OpenCV没办法获取FPS,如果需要可以用time.time()计算
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):
"""捕获下一帧,如果有的话"""
assert not self._enteredFrame, \
'previous enterFrame() had no matching exitFrame()'
if self._capture is not None:
self._enteredFrame = self._capture.grab()
def exitFrame(self):
"""绘制窗口. 写入文件. 释放."""
if self.frame is None:
self._enteredFrame = False
return
# 获取FPS
if self._framesElapsed == 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._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):
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)
class Cameo(object):
def __init__(self):
self._windowManager = WindowManager('Cameo', 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 -> 截屏
tab -> 开始/停止录像
escape -> 退出
"""
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()

文章目录
  1. 1. 读写图像文件
  2. 2. 转换
  3. 3. 通过numpy.array访问图像
  4. 4. 读写视频文件
    1. 4.1. 支持的格式
  5. 5. 使用摄像头
  6. 6. 在窗口中显示图像
  7. 7. 实战
|