算法工程师实习总结(7-9)

这篇博客总结了算法工程师在实习期间使用Python的PyQt5库开发多任务RTSP视频流播放及画线UI的过程。通过配置PyQt5和OpenCV,使用Qt Designer设计界面,PyUIC转换代码,实现了视频流播放、区域画线等功能。文章还讨论了项目中的技术难点、走过的弯路以及解决问题的方法,强调了面向对象思维在软件开发中的重要性。
部署运行你感兴趣的模型镜像

1. 软件开发

  • 业务要求:制作多任务RTSP视频流播放以及区域画线的界面,并完善界面程序相应的基本功能。
  • 整体思路:由于使用的语言是Python,且该软件制作只是用作后面务中算法调用的工具,并未采用专业的软开制作程序以及界面美化等手段,只需要达到基本的功能即可,恰好在Python中有制作程序界面最好的库:PyQt5, 实现较为简单。为了更好地调整软件界面相关部件的布局,采用了Qt Designer设计了界面相关部件的布局,使用PyUIC将设计好的界面导出为python 基本程序,在此基础上,在界面程序中嵌入实现相应的功能,开发框架见图1.

在这里插入图片描述

图1 多任务RTSP视频流播放以及区域画线程序界面开发框架流程

1.1 多任务RTSP视频流播放与画线UI设计

1.1.1 Pycharm(或VsCode)配置PyQt5及其工具

  环境配置:pyqt5、pyqt5-tools、opencv,为了避免因为环境包之间的冲突,安装包主要在anaconda单独配置的环境中,本文为anaconda下的pytorch环境。若出现版本不一致的问题。建议降低版本再操作,特别是opencv与pyqt5要保持版本匹配,否则在后续RTSP视频流播放嵌入会出现问题。经过测试,在Windows上能够正常运行,在Ubuntu系统上会出现版本匹配不一致,无法播放视频流的问题,需要重新编译opencv才行。

pip install pyqt5
pip install pyqt5-tools
pip install opencv-contrib-python
  1. 进入Pycharm 项目界面,点击”File“,选择”Settings“,弹出设置界面。在设置界面中选择”Tools“ →\rightarrowExternal Tools“,操作见图2。
    在这里插入图片描述

    图2 操作流程1
  2. Qt Designer配置:点击”+“按钮新建工具,弹出工具配置界面,“Name”一栏填写”Qt Designer“;在”Group“一栏填写”Qt“;”Program“一栏填designe.exe的路径,本文安装在anaconda下pytorch环境里,位置为:E:\anaconda\envs\pytorch\Lib\sitepackages\qt5_applications\Qt\bin\designer.exe”(根据实际情况填写);在”Working directory“一栏填写”$FileDir“。最后点击OK,如图3所示。
    在这里插入图片描述

    图3 操作流程2
  3. PyUIC配置:同样地点击”+“按钮新建工具,弹出工具配置界面;“Name”一栏填写”Qt Designer“,在”Group“一栏填写”Qt“;”Program“一栏填pyuic5.exe的路径,一般在对应环境的”Scripts“目录下,本文路径为”E:\anaconda\envs\pytorch\Scripts\pyuic5.exe“;在”Working directory“一栏填写”$FileDir“。在”Arguments“中填写如下内容,完成填写后,点击“OK”,步骤如图4

$FileName$
-o
$FileNameWithoutExtension.py$

在这里插入图片描述

图4 操作流程3

1.1.2 Qt Designer设计基本布局

  按照业务要求,分两步进行。

  • 主窗口布局设计:将整个窗口主要划分为3部分,具体来说分为菜单栏、主体部分、提交部分。
    菜单栏主要在子窗口进行新增任务,通过新增任务按钮实现;以及实现RTSP视频流画线的保存、撤销、清空、撤销等基本功能;主体部分呈现任务列表、RTSP视频流播放以及参数配置;提交部分在完成任务人进行保存区域画线提交并退出所有操作。其界面如下

在这里插入图片描述

图5 主窗口UI
  • 子窗口布局设计:子窗口主要是用来填写新增任务的信息,包括任务名称以及rtsp视频流地址。其布局见图6。
    在这里插入图片描述
图6 子窗口UI

