• OpenCV-Python入门教程7-PyQt编写GUI界面


    前面一直都是使用命令行运行代码,不够人性化。这篇用Python编写一个GUI界面,使用PyQt5编写图像处理程序。包括:打开、关闭摄像头,捕获图片,读取本地图片,灰度化和Otsu自动阈值分割的功能。

    使用Qt Designer来设计界面。而anaconda里自带了designer.exe,我使用的就是这个。designer.exe的路径:D:ProgramDataAnaconda3Libraryin,如果是普通的Python环境,则需要自行安装

    pip install pyqt5-tools

    安装完成后,designer.exe应该在Python的安装目录下:xxxLibsite-packagespyqt5_tools

    生成一个简单的界面,后面还会用到

    import sys
    from PyQt5.QtWidgets import QApplication, QWidget
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = QWidget()
        window.setWindowTitle('Hello World!')
        window.show()
        sys.exit(app.exec_())

    一、界面设计

    在D:ProgramDataAnaconda3Libraryin下打开designer.exe,会弹出创建新窗体的窗口,我们直接点击"创建"(英文版是create)

    界面左侧是Qt的常用控件"Widget Box",右侧有一个控件属性窗口"Property Editor"。本例中我们只用到了"Push Button"控件和"Label"控件,可以在属性窗口调整它的大小150x150(可以根据自己的需求适当调大或者缩小):

    控件上显示的文字"text"属性和控件的名字"objectName"属性需要修改,便于显示和代码调用,可以按照下面的表格命名:

    控件 显示内容text 控件名objectName
    PushButton 打开摄像头 btnOpenCamera
    PushButton 捕获图片 btnCapture
    PushButton 打开图片 btnReadImage
    PushButton 灰度化 btnGray
    PushButton 阈值分割 btnThreshold
    Label 摄像头 labelCamera
    Label 捕获图 labelCapture
    Label 结果图 labelResult

    前面设计好了界面,接下来就是实现"打开摄像头"到"阈值分割"这五个按钮的功能,也就是给每个按钮指定一个函数,逻辑代码写在这个函数里面。这个函数就称事件,Qt中称为槽连接

    点击Designer工具栏的Edit Signals/Slots按钮,进入槽函数编辑界面,点击旁边的"Edit Widgets"可以恢复正常视图:

    在弹出的配置窗口中,可以看到左侧是按钮的常用事件,我们选择点击事件”clicked()”,然后添加一个名为”btnOpenCamera_Clicked()”的槽函数:

    重复上面的步骤,给五个按钮添加五个槽函数,最终结果如下:

    Ctrl + S保存.ui文件。我们需要将ui转py代码。

    打开cmd命令行,切换到ui文件的保存目录。Windows下有个小技巧,可以在目录的地址栏输入cmd,一步切换到当前目录:

    执行这条指令

    pyuic5 -o mainForm.py using_pyqt_create_ui.ui

    生成mainForm.py文件,里面包含一个名为”Ui_MainWindow”的类。

    二、编写逻辑代码

    mainForm.py是根据ui文件生成的,也就是说,一旦ui文件有所改变,需要重新生成覆盖原来的文件。

    新建一个mainEntry.py存放逻辑代码,代码虽然很长,但是很简单并不难懂。有些部分有所重复,并没有将其封装成一个函数(博主能力有限),感兴趣的可以试一下

    import sys
    import cv2
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import QFileDialog, QMainWindow
    from mainForm import Ui_MainWindow
    
    
    class PyQtMainEntry(QMainWindow, Ui_MainWindow):
        def __init__(self):
            super().__init__()
            self.setupUi(self)
    
            self.camera = cv2.VideoCapture(0)
            self.is_camera_opened = False  # 摄像头有没有打开标记
    
            # 定时器:30ms捕获一帧
            self._timer = QtCore.QTimer(self)
            self._timer.timeout.connect(self._queryFrame)
            self._timer.setInterval(30)
    
        def btnOpenCamera_Clicked(self):
            '''
            打开和关闭摄像头
            '''
            self.is_camera_opened = ~self.is_camera_opened
            if self.is_camera_opened:
                self.btnOpenCamera.setText("关闭摄像头")
                self._timer.start()
            else:
                self.btnOpenCamera.setText("打开摄像头")
                self._timer.stop()
    
        def btnCapture_Clicked(self):
            '''
            捕获图片
            '''
            # 摄像头未打开,不执行任何操作
            if not self.is_camera_opened:
                return
    
            self.captured = self.frame
            # 后面这几行代码几乎都一样,可以尝试封装成一个函数
            rows, cols, channels = self.captured.shape
            bytesPerLine = channels * cols
            # Qt显示图片时,需要先转换成QImgage类型
            QImg = QImage(self.captured.data, cols, rows, bytesPerLine, QImage.Format_RGB888)
            self.labelCapture.setPixmap(QPixmap.fromImage(QImg).scaled(
                self.labelCapture.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
    
        def btnReadImage_Clicked(self):
            '''
            从本地读取图片 文件路径不能有中文
            '''
            # 打开文件选取对话框
            filename, _ = QFileDialog.getOpenFileName(self, '打开图片')
            if filename:
                self.captured = cv2.imread(str(filename))
                # OpenCV图像以BGR通道存储,显示时需要从BGR转到RGB
                self.captured = cv2.cvtColor(self.captured, cv2.COLOR_BGR2RGB)
    
                rows, cols, channels = self.captured.shape
                bytesPerLine = channels * cols
                QImg = QImage(self.captured.data, cols, rows, bytesPerLine, QImage.Format_RGB888)
                self.labelCapture.setPixmap(QPixmap.fromImage(QImg).scaled(
                    self.labelCapture.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
    
        def btnGray_Clicked(self):
            '''
            灰度化
            '''
            # 如果没有捕获图片,则不执行操作
            if not hasattr(self, "captured"):
                return
            self.cpatured = cv2.cvtColor(self.captured, cv2.COLOR_RGB2GRAY)
            rows, columns = self.cpatured.shape
            bytesPerLine = columns
            # 灰度图是单通道,所以需要用Format_Indexed8
            QImg = QImage(self.cpatured.data, columns, rows, bytesPerLine, QImage.Format_Indexed8)
            self.labelResult.setPixmap(QPixmap.fromImage(QImg).scaled(
                self.labelResult.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
    
        def btnThreshold_Clicked(self):
            '''
            Otsu自动阈值分割
            '''
            if not hasattr(self, "captured"):
                return
    
            _, self.cpatured = cv2.threshold(
                self.cpatured, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
            rows, columns = self.cpatured.shape
            bytesPerLine = columns
            # 阈值分割图也是单通道,也需要用Format_Indexed8
            QImg = QImage(self.cpatured.data, columns, rows, bytesPerLine, QImage.Format_Indexed8)
            self.labelResult.setPixmap(QPixmap.fromImage(QImg).scaled(
                self.labelResult.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
    
        @QtCore.pyqtSlot()
        def _queryFrame(self):
            '''
            循环捕获图片
            '''
            ret, self.frame = self.camera.read()
            img_rows, img_cols, channels = self.frame.shape
            bytesPerLine = channels * img_cols
    
            cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB, self.frame)
            QImg = QImage(self.frame.data, img_cols, img_rows, bytesPerLine, QImage.Format_RGB888)
            self.labelCamera.setPixmap(QPixmap.fromImage(QImg).scaled(
                self.labelCamera.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        window = PyQtMainEntry()
        window.show()
        sys.exit(app.exec_())

     参考网址:https://tianchi.aliyun.com/course/courseConsole?courseId=40992&chapterIndex=1&sectionIndex=16

  • 相关阅读:
    C#反射
    Ubuntu20,21登录界面卡死【thinkpad S5】
    121. 买卖股票的最佳时机
    Ubuntu20,21开机配置
    简洁的 Markdown 编辑器 Smark 下的段子
    617. 合并二叉树
    http GET 请求字符串经典bug
    Linux常用命令手册
    136. 只出现一次的数字
    Ubuntu20,21软件添加与删除历史记录以及还原
  • 原文地址:https://www.cnblogs.com/gezhuangzhuang/p/10289980.html
Copyright © 2020-2023  润新知