第 26 章
OpenCV 的摄影功能
VideoCapture 类别 · VideoWriter 类别 · 播放影片 · 读取摄影功能属性
本章介绍 OpenCV 与摄影机、影片档案相关的基本操作,包含读取摄影镜头影像、储存影片、播放影片,以及读取或设定摄影功能的属性。
26-1
启用摄影机功能 VideoCapture 类别
一般计算机通常在屏幕上方内建摄影机,也可能另外连接外部摄影机。OpenCV 提供摄影功能,可以读取并显示摄影机镜头的影像内容。
26-1-1 VideoCapture
OpenCV 的 VideoCapture 类别建构函数可以初始化摄影功能,语法如下:
capture = cv2.VideoCapture(index)
参数 index 是摄影机镜头的索引编号。一般计算机只有一台内建摄影机时,通常设为 0,表示使用内建镜头。
capture = cv2.VideoCapture(0)
建立 VideoCapture 对象后,如果计算机安装多个镜头,可改变索引编号尝试开启其它摄影镜头。
26-1-2 检测摄影功能是否开启成功
isOpened() 函数可以检测摄影功能是否开启成功,语法如下:
retval = capture.isOpened()
如果摄影功能开启成功,isOpened() 回传 True;如果失败则回传 False。
26-1-3 读取摄影镜头的影像内容
摄影机可以撷取影片,所以可以说影片是由一系列影像组成。VideoCapture 类别提供 read() 函数读取影像。
retval, frame = capture.read()
| 参数 | 说明 |
retval | 如果读取影像成功回传 True,否则回传 False。 |
frame | 如果读取影像成功,frame 可以显示回传的结果,可使用 imshow() 显示影像。 |
注
在摄影术语中,通常将单一影像称为帧(Frame)。
26-1-4 关闭摄影功能
OpenCV 官方手册强调,使用摄影功能结束后需要关闭摄影功能。假设 capture 已经建立,可以使用下列函数关闭摄影功能。
capture.release()
因此,启用摄影功能的程序语法可以写成:
capture = cv2.VideoCapture(0)
...
capture.release()
26-1-5 读取影像的基础实例
程序实例 ch26_1.py:读取影像的基础实例,执行时会开启一个 Frame 视窗显示摄影机录制的影像,按 Esc 键结束程序。
# ch26_1.py
import cv2
capture = cv2.VideoCapture(0) # 初始化摄影功能
while(capture.isOpened()):
ret, frame = capture.read() # 读取摄影镜头的影像
cv2.imshow('Frame', frame) # 显示摄影镜头的影像
c = cv2.waitKey(1) # 等待时间 1 毫秒 ms
if c == 27: # 按 Esc 键,结束
break
capture.release() # 关闭摄影功能
cv2.destroyAllWindows()
程序实例 ch26_2.py:重新设计 ch26_1.py,显示 2 个视窗,一个显示彩色影像,另一个显示灰阶影像。
# ch26_2.py
import cv2
capture = cv2.VideoCapture(0) # 初始化摄影功能
while(capture.isOpened()):
ret, frame = capture.read() # 读取摄影镜头的影像
cv2.imshow('Frame', frame) # 显示彩色影像
# 转灰阶显示
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray Frame', gray_frame) # 显示灰阶影像
c = cv2.waitKey(1) # 等待时间 1 毫秒 ms
if c == 27: # 按 Esc 键,结束
break
capture.release() # 关闭摄影功能
cv2.destroyAllWindows()
26-1-6 影像翻转
OpenCV 的 flip() 函数可以执行影像翻转,语法如下:
new_image = cv2.flip(image, flipCode)
flipCode | 意义 |
1 | 水平翻转 |
0 | 垂直翻转 |
-1 | 水平垂直翻转 |
程序实例 ch26_3.py:影像水平翻转。
# ch26_3.py
import cv2
capture = cv2.VideoCapture(0) # 初始化摄影功能
while(capture.isOpened()):
ret, frame = capture.read() # 读取摄影镜头的影像
cv2.imshow('Frame', frame) # 显示彩色影像
h_frame = cv2.flip(frame, 1) # 水平翻转
cv2.imshow('Flip Frame', h_frame) # 显示水平翻转
c = cv2.waitKey(1) # 等待时间 1 毫秒 ms
if c == 27: # 按 Esc 键,结束
break
capture.release() # 关闭摄影功能
cv2.destroyAllWindows()
26-1-7 保存某一瞬间的影像
可以使用 OpenCV 的 imwrite() 函数保存摄影期间某一瞬间的影像,也就是将特定时刻的影像储存为图档。
程序实例 ch26_4.py:按 Enter 键时,保存当前帧并写入 mypict.png。
# ch26_4.py
import cv2
capture = cv2.VideoCapture(0) # 初始化摄影功能
while(capture.isOpened()):
ret, frame = capture.read() # 读取摄影镜头的影像
cv2.imshow('Frame', frame) # 显示摄影镜头的影像
c = cv2.waitKey(1) # 等待时间 1 毫秒 ms
if c == 13: # 按 Enter 键
cv2.imwrite('mypict.png', frame)
cv2.imshow('My picture', frame)
if c == 27: # 按 Esc 键
break
capture.release() # 关闭摄影功能
cv2.destroyAllWindows()
程序执行后,可以在 ch26 资料夹看到 mypict.png 图档。
26-2
使用 VideoWriter 类别执行录影
如果要记录摄影机所拍摄的内容,可以使用 VideoWriter 类别将影像保存为影片档案。
26-2-1 VideoWriter 类别
VideoWriter() 建构函数可以初始化输出影片对象,语法如下:
video_out = cv2.VideoWriter(filename, fourcc, fps, frameSize, isColor)
| 参数 | 说明 |
filename | 输出影片档案的名称。 |
fourcc | 用 4 个字符表示影片编码、解码格式。 |
fps | 每秒帧数。例如常用设定为 20.0。 |
frameSize | 每一帧的宽度和高度,格式如 (640, 480)。 |
isColor | 可选参数,是否为彩色,默认是 True。 |
26-2-2 影片编码格式 VideoWriter_fourcc()
设定录影时的编码格式,需要使用 VideoWriter_fourcc() 函数。
| 方法 | 说明 |
VideoWriter_fourcc('I','4','2','0') | 未压缩 YUV 编码,相容性佳,但需要较多磁盘空间。 |
VideoWriter_fourcc('P','I','M','1') | MPEG-1 编码。 |
VideoWriter_fourcc('X','V','I','D') | MPEG-4 编码。 |
VideoWriter_fourcc('T','H','E','O') | Ogg Vorbis 编码。 |
VideoWriter_fourcc('F','L','V','1') | Flash 视频编码。 |
如果使用 MPEG-4 编码,可以写成:
fourcc = cv2.VideoWriter_fourcc(*'XVID')
video_out = cv2.VideoWriter('out.avi', fourcc, 20.0, (640, 480))
26-2-3 写入帧的功能 write()
write() 函数可以将一帧写入 VideoWriter 对象。
video_out.write(frame)
26-2-4 保存动态影片实例
程序实例 ch26_5.py:将动态影像保存,储存为 out26_5.avi。
# ch26_5.py
import cv2
capture = cv2.VideoCapture(0) # 初始化摄影功能
fourcc = cv2.VideoWriter_fourcc(*'XVID') # MPEG-4
# 建立输出对象
video_out = cv2.VideoWriter('out26_5.avi', fourcc, 20.0, (640, 480))
while(capture.isOpened()):
ret, frame = capture.read()
if ret:
video_out.write(frame) # 写入影片对象
cv2.imshow('frame', frame) # 显示摄影镜头影像
c = cv2.waitKey(1) # 等待时间 1 毫秒 ms
if c == 27: # 按 Esc 键结束
break
capture.release() # 关闭摄影功能
video_out.release() # 关闭输出对象
cv2.destroyAllWindows()
执行后可在 ch26 资料夹看到 out26_5.avi 影片档案。
26-3
播放影片
26-3-1 播放所录制的影片
播放影片同样使用 VideoCapture 类别。若要播放影片档案,VideoCapture() 的参数改为影片档名。
video = cv2.VideoCapture(fn)
程序实例 ch26_6.py:播放 out26_5.avi 影片档案。
# ch26_6.py
import cv2
video = cv2.VideoCapture('out26_5.avi') # 开启影片档案
while(video.isOpened()):
ret, frame = video.read() # 读取影片档案
if ret:
cv2.imshow('frame', frame) # 显示影片
else:
break
c = cv2.waitKey(50) # 可控制播放速度
if c == 27: # 按 Esc 键结束
break
video.release() # 关闭输出对象
cv2.destroyAllWindows()
26-3-2 播放 iPhone 所录制的影片
OpenCV 支持许多影片播放格式,例如 iPhone 录制的影片扩展名是 mov,也可以使用同样方式播放。
程序实例 ch26_7.py:播放 iceocean.mov 档案。
# ch26_7.py
import cv2
video = cv2.VideoCapture('iceocean.mov') # 开启影片档案
while(video.isOpened()):
ret, frame = video.read() # 读取影片档案
if ret:
cv2.imshow('frame', frame) # 显示影片
else:
break
c = cv2.waitKey(50) # 可控制播放速度
if c == 27: # 按 Esc 键结束
break
video.release()
cv2.destroyAllWindows()
26-3-3 灰阶播放影片
如果要显示灰阶影片,概念和 ch26_2.py 相同,先将读取到的帧转为灰阶再显示。
程序实例 ch26_8.py:同时显示彩色影片与灰阶影片。
# ch26_8.py
import cv2
video = cv2.VideoCapture('iceocean2.mov') # 开启影片档案
while(video.isOpened()):
ret, frame = video.read() # 读取影片档案
if ret == True:
cv2.imshow('frame', frame) # 显示彩色影片
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray_frame', gray_frame) # 显示灰阶影片
else:
break
c = cv2.waitKey(50) # 可控制播放速度
if c == 27: # 按 Esc 键结束
break
video.release() # 关闭输出对象
cv2.destroyAllWindows()
26-3-4 暂停和继续播放
程序实例 ch26_9.py:按空白键可以暂停播放,如果再按一次空白键可以恢复播放。
# ch26_9.py
import cv2
video = cv2.VideoCapture('iceocean.mov') # 开启影片档案
while(video.isOpened()):
ret, frame = video.read()
if ret: # 读取影片档案
cv2.imshow('frame', frame) # 显示影片
c = cv2.waitKey(50) # 可控制播放速度
else:
break
if c == 32: # 空白键
cv2.waitKey(0) # 等待按键发生
continue
if c == 27: # 按 Esc 键结束
break
video.release() # 关闭输出对象
cv2.destroyAllWindows()
26-3-5 更改显示视窗大小
resizeWindow() 函数可以更改显示视窗的大小,语法如下:
cv2.resizeWindow(windowName, width, height)
参数 windowName 是视窗名称,width 是视窗宽度,height 是视窗高度。更改视窗大小前,应先使用 namedWindow() 建立视窗。
程序实例 ch26_10.py:将视窗更改为宽 300、高 300,显示 iceocean.mov。
# ch26_10.py
import cv2
video = cv2.VideoCapture('iceocean.mov') # 开启影片档案
while(video.isOpened()):
ret, frame = video.read() # 读取影片档案
if ret:
cv2.namedWindow('myVideo', cv2.WINDOW_NORMAL)
cv2.resizeWindow('myVideo', 300, 300)
cv2.imshow('myVideo', frame) # 显示影片
else:
break
c = cv2.waitKey(50) # 可控制播放速度
if c == 27: # 按 Esc 键结束
break
video.release() # 关闭输出对象
cv2.destroyAllWindows()
26-4
读取摄影功能的属性
26-4-1 取得摄影功能的属性
get() 函数可以取得目前摄影功能的属性,语法如下:
retval = capture.get(propId)
| 属性参数 | 说明 |
CAP_PROP_POS_MSEC | 影片目前的位置,单位为 ms。 |
CAP_PROP_POS_FRAMES | 从 0 开始索引的下一帧编号。 |
CAP_PROP_POS_AVI_RATIO | 影片相对位置,0 表示开始,1 表示结束。 |
CAP_PROP_FRAME_WIDTH | 帧宽度。 |
CAP_PROP_FRAME_HEIGHT | 帧高度。 |
CAP_PROP_FPS | 帧速度,也就是每秒帧数。 |
CAP_PROP_FOURCC | 用 4 个字符表示的影片编码格式。 |
CAP_PROP_FRAME_COUNT | 影片总帧数。 |
程序实例 ch26_11.py:取得目前帧的宽度和高度。
# ch26_11.py
import cv2
capture = cv2.VideoCapture(0) # 初始化摄影功能
while(capture.isOpened()):
ret, frame = capture.read() # 读取摄影镜头的影像
cv2.imshow('Frame', frame) # 显示摄影镜头的影像
width = capture.get(cv2.CAP_PROP_FRAME_WIDTH) # 宽度
height = capture.get(cv2.CAP_PROP_FRAME_HEIGHT) # 高度
c = cv2.waitKey(1) # 等待时间 1 毫秒 ms
if c == 27: # 按 Esc 键
break
print(f"Frame 的高度 = {height}") # 输出 Frame 的高度
print(f"Frame 的宽度 = {width}") # 输出 Frame 的宽度
capture.release() # 关闭摄影功能
cv2.destroyAllWindows()
Frame 的高度 = 480
Frame 的宽度 = 640
程序实例 ch26_12.py:取得 iceocean.mov 的宽度、高度、帧速度和帧数。
# ch26_12.py
import cv2
video = cv2.VideoCapture('iceocean.mov') # 开启影片档案
while(video.isOpened()):
ret, frame = video.read() # 读取影片档案
cv2.imshow('Frame', frame) # 显示影片
width = video.get(cv2.CAP_PROP_FRAME_WIDTH) # 宽度
height = video.get(cv2.CAP_PROP_FRAME_HEIGHT) # 高度
video_fps = video.get(cv2.CAP_PROP_FPS) # 速度
video_frames = video.get(cv2.CAP_PROP_FRAME_COUNT)# 帧数
c = cv2.waitKey(50)
if c == 27:
break
print(f"video 的宽度 = {width}") # 输出 video 的宽度
print(f"video 的高度 = {height}") # 输出 video 的高度
print(f"video 的速度 = {video_fps}") # 输出 video 的速度
print(f"video 总帧数 = {video_frames}") # 输出 video 总帧数
video.release() # 关闭摄影功能
cv2.destroyAllWindows()
video 的宽度 = 640.0
video 的高度 = 480.0
video 的速度 = 30.0
video 总帧数 = 657.0
26-4-2 设定摄影功能的属性
set() 函数可以设定目前摄影功能的属性,语法如下:
retval = capture.set(propId, value)
程序实例 ch26_13.py:设定帧的宽度和高度分别是 1280、960。
# ch26_13.py
import cv2
capture = cv2.VideoCapture(0) # 初始化摄影功能
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) # 设定宽度
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 960) # 设定高度
while(capture.isOpened()):
ret, frame = capture.read() # 读取摄影镜头的影像
cv2.imshow('Frame', frame) # 显示摄影镜头的影像
c = cv2.waitKey(1) # 等待时间 1 毫秒 ms
if c == 27: # 按 Esc 键
break
capture.release() # 关闭摄影功能
cv2.destroyAllWindows()
26-4-3 显示影片播放进度
程序实例 ch26_14.py:输出影片时,在影片下方显示 Frames(帧数)和 Seconds(秒数)计数器。
# ch26_14.py
import cv2
video = cv2.VideoCapture('iceocean.mov') # 开启影片档案
video_fps = video.get(cv2.CAP_PROP_FPS) # 计算速度
height = video.get(cv2.CAP_PROP_FRAME_HEIGHT) # 影片高度
counter = 1 # 帧数计数器
font = cv2.FONT_HERSHEY_SIMPLEX
while(video.isOpened()):
ret, frame = video.read() # 读取影片档案
if ret:
y = int(height - 50) # Frames 计数器位置
cv2.putText(frame, "Frames : " + str(counter), (0, y),
font, 1, (255, 0, 0), 2) # 显示帧数
seconds = round(counter / video_fps, 1)# 计算秒数
y = int(height - 10) # Seconds 计数器位置
cv2.putText(frame, "Seconds : " + str(seconds), (0, y),
font, 1, (255, 0, 0), 2) # 显示秒数
cv2.imshow('myVideo', frame) # 显示影片
else:
break
c = cv2.waitKey(50) # 可控制播放速度
counter += 1 # 计数器加 1
if c == 27: # 按 Esc 键结束
break
video.release() # 关闭输出对象
cv2.destroyAllWindows()
26-4-4 裁剪影片
程序实例 ch26_15.py:将 iceocean.mov 裁剪为 5 秒长度的 out26_15.avi 影片。
# ch26_15.py
import cv2
video = cv2.VideoCapture('iceocean.mov') # 开启影片档案
video_fps = video.get(cv2.CAP_PROP_FPS) # 计算速度
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) # 宽度
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 高度
# 建立裁剪影片对象
fourcc = cv2.VideoWriter_fourcc(*'I420') # 编码格式
new_video = cv2.VideoWriter('out26_15.avi', fourcc,
video_fps, (width, height))
counter = video_fps * 5 # 影片长度
while(video.isOpened() and counter >= 0):
ret, frame = video.read() # 读取影片档案
if ret:
new_video.write(frame) # 写入新影片
counter -= 1 # 帧数减少
video.release()
new_video.release() # 关闭输出对象
cv2.destroyAllWindows()
执行后,可以在 ch26 资料夹看到 out26_15.avi 影片档案。
1. 参考 ch26_3.py,增加垂直翻转和水平垂直翻转的显示视窗。
2. 重新修改 ch26_14.py,在屏幕左下角显示帧数(Frames)和秒数(Seconds),影片档案使用 iceocean3.mov。