1.1.3 PyUIC转python代码

  • 主窗口mainWindow.py
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1675, 854)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_5.setContentsMargins(9, 9, 9, 9)
        self.verticalLayout_5.setSpacing(6)
        self.verticalLayout_5.setObjectName("verticalLayout_5")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setSpacing(6)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.button_a_1 = QtWidgets.QPushButton(self.centralwidget)
        self.button_a_1.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_a_1.setObjectName("button_a_1")
        self.horizontalLayout_2.addWidget(self.button_a_1)
        spacerItem = QtWidgets.QSpacerItem(148, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_2.addItem(spacerItem)
        self.hl_a_1 = QtWidgets.QHBoxLayout()
        self.hl_a_1.setSpacing(6)
        self.hl_a_1.setObjectName("hl_a_1")
        self.button_a_2 = QtWidgets.QPushButton(self.centralwidget)
        self.button_a_2.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_a_2.setObjectName("button_a_2")
        self.hl_a_1.addWidget(self.button_a_2)
        self.button_a_3 = QtWidgets.QPushButton(self.centralwidget)
        self.button_a_3.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_a_3.setObjectName("button_a_3")
        self.hl_a_1.addWidget(self.button_a_3)
        self.button_a_4 = QtWidgets.QPushButton(self.centralwidget)
        self.button_a_4.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_a_4.setObjectName("button_a_4")
        self.hl_a_1.addWidget(self.button_a_4)
        self.button_a_5 = QtWidgets.QPushButton(self.centralwidget)
        self.button_a_5.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_a_5.setObjectName("button_a_5")
        self.hl_a_1.addWidget(self.button_a_5)
        self.horizontalLayout_2.addLayout(self.hl_a_1)
        spacerItem1 = QtWidgets.QSpacerItem(298, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_2.addItem(spacerItem1)
        self.horizontalLayout_2.setStretch(0, 1)
        self.horizontalLayout_2.setStretch(1, 1)
        self.horizontalLayout_2.setStretch(2, 4)
        self.horizontalLayout_2.setStretch(3, 5)
        self.verticalLayout_5.addLayout(self.horizontalLayout_2)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setSpacing(6)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setSpacing(6)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.label_b_1 = QtWidgets.QLabel(self.centralwidget)
        self.label_b_1.setObjectName("label_b_1")
        self.verticalLayout_3.addWidget(self.label_b_1)
        self.scrollArea_b_1 = QtWidgets.QScrollArea(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.scrollArea_b_1.sizePolicy().hasHeightForWidth())
        self.scrollArea_b_1.setSizePolicy(sizePolicy)
        self.scrollArea_b_1.setWidgetResizable(False)
        self.scrollArea_b_1.setAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop)
        self.scrollArea_b_1.setObjectName("scrollArea_b_1")
        self.scrollAreaWidgetContents_b_1 = QtWidgets.QWidget()
        self.scrollAreaWidgetContents_b_1.setGeometry(QtCore.QRect(80, 0, 131, 251))
        self.scrollAreaWidgetContents_b_1.setObjectName("scrollAreaWidgetContents_b_1")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_b_1)
        self.verticalLayout.setContentsMargins(9, 9, 9, 9)
        self.verticalLayout.setSpacing(6)
        self.verticalLayout.setObjectName("verticalLayout")
        self.button_b_1_1 = QtWidgets.QPushButton(self.scrollAreaWidgetContents_b_1)
        self.button_b_1_1.setEnabled(False)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.button_b_1_1.sizePolicy().hasHeightForWidth())
        self.button_b_1_1.setSizePolicy(sizePolicy)
        self.button_b_1_1.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_b_1_1.setObjectName("button_b_1_1")
        self.verticalLayout.addWidget(self.button_b_1_1)
        self.button_b_1_2 = QtWidgets.QPushButton(self.scrollAreaWidgetContents_b_1)
        self.button_b_1_2.setEnabled(False)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.button_b_1_2.sizePolicy().hasHeightForWidth())
        self.button_b_1_2.setSizePolicy(sizePolicy)
        self.button_b_1_2.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_b_1_2.setObjectName("button_b_1_2")
        self.verticalLayout.addWidget(self.button_b_1_2)
        self.scrollArea_b_1.setWidget(self.scrollAreaWidgetContents_b_1)
        self.verticalLayout_3.addWidget(self.scrollArea_b_1)
        self.hline_2 = QtWidgets.QFrame(self.centralwidget)
        self.hline_2.setFrameShape(QtWidgets.QFrame.HLine)
        self.hline_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.hline_2.setObjectName("hline_2")
        self.verticalLayout_3.addWidget(self.hline_2)
        self.verticalLayout_3.setStretch(0, 1)
        self.verticalLayout_3.setStretch(1, 5)
        self.horizontalLayout.addLayout(self.verticalLayout_3)
        self.vline_1 = QtWidgets.QFrame(self.centralwidget)
        self.vline_1.setFrameShape(QtWidgets.QFrame.VLine)
        self.vline_1.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.vline_1.setObjectName("vline_1")
        self.horizontalLayout.addWidget(self.vline_1)
        self.verticalLayout_4 = QtWidgets.QVBoxLayout()
        self.verticalLayout_4.setSpacing(6)
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.label = QtWidgets.QLabel(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setScaledContents(True)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout_4.addWidget(self.label)
        self.horizontalLayout.addLayout(self.verticalLayout_4)
        self.vlie_2 = QtWidgets.QFrame(self.centralwidget)
        self.vlie_2.setFrameShape(QtWidgets.QFrame.VLine)
        self.vlie_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.vlie_2.setObjectName("vlie_2")
        self.horizontalLayout.addWidget(self.vlie_2)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setSpacing(6)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.label_b_3 = QtWidgets.QLabel(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_b_3.sizePolicy().hasHeightForWidth())
        self.label_b_3.setSizePolicy(sizePolicy)
        self.label_b_3.setObjectName("label_b_3")
        self.verticalLayout_2.addWidget(self.label_b_3)
        self.hline_4 = QtWidgets.QFrame(self.centralwidget)
        self.hline_4.setFrameShape(QtWidgets.QFrame.HLine)
        self.hline_4.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.hline_4.setObjectName("hline_4")
        self.verticalLayout_2.addWidget(self.hline_4)
        self.button_b_3_1 = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.button_b_3_1.sizePolicy().hasHeightForWidth())
        self.button_b_3_1.setSizePolicy(sizePolicy)
        self.button_b_3_1.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.button_b_3_1.setObjectName("button_b_3_1")
        self.verticalLayout_2.addWidget(self.button_b_3_1)
        self.splitter_b_31 = QtWidgets.QSplitter(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.splitter_b_31.sizePolicy().hasHeightForWidth())
        self.splitter_b_31.setSizePolicy(sizePolicy)
        self.splitter_b_31.setOrientation(QtCore.Qt.Horizontal)
        self.splitter_b_31.setObjectName("splitter_b_31")
        self.button_b_3_2 = QtWidgets.QPushButton(self.splitter_b_31)
        self.button_b_3_2.setEnabled(True)
        self.button_b_3_2.setObjectName("button_b_3_2")
        self.lineEdit_b_3_1 = QtWidgets.QLineEdit(self.splitter_b_31)
        self.lineEdit_b_3_1.setEnabled(True)
        self.lineEdit_b_3_1.setText("")
        self.lineEdit_b_3_1.setObjectName("lineEdit_b_3_1")
        self.verticalLayout_2.addWidget(self.splitter_b_31)
        self.splitter_b_32 = QtWidgets.QSplitter(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.splitter_b_32.sizePolicy().hasHeightForWidth())
        self.splitter_b_32.setSizePolicy(sizePolicy)
        self.splitter_b_32.setOrientation(QtCore.Qt.Horizontal)
        self.splitter_b_32.setObjectName("splitter_b_32")
        self.button_b_3_3 = QtWidgets.QPushButton(self.splitter_b_32)
        self.button_b_3_3.setEnabled(True)
        self.button_b_3_3.setObjectName("button_b_3_3")
        self.lineEdit_b_3_2 = QtWidgets.QLineEdit(self.splitter_b_32)
        self.lineEdit_b_3_2.setEnabled(True)
        self.lineEdit_b_3_2.setText("")
        self.lineEdit_b_3_2.setObjectName("lineEdit_b_3_2")
        self.verticalLayout_2.addWidget(self.splitter_b_32)
        self.splitter_b_33 = QtWidgets.QSplitter(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.splitter_b_33.sizePolicy().hasHeightForWidth())
        self.splitter_b_33.setSizePolicy(sizePolicy)
        self.splitter_b_33.setOrientation(QtCore.Qt.Horizontal)
        self.splitter_b_33.setObjectName("splitter_b_33")
        self.button_b_3_4 = QtWidgets.QPushButton(self.splitter_b_33)
        self.button_b_3_4.setEnabled(True)
        self.button_b_3_4.setObjectName("button_b_3_4")
        self.lineEdit_b_3_4 = QtWidgets.QLineEdit(self.splitter_b_33)
        self.lineEdit_b_3_4.setEnabled(True)
        self.lineEdit_b_3_4.setText("")
        self.lineEdit_b_3_4.setObjectName("lineEdit_b_3_4")
        self.verticalLayout_2.addWidget(self.splitter_b_33)
        self.splitter_b_34 = QtWidgets.QSplitter(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.splitter_b_34.sizePolicy().hasHeightForWidth())
        self.splitter_b_34.setSizePolicy(sizePolicy)
        self.splitter_b_34.setOrientation(QtCore.Qt.Horizontal)
        self.splitter_b_34.setObjectName("splitter_b_34")
        self.button_b_3_7 = QtWidgets.QPushButton(self.splitter_b_34)
        self.button_b_3_7.setEnabled(True)
        self.button_b_3_7.setObjectName("button_b_3_7")
        self.lineEdit_b_3_5 = QtWidgets.QLineEdit(self.splitter_b_34)
        self.lineEdit_b_3_5.setEnabled(True)
        self.lineEdit_b_3_5.setText("")
        self.lineEdit_b_3_5.setObjectName("lineEdit_b_3_5")
        self.verticalLayout_2.addWidget(self.splitter_b_34)
        self.splitter = QtWidgets.QSplitter(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.splitter.sizePolicy().hasHeightForWidth())
        self.splitter.setSizePolicy(sizePolicy)
        self.splitter.setOrientation(QtCore.Qt.Horizontal)
        self.splitter.setObjectName("splitter")
        self.button_b_3_6 = QtWidgets.QPushButton(self.splitter)
        self.button_b_3_6.setObjectName("button_b_3_6")
        self.lineEdit_b_3_3 = QtWidgets.QLineEdit(self.splitter)
        self.lineEdit_b_3_3.setEnabled(False)
        self.lineEdit_b_3_3.setText("")
        self.lineEdit_b_3_3.setObjectName("lineEdit_b_3_3")
        self.verticalLayout_2.addWidget(self.splitter)
        self.hline_5 = QtWidgets.QFrame(self.centralwidget)
        self.hline_5.setFrameShape(QtWidgets.QFrame.HLine)
        self.hline_5.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.hline_5.setObjectName("hline_5")
        self.verticalLayout_2.addWidget(self.hline_5)
        self.horizontalLayout.addLayout(self.verticalLayout_2)
        self.horizontalLayout.setStretch(0, 2)
        self.horizontalLayout.setStretch(2, 5)
        self.horizontalLayout.setStretch(4, 4)
        self.verticalLayout_5.addLayout(self.horizontalLayout)
        self.hl_c = QtWidgets.QHBoxLayout()
        self.hl_c.setSpacing(6)
        self.hl_c.setObjectName("hl_c")
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.hl_c.addItem(spacerItem2)
        self.button_c_1 = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.button_c_1.sizePolicy().hasHeightForWidth())
        self.button_c_1.setSizePolicy(sizePolicy)
        self.button_c_1.setObjectName("button_c_1")
        self.hl_c.addWidget(self.button_c_1)
        spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.hl_c.addItem(spacerItem3)
        self.hl_c.setStretch(0, 5)
        self.hl_c.setStretch(1, 1)
        self.hl_c.setStretch(2, 5)
        self.verticalLayout_5.addLayout(self.hl_c)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1675, 28))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.button_c_1.clicked.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "多任务RTSP视频流区域画线"))
        self.button_a_1.setText(_translate("MainWindow", "新增任务"))
        self.button_a_2.setText(_translate("MainWindow", "保存"))
        self.button_a_3.setText(_translate("MainWindow", "撤销"))
        self.button_a_4.setText(_translate("MainWindow", "清空"))
        self.button_a_5.setText(_translate("MainWindow", "删除"))
        self.label_b_1.setText(_translate("MainWindow", "任务列表"))
        self.button_b_1_1.setText(_translate("MainWindow", "2207002"))
        self.button_b_1_2.setText(_translate("MainWindow", "2207001"))
        self.label.setText(_translate("MainWindow", "RTSP视频流"))
        self.label_b_3.setText(_translate("MainWindow", "参数配置"))
        self.button_b_3_1.setText(_translate("MainWindow", "检测区域"))
        self.button_b_3_2.setText(_translate("MainWindow", "区域"))
        self.lineEdit_b_3_1.setPlaceholderText(_translate("MainWindow", "请输入区域名称"))
        self.button_b_3_3.setText(_translate("MainWindow", "区域编号"))
        self.lineEdit_b_3_2.setPlaceholderText(_translate("MainWindow", "请输入区域编号"))
        self.button_b_3_4.setText(_translate("MainWindow", "区域类型"))
        self.lineEdit_b_3_4.setPlaceholderText(_translate("MainWindow", "请输入区域类型"))
        self.button_b_3_7.setText(_translate("MainWindow", "区域偷油dist"))
        self.lineEdit_b_3_5.setPlaceholderText(_translate("MainWindow", "请输入区域dist"))
        self.button_b_3_6.setText(_translate("MainWindow", "RTSP地址"))
        self.button_c_1.setText(_translate("MainWindow", "提交"))

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = QtWidgets.QMainWindow()
    Ui= Ui_MainWindow()
    Ui.setupUi(mainWindow)
    mainWindow.show()
    sys.exit(app.exec_())
  • 子窗口Ui_Dialog.py
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(388, 161)
        font = QtGui.QFont()
        font.setBold(False)
        font.setWeight(50)
        Dialog.setFont(font)
        self.layoutWidget = QtWidgets.QWidget(Dialog)
        self.layoutWidget.setGeometry(QtCore.QRect(0, 110, 372, 26))
        self.layoutWidget.setObjectName("layoutWidget")
        self.hl_b = QtWidgets.QHBoxLayout(self.layoutWidget)
        self.hl_b.setContentsMargins(0, 0, 0, 0)
        self.hl_b.setObjectName("hl_b")
        spacerItem = QtWidgets.QSpacerItem(208, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.hl_b.addItem(spacerItem)
        self.ok_cancel_box_b = QtWidgets.QDialogButtonBox(self.layoutWidget)
        self.ok_cancel_box_b.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.ok_cancel_box_b.setObjectName("ok_cancel_box_b")
        self.hl_b.addWidget(self.ok_cancel_box_b)
        self.layoutWidget1 = QtWidgets.QWidget(Dialog)
        self.layoutWidget1.setGeometry(QtCore.QRect(10, 20, 261, 52))
        self.layoutWidget1.setObjectName("layoutWidget1")
        self.hl_a = QtWidgets.QHBoxLayout(self.layoutWidget1)
        self.hl_a.setContentsMargins(0, 0, 0, 0)
        self.hl_a.setObjectName("hl_a")
        self.vl_a_1 = QtWidgets.QVBoxLayout()
        self.vl_a_1.setObjectName("vl_a_1")
        self.label_a_1 = QtWidgets.QLabel(self.layoutWidget1)
        self.label_a_1.setObjectName("label_a_1")
        self.vl_a_1.addWidget(self.label_a_1)
        self.label_a_2 = QtWidgets.QLabel(self.layoutWidget1)
        self.label_a_2.setObjectName("label_a_2")
        self.vl_a_1.addWidget(self.label_a_2)
        self.hl_a.addLayout(self.vl_a_1)
        self.vl_a_2 = QtWidgets.QVBoxLayout()
        self.vl_a_2.setObjectName("vl_a_2")
        self.lineEdit_a_1 = QtWidgets.QLineEdit(self.layoutWidget1)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.lineEdit_a_1.sizePolicy().hasHeightForWidth())
        self.lineEdit_a_1.setSizePolicy(sizePolicy)
        self.lineEdit_a_1.setText("")
        self.lineEdit_a_1.setObjectName("lineEdit_a_1")
        self.vl_a_2.addWidget(self.lineEdit_a_1)
        self.lineEdit_a_2 = QtWidgets.QLineEdit(self.layoutWidget1)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.lineEdit_a_2.sizePolicy().hasHeightForWidth())
        self.lineEdit_a_2.setSizePolicy(sizePolicy)
        self.lineEdit_a_2.setObjectName("lineEdit_a_2")
        self.vl_a_2.addWidget(self.lineEdit_a_2)
        self.hl_a.addLayout(self.vl_a_2)

        self.retranslateUi(Dialog)
        self.ok_cancel_box_b.accepted.connect(Dialog.accept)
        self.ok_cancel_box_b.rejected.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "+新增任务"))
        self.label_a_1.setText(_translate("Dialog", "* 任务名称"))
        self.label_a_2.setText(_translate("Dialog", "* RTSP地址"))
        self.lineEdit_a_1.setPlaceholderText(_translate("Dialog", "请输入任务名称,不能为空"))
        self.lineEdit_a_2.setPlaceholderText(_translate("Dialog", "请输入RTSP地址,不能为空"))



