OpenCV-PyQT项目实战(11)项目案例07:摄像头操作与拍摄视频
Posted YouCans
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV-PyQT项目实战(11)项目案例07:摄像头操作与拍摄视频相关的知识,希望对你有一定的参考价值。
欢迎关注『OpenCV-PyQT项目实战 @ Youcans』系列,持续更新中
OpenCV-PyQT项目实战(1)安装与环境配置
OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门
OpenCV-PyQT项目实战(3)信号与槽机制
OpenCV-PyQT项目实战(4)OpenCV 与PyQt的图像转换
OpenCV-PyQT项目实战(5)项目案例01:图像模糊
OpenCV-PyQT项目实战(6)项目案例02:滚动条应用
OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
OpenCV-PyQT项目实战(8)项目案例04:鼠标定位
OpenCV-PyQT项目实战(9)项目案例04:视频播放
OpenCV-PyQT项目实战(10)项目案例06:键盘事件与视频抓拍
OpenCV-PyQT项目实战(11)项目案例07:摄像头操作与拍摄视频
OpenCV-PyQT项目实战(12)项目案例08:多线程视频处理
文章目录
OpenCV-PyQT项目实战(11)项目案例07:实时拍摄视频
在上一个案例中我们介绍了OpenCV和PyQt 实现视频播放,本节介绍摄像头操作与拍摄实时视频。
本例使用 OpenCV处理摄像头设备进行解码获得图像帧,然后用 QTime 定时器控制 QLabel 中的图像更新,使用按钮控制摄像画面的移动。
1. 用OpenCV获取视频流
1.1 OpenCV的VideoCapture类
OpenCV提供了VideoCapture类和VideoWriter类处理视频流,既可以处理视频文件,也可以处理摄像头设备。
函数原型:
cv.VideoCapture( index[, apiPreference] ) →
cv.VideoWriter([filename, fourcc, fps, frameSize[, isColor]]) →
VideoCapture类用于读取视频文件、视频流或从摄像机捕获视频,VideoWriter类用于视频文件的写入和保存。
构造函数cv.VideoCapture和cv.VideoWrite用于实现类的初始化。
参数说明:
- index:摄像头的 ID 编号,0 表示默认后端打开默认摄像机
- filename:读取或保存的视频文件的路径,包括扩展名
- apiPreference:读取视频流的属性设置
- fourcc:用于压缩帧的编码器/解码器的字符代码,
- CV_FOURCC(‘I’,‘4’,‘2’,‘0’),未压缩的YUV编码格式,扩展名为 .avi
- CV_FOURCC(‘P’,‘I’,‘M’,‘1’),MPEG-1 编码格式,扩展名为 .avi
- CV_FOURCC( ‘X’,‘V’,‘I’,‘D’),MPEG-4 编码格式,扩展名为 .avi
- CV_FOURCC( ‘F’,‘L’,‘V’,‘I’),Flash 编码格式,件扩展名为 .flv
- fps:视频流的帧速率
- frameSize:元组 (w, h),视频帧的宽度和高度
- isColor:是否彩色图像
成员函数:
- cv.VideoCapture.isOpened(),检查视频捕获是否初始化成功
- cv.VideoCapture.read(),捕获视频文件、视频流或捕获的视频设备
- cv.VideoCapture.release(),关闭视频文件或设备,释放对象
- cv.VideoCapture.get(propId) ,获取 VideoCapture 类对象的属性
- cv.VideoCapture.set(propId, value),设置 VideoCapture 类对象的属性
- cv.VideoWriter.fourcc(c1, c2, c3, c4[, ]),构造编码器/解码器的fourcc代码
- cv.VideoWriter.write(image[, ]),写入下一帧视频
- cv.VideoWriter.release(),关闭视频写入,释放对象
注意问题:
⒈读取视频文件、视频流中读取时,通过 filename 传递视频文件、视频流的路径。使用摄像头时,通过 index 传递摄像头的 ID 号。
⒉使用摄像头时,index=0 表示默认后端打开默认摄像机,例如笔记本内置摄像头。可以使用计算机的内置或外接的摄像头,也支持本地网络或公共网络的 IP 摄像机。
⒊视频写入类VideoWriter的参数frameSize是元组 (w, h),即视频帧的宽度和高度,而OpenCV图像的形状是 (h, w),注意二者的顺序是反的。
⒋视频处理过程较为复杂,一些程序设置与具体系统环境有关,本文只介绍基本的成员函数,通用的处理方法。更多内容详见:[https://docs.opencv.org/]。
⒌视频处理中的很多问题涉及摄像机和计算机的硬件设备,需要结合具体系统环境来分析。
1.2 OpenCV拍摄视频例程
OpenCV视频拍摄的基本步骤为:
(1)创建视频读取/捕获对象;
(2)获取视频的一帧图像;
(3)检查视频获取是否成功;
(4)释放视频读取/捕获对象。
【例程0107】调用摄像头拍照和录制视频
本例程示例调用笔记本内置摄像头抓拍图片和录制视频。根据计算机和摄像头的配置和接口的不同,可能需要修改API的设置。
# OpenCV 调用摄像头拍照和录制视频
import cv2 as cv
if __name__ == '__main__':
# 创建视频捕获对象,调用笔记本摄像头
# cam = cv.VideoCapture(0) # 创建捕获对象,0 为笔记本摄像头
cam = cv.VideoCapture(0, cv.CAP_DSHOW) # 修改 API 设置为视频输入 DirectShow
# 设置写入视频图像的高,宽,帧速率和总帧数
fps = 20 # 设置帧速率
width = int(cam.get(cv.CAP_PROP_FRAME_WIDTH)) # 640
height = int(cam.get(cv.CAP_PROP_FRAME_HEIGHT)) # 480
fourcc = cv.VideoWriter_fourcc(*'XVID') # 编码器设置 XVID
# 创建写入视频对象
vedioPath = "../images/camera.avi" # 写入视频文件的路径
capWrite = cv.VideoWriter(vedioPath, fourcc, fps, (width, height))
print(fourcc, fps, (width, height))
sn = 0 # 抓拍图像编号
while cam.isOpened(): # 检查视频捕获是否成功
success, frame = cam.read() # 读取下一帧视频图像
if success is True:
cv.imshow('vedio', frame) # 播放视频图像
capWrite.write(frame) # 将当前帧写入视频文件
key = cv.waitKey(1) & 0xFF # 接收键盘输入
if key == ord('c'): # 按 'c' 键抓拍当前帧
filePath = "../images/photo:d.png".format(sn) # 保存文件名
cv.imwrite(filePath, frame) # 将当前帧保存为图片
sn += 1 # 更新写入图像编号
print(filePath)
elif key == ord('q'): # 按 'q' 键结束录制视频
break
else:
print("Can't receive frame.")
break
cam.release() # 关闭视频捕获对象
capWrite.release() # 关闭视频写入对象
cv.destroyAllWindows() # 关闭显示窗口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rmDfexHk-1677464268030)(D:\\OpenCVPyQt\\images\\OpenCV_35.png)]
2. 用PyQt5显示OpenCV拍摄的视频流
2.1 用 QtDesigner 开发 PyQt5 图形界面
(1)打开QtDesigner,新建窗体。在 “新建窗体” 窗口的左侧菜单选择 “MainWindow” 新建一个图形窗口。
(2)创建一个显示框控件QLabel,用于显示视频流。
- 从左侧控件栏的 DisplayWidgets 中选择 Label,拖动到新建图形窗口,生成了一个 QLabel 对象。
- 鼠标选中 QLabel 对象,在右侧的 “属性编辑器” 内可以对对象的属性进行编辑和修改。
(3)创建按钮控件:
- 从左侧控件栏的 Button 中选择 PushButton 按钮,拖动到新建图形窗口,生成 PushButton 按钮对象。
- 鼠标左键点击图形窗口中的这个 PushButton 按钮对象,拖动按钮可以调整控件的位置,对于其它控件也可以通过鼠标拖动来调整位置。
- 鼠标选中 PushButton 按钮对象,控件周围的边界位置上就出现 8个蓝色的点,表示控件被选中,这时可以在右侧的 “属性编辑器” 内对对象的属性进行编辑和修改,例如:
- 将 PushButton 对象的高度修改为 120,宽度修改为 40;
- 将 PushButton 对象的 “QAbstractButton->text” 修改为 “开启”。
根据设计方案,用 QtDesigner 完成一个基本的图形界面。
(4) 将设计的图形界面保存为 .ui文件
文件默认保存在添加 QtDesigner 工具时 “Working directory” 所设置的路径,当然也可以另存到其它路径。
如果 PyChrm 或 QtDesigner 设置的文件保存路径不同,要注意导入图形界面文件时设置和使用正确的路径。
于是,我们就完成了本项目的图形界面设计,将其保存为 uiDemo9.ui文件。
在 PyCharm中,使用 PyUIC 将选中的 uiDemo9.ui 文件转换为 .py 文件,就得到了 uiDemo9.py 文件。
2.2 用 QTime 定时器实现视频播放
使用 OpenCV 对视频文件进行解码获得图像帧以后,可以使用 QTime 定时器来控制 QLabel 控件中的图像更新,实现视频播放。
PyQt5 中的 QTimer类提供了重复的和单次的定时器,为计时器提供了高级编程接口。要使用定时器,需要先创建一个QTimer实例,将定时器的timeout信号连接到相应的槽函数,并调用start(),定时器就会以设定的间隔发出timeout信号。
例程:使用 QTimer 在PyQt 控件中播放解码的图像帧
self.timerCam = QtCore.QTimer() # 定时器,毫秒
self.timerCam.timeout.connect(self.refreshFrame) # 计时器结束时调用槽函数刷新当前帧
def refreshFrame(self): # 刷新视频图像
success, self.frame = self.cam.read() # 读取下一帧视频图像
image = self.frame[self.top:self.bottom, self.left:self.right].copy()
qImg = self.cvToQImage(image) # OpenCV 转为 PyQt 图像格式
# qImg = self.cvToQImage(self.frame) # OpenCV 转为 PyQt 图像格式
self.label_1.setPixmap((QPixmap.fromImage(qImg))) # 加载 PyQt 图像
return
2.3 摄像头的打开与关闭控制
通过开启/关闭按键controlButton控制摄像头的打开与关闭。
(1)摄像头当前状态为打开时,按键显示的文本为“停止”,表示触发该按键时停止摄像;摄像头当前状态为关闭时,按钮显示的文本为“打开”,表示触发该按键时开始摄像。
(2)如果摄像头尚未初始化,则要初始化定义一个视频输入对象 self.cam。
def controlCamera(self): # 控制摄像头打开与关闭
if self.timerCamera.isActive()==False: # 摄像头关闭状态,打开动作
print("正在打开摄像头")
if self.cam.isOpened()==True: # 检查视频捕获是否成功
success, self.frame = self.cam.read() # 读取下一帧视频图像
self.height, self.width = self.frame.shape[:2]
self.top, self.bottom = int(0.1*self.height), int(0.9*self.height)
self.left, self.right = int(0.1*self.width), int(0.9*self.width)
print(self.left, self.right, self.top, self.bottom)
self.timerCamera.start(30)
else:
msg = QMessageBox.warning(self, "Warning", "开启摄像头失败,重试一次")
self.cam = cv.VideoCapture(0, cv.CAP_DSHOW) # 修改 API 设置为视频输入 DirectSho
self.controlButton.setText("停止")
else: # 摄像头开启状态,关闭动作
print("正在关闭摄像头")
self.timerCamera.stop() # 关闭定时器
self.controlButton.setText("开启")
2.4 摄像画面的移动控制
本例程使用按钮控制摄像画面的移动.
由于摄像头移动控制比较复杂,而且通常涉及硬件接口,本文不做详细介绍。为了便于测试,本例从拍摄图像中裁剪不同区域进行显示,在显示窗口实现拍摄画面的移动,来模拟摄像头的移动控制。
默认显示拍摄图像的中间的 0.1~0.9 高度×宽度区域。操作向上按键,则显示 0.0~0.8 高度区域;操作向下按键,则显示 0.2~1.0 高度区域;操作向左按键,则显示 0.0~0.8 宽度区域;操作向右按键,则显示 0.2~1.0 宽度区域。
这样处理,一方面可以达到模拟移动摄像头的显示效果,另一方面简化程序便于读者理解整个程序的结构。
# 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButton
self.pushButton_1.clicked.connect(self.upFrame) # 向上
self.pushButton_2.clicked.connect(self.downFrame) # 向下
self.pushButton_3.clicked.connect(self.leftFrame) # 向左
self.pushButton_4.clicked.connect(self.rightFrame) # 向右
self.height, self.width = self.frame.shape[:2]
self.top, self.bottom = int(0.1*self.height), int(0.9*self.height)
self.left, self.right = int(0.1*self.width), int(0.9*self.width)
print(self.left, self.right, self.top, self.bottom)
def upFrame(self):
print("视频 图像上移")
self.top, self.bottom = 0, int(0.8*self.height)
def downFrame(self):
print("视频 图像下移")
self.top, self.bottom = int(0.2*self.height), self.height
def leftFrame(self):
print("视频 图像左移")
self.left, self.right = 0, int(0.8*self.width)
def rightFrame(self):
print("视频 图像右移")
self.left, self.right = int(0.2*self.width), self.width
3. 项目实战:PyQt 视频拍摄与摄像头控制
本节介绍摄像头操作与拍摄实时视频。本例使用 OpenCV处理摄像头设备进行解码获得图像帧,然后用 QTime 定时器控制 QLabel 中的图像更新,使用按钮控制摄像画面的移动。
3.1 UI 程序 uiDemo12.py
为了便于测试,本节给出uiDemo12.py供读者参考,这是由uiDemo12.ui生成的。
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'uiDemo12.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(721, 493)
MainWindow.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("../images/youcansSmallLogo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label_1 = QtWidgets.QLabel(self.centralwidget)
self.label_1.setGeometry(QtCore.QRect(0, 0, 480, 400))
self.label_1.setText("")
self.label_1.setPixmap(QtGui.QPixmap("../images/youcansSmallLogo.png"))
self.label_1.setAlignment(QtCore.Qt.AlignCenter)
self.label_1.setObjectName("label_1")
self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_1.setGeometry(QtCore.QRect(560, 60, 60, 60))
self.pushButton_1.setText("")
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap("../images/iconUp.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_1.setIcon(icon1)
self.pushButton_1.setIconSize(QtCore.QSize(60, 60))
self.pushButton_1.setObjectName("pushButton_1")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(560, 180, 60, 60))
self.pushButton_2.setText("")
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap("../images/iconDown.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_2.setIcon(icon2)
self.pushButton_2.setIconSize(QtCore.QSize(60, 60))
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_3.setGeometry(QtCore.QRect(500, 120, 60, 60))
self.pushButton_3.setText("")
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap("../images/iconLeft.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_3.setIcon(icon3)
self.pushButton_3.setIconSize(QtCore.QSize(60, 60))
self.pushButton_3.setObjectName("pushButton_3")
self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_4.setGeometry(QtCore.QRect(620, 120, 60, 60))
self.pushButton_4.setText("")
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap("../images/iconRight.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_4.setIcon(icon4)
self.pushButton_4.setIconSize(QtCore.QSize(60, 60))
self.pushButton_4.setObjectName("pushButton_4")
self.controlButton = QtWidgets.QPushButton(self.centralwidget)
self.controlButton.setGeometry(QtCore.QRect(500, 300, 80, 55))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
self.controlButton.setFont(font)
self.controlButton.setIconSize(QtCore.QSize(60, 60))
self.controlButton.setObjectName("controlButton")
self.returnButton = QtWidgets.QPushButton(self.centralwidget)
self.returnButton.setGeometry(QtCore.QRect(600, 300, 80, 55))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
self.returnButton.setFont(font)
self.returnButton.setIconSize(QtCore.QSize(60, 60))
self.returnButton.setObjectName("returnButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 721, 25))
self.menubar.setBaseSize(QtCore.QSize(9, 0))
font = QtGui.QFont()
font.setPointSize(10)
self.menubar.setFont(font)
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menuQuit = QtWidgets.QMenu(self.menubar)
self.menuQuit.setObjectName("menuQuit")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.toolBar = QtWidgets.QToolBar(MainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.toolBar.sizePolicy().hasHeightForWidth())
self.toolBar.setSizePolicy(sizePolicy)
self以上是关于OpenCV-PyQT项目实战(11)项目案例07:摄像头操作与拍摄视频的主要内容,如果未能解决你的问题,请参考以下文章