if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Dialog = QtWidgets.QDialog()
    Ui = Ui_Dialog()
    Ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())

1.2 RTSP视频流播放以及区域画线嵌入UI

  由于整个项目的代码较多,本文只放核心代码以供展示。

1.2.1 新增任务

  点击主窗口的新增任务按钮,弹出子窗口,在子窗口的界面中填写新增任务的名称以及对应的rtsp视频流地址,点击OK键,保存该任务的初始收据并且会在主窗口任务列表下新增该任务的Label。其技术要点涉及到PyQt5最为重要的知识,即信号与槽的绑定。初始数据为字典形式,如下:

{"任务名称": "2208001", "rtsp": "对应的rtsp地址", "区域位置信息": {}}

class AddTask:
    '''
    click button(“新增任务”): main_ui.button_a_1
    fill task_name, rtsp_url in child_ui
    show task_name in main_ui
    '''
    def __init__(self, main_ui, child_ui, child,
                 click_rtsp_url,click_task_name,click_task_data,
                 root_path=task_root_path):

        self.main_ui = main_ui
        self.child_ui = child_ui
        # 新增任务按钮绑定子窗口
        self.child = child
        self.root_path = root_path
        self.main_ui.button_a_1.clicked.connect(self.addtask_slot) #新增任务
        self.child_ui.ok_cancel_box_b.accepted.connect(self.setTask) #子窗口添加任务

        self.add_task_data = {} #往新增列表加任务初始化的data
        # 点击新增任务时的,拿到的rtsp,任务名,初始化数据
        self.click_rtsp_url = click_rtsp_url
        self.click_task_name = click_task_name
        self.click_task_data = click_task_data

        print('请填写任务名称与rtsp地址')

    def addtask_slot(self):
        self.child.show()

    def fillTask(self):
        '''
        task_name fill in child_ui.lineEdit_a_1
        rtsp_url fill in child_ui.lineEdit_a_2
        :return:
        '''
        # add_task_button = self.main_ui.button_a_1
        add_task_name = self.child_ui.lineEdit_a_1.text()  # 任务名称
        add_rtsp_url = self.child_ui.lineEdit_a_2.text()   # rtsp地址
        if add_task_name == '' or add_rtsp_url == '':
            print('任务名,rtsp地址不能为空,请重新填写')
        return add_task_name, add_rtsp_url

    # add_task_name, add_rtp_url只针对增加的task_name和rtsp,不关联点击任务名,独立的


    def setTask(self):
        '''
        add_task_name button show in main_ui.scrollAreaWidgetContents_b_1
        :return:
        '''
        # global main_ui
        # global task_name
        add_task_name, add_rtsp_url = self.fillTask()
        self.main_ui.button_add = QtWidgets.QPushButton(self.main_ui.scrollAreaWidgetContents_b_1)
        self.main_ui.button_add.setEnabled(True)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.main_ui.button_add.sizePolicy().hasHeightForWidth())
        self.main_ui.button_add.setSizePolicy(sizePolicy)
        self.main_ui.button_add.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.main_ui.button_add.setObjectName(add_task_name)
        self.main_ui.button_add.setText(add_task_name)
        self.main_ui.verticalLayout.addWidget(self.main_ui.button_add)
        self.generate_data(add_task_name, add_rtsp_url)
        print(self.main_ui.verticalLayout.count())

        # sender获取信号发送
        def clickTask():
            global click_rtsp_url, click_task_name, click_task_data, main_ui
            self.click_rtsp_url = add_rtsp_url_item
            self.click_task_name = add_task_name_item
            self.click_task_data = add_task_data_item
            self.main_ui.lineEdit_b_3_3.setText(self.click_rtsp_url)
            self.main_ui.lineEdit_b_3_3.setEnabled(True)
            click_rtsp_url = self.click_rtsp_url
            click_task_name = self.click_task_name
            click_task_data = self.click_task_data
            main_ui = self.main_ui
            # add_display()
    def generate_data(self, add_task_name, add_rtsp_url):
        if not os.path.isdir(os.path.join(self.root_path, add_task_name)):  # 如果没有tasks/task_name文件夹则生成
            data_path = os.path.join(self.root_path, add_task_name)
            os.mkdir(data_path)
        data_path = os.path.join(self.root_path, add_task_name)
        self.add_task_data['任务名'] = add_task_name
        self.add_task_data['rtsp'] = add_rtsp_url
        self.add_task_data['区域位置信息'] = {}
        np.save(data_path+'/taskData.npy', self.add_task_data)

1.2.2 RTSP视频流播放

  点击任务列表中的任务按钮,会在RTSP视频流播放区域进行播放视频流。此节的技术要点是要将一个视频流放入QLabel中进行播放。其解决思路如下:

  1. 清空上一个视频流播放,再利用opencv读取当前视频流视频帧
  2. 将读取到视频帧的图片转化为Qimage对象
  3. 然后将Qimage对象的图片转为Qlabel能够展示图片的对象QPixmap
  4. 最后在Qlabel中直接读取QPixmap对象,即可达到对视频帧的读取

关键代码如下:

def display():
    main_ui.label.setPixmap(QPixmap.fromImage(QImage(np.zeros((img_height, img_width)),
                                                     img_width, img_height, QImage.Format_RGB888)))
    cap = cv2.VideoCapture(click_rtsp_url)
    while cap.isOpened():
        success, frame = cap.read()  # frame是RGB
        # 宽度
        frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))

        #高度
        frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        # print('图片尺寸为:宽度%s,高度%s' % (frame_width, frame_height))
        label_with = main_ui.label.width()
        label_height = main_ui.label.height()
        ratio_width = label_with/frame_width
        ratio_height = label_height/frame_height
        ratio = resize_width/frame_width
        if success:
            frame = imutils.resize(frame, height=resize_width)
            frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)  # 红绿蓝转蓝绿红
            img = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
            main_ui.label.setPixmap(QPixmap.fromImage(img))
            cv2.waitKey(10)
            cv2.destroyAllWindows()

1.2.3 RTSP视频流区域画线

  在视频流播放后,填写当前任务的区域信息,点击参数配置的检测区域按钮后,启动画线操作。鼠标左键点击视频流上的点,会生成红色点,再次点击会实现当前点与上一个点的连接,并且最后一个点与初始点也会进行连接。这样在点击至少3个不同点的位置后会形成区域框图,完成画线操作后,点击菜单栏上的保存按钮完成当前区域画线的数据保存。
在这里插入图片描述

图7 RTSP视频流画线

 class MyLabel(QLabel):
    def __init__(self, parent=None):
        super(MyLabel, self).__init__(parent)
    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self)
        if click_org: #画保存了的区域task_areas_org
            if len(task_areas_org)>0:
                for org_pts_info in list(task_areas_org.values()):
                    # print(org_pts)
                    # print(len(org_pts))
                    org_pts = org_pts_info['areaPos']
                    for i in range(len(org_pts)):
                        painter.setPen(QPen(Qt.green, 7))
                        painter.drawPoint(org_pts[i][0], org_pts[i][1])
                        if len(org_pts) > 1:
                            for j in range(0, len(org_pts) - 1):
                                painter.setPen(QPen(Qt.red, 2))
                                painter.drawLine(org_pts[j][0], org_pts[j][1], org_pts[j + 1][0], org_pts[j + 1][1])
                                painter.drawLine(org_pts[0][0], org_pts[0][1], org_pts[-1][0], org_pts[-1][1])
            if del_choose:
                if del_org_area_idx is not None:
                    del_org_pts = list(task_areas_org.values())[del_org_area_idx-1]['areaPos'] #获取要删除的区域位置
                    for i in range(len(del_org_pts)):
                        painter.setPen(QPen(Qt.blue, 7))
                        painter.drawPoint(del_org_pts[i][0], del_org_pts[i][1])

        if len(pts) > 0 and flag == False:
            for i in range(len(pts)):  # 对每个点进行绘制,绿色
                painter.setPen(QPen(Qt.green, 7))
                painter.drawPoint(pts[i][0], pts[i][1])
            if len(pts) > 1:
                for j in range(0, len(pts) - 1):
                    painter.setPen(QPen(Qt.blue, 2))  # 线条设置为蓝色
                    painter.drawLine(pts[j][0], pts[j][1], pts[j + 1][0], pts[j + 1][1])
                    painter.drawLine(pts[0][0], pts[0][1], pts[-1][0], pts[-1][1])
        # 点击了保存按钮,之前保存的区域都显示为红色,后面画的为蓝色
        if len(pts) > 0 and flag == True: #分del_choose
            for i in range(0, len(pts)):
                painter.setPen(QPen(Qt.green, 7))
                painter.drawPoint(pts[i][0], pts[i][1])
            if len(pts[len_save:]) >= 0:  # 表示区域保存完后面有点
                    for i in range(len(all_len) - 1):  # 点了保存前面m每个区域显示为红色,区域个数为len(all_len) - 1, 如all_len=[0,4,8]
                        # 起始点索引
                        start = all_len[i]
                        # 第i个区域的长度
                        length = all_len[i + 1] - all_len[i]
                        # 绘制第i个区域
                        for j in range(start, start + length - 1):
                            painter.setPen(QPen(Qt.red, 2))
                            painter.drawLine(pts[j][0], pts[j][1], pts[j + 1][0], pts[j + 1][1])
                            painter.drawLine(pts[start][0], pts[start][1], pts[start + length - 1][0],
                                             pts[start + length - 1][1])
                    #绘制len_save索引后面的点:绿色,线:蓝色
                    for k in range(len_save, len(pts) - 1):
                        painter.setPen(QPen(Qt.blue, 2))
                        painter.drawLine(pts[k][0], pts[k][1], pts[k + 1][0], pts[k + 1][1])
                        painter.drawLine(pts[len_save][0], pts[len_save][1], pts[-1][0], pts[-1][1])
            if del_choose:
                if del_pts_index_start is not None:
                    # 表示选中了新增的区域
                    for i in range(del_pts_index_start, del_pts_index_end):
                        painter.setPen(QPen(Qt.blue, 7))
                        painter.drawPoint(pts[i][0], pts[i][1])
                else:# 选中了原始区域del_org_area_idx
                    del_org_pts = task_areas_org.values()[del_org_area_idx-1]['areaPos']
                    for i in range(len(del_org_pts)):
                        painter.setPen(QPen(Qt.blue, 7))
                        painter.drawPoint(del_org_pts[i][0], del_org_pts[i][1])

1.3 总结

  • 本项目需要的基础知识:opencv、PyQt5、UI设计以及能够熟练使用python实现业务的要求。
  • 技术难点:多个任务的代码耦合处理(此处是最容易出现Bug的地方),RTSP视频流画线的基本功能操作。
  • 走的弯路:没有清晰地对项目进行合理分解以及在实现业务要求时没有做到整体把握,导致走一步看一步(过程思维),遇到一个Bug花费太多时间进行处理,甚至因为项目的耦合部分陷入不能解决的地步。过程思维的缺陷,就是不能把握全局,在全局完成后,很容易因为一个错误而陷入较大部分崩盘。
  • 解决办法:必须清楚明白的了解项目整体,以及各部分的具体任务以及解决思路,不能有一丝混乱和不清楚的部分,把握全局必须做到没有一丝问题,否则后面修补将会花费大量的时间代价。在动手之前,必须将整体项目拆分为多个独立的对象,不能耦合,即使在后面遇到问题,只需要改独立的块,而不影响其他部分。
  • 收获:强化了python代码动手实现业务逻辑的能力。深刻体验到从过程思维转化为面向对象思维的重要性,灵活使用类与函数处理各种业务要求,能够使用UI设计以及PyQt5完成简单的软件开发工作,为接下来的算法实操奠定了基础。

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值