OpenCV教程拓展挑战:PyQt编写GUI界面-阿里云开发者社区

开发者社区> 初商> 正文

OpenCV教程拓展挑战:PyQt编写GUI界面

简介: 如果你也有想分享的干货,可以登录天池实验室(notebook),包括赛题的理解、数据分析及可视化、算法模型的分析以及一些核心的思路等内容。
+关注继续查看

PyQt编写GUI界面

推荐人群:

初级入门、机器学习小白、技术爱好者

image.png

1、简介

前面我们学习的OpenCV内容都是运行在命令行中的,没有界面,所以本次的拓展挑战内容便是:

了解Python编写GUI界面的方法,使用PyQt5编写如下的图像处理应用程序,实现打开摄像头、捕获图片、读取本地图片、灰度化和Otsu自动阈值分割的功能。

最新版本:PyQt 5.x

官网:https://www.riverbankcomputing.com/software/pyqt/

大家感兴趣的话,除去官网,下面是一些可参考的资源:

Python Wiki: PyQt http://t.cn/R3XvLpk

PyQt/Tutorials http://t.cn/EiE97YM

PyQt5 tutorial:英文原版 http://zetcode.com/gui/pyqt5/

PyQt4 tutorial:中文版 http://t.cn/EiE9fv8、英文原版 http://t.cn/RzPrRin

Qt5 Documentation https://doc.qt.io/qt-5/

中文参考书:PyQt5快速开发与实战 http://t.cn/RlwsiHL

基于Qt的Python IDE Eric http://t.cn/hGLo4a

2、安装

pip install pyqt5

下载速度慢的话,可以到PyPI上下载离线版安装。另外我推荐使用Qt Designer来设计界面,如果你装的是Anaconda的话,就已经自带了designer.exe,例如我的是在:D:\ProgramData\Anaconda3\Library\bin\,如果是普通的Python环境,则需要自行安装:

pip install pyqt5-tools

安装完成后,designer.exe应该在Python安装目录下:xxx\Lib\site-packages\pyqt5_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_())

image.png

3、界面设计

根据我们的挑战内容,解决思路是使用Qt Designer来设计界面,使用Python完成代码逻辑。打开designer.exe,会弹出创建新窗体的窗口,我们直接点击“create”:

image.png

界面的左侧是Qt的常用控件”Widget Box”,右侧有一个控件属性窗口”Property Editor”,其余暂时用不到。本例中我们只用到了”Push Button”控件和”Label”控件:最上面的三个Label控件用于显示图片,可以在属性窗口调整它的大小,我们统一调整到150×150:

image.png

image.png

另外,控件上显示的文字”text”属性和控件的名字”objectName”属性需要修改,便于显示和代码调用。可以按照下面我推荐的命名:

image.png

4、按钮事件

如果你之前有过一些GUI开发经验,比如MFC,WinForm等,就知道GUI是通过事件驱动的,什么意思呢?比如前面我们已经设计好了界面,接下来就需要实现”打开摄像头”到”阈值分割”这5个按钮的功能,也就是给每个按钮指定一个”函数”,逻辑代码写在这个函数里面。这种函数就称为事件,Qt中称为槽连接。

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

image.png

然后点击按钮并拖动,当产生类似于电路中的接地符号时释放鼠标,参看下面动图:

image.png

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

640 (3).gif

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

image.png

到此,我们就完成了界面设计的所有工作,按下Ctrl+S保存当前窗口为.ui文件。.ui文件其实是按照XML格式标记的内容,可以用文本编辑器将.ui文件打开看看。

5、ui文件转py代码

因为我们是用Designer工具设计出的界面,并不是用Python代码敲出来的,所以要想真正运行,需要使用pyuic5将ui文件转成py文件。pyuic5.exe默认在%\Scripts\下,比如我的是在:D:\ProgramData\Anaconda3\Scripts\。

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

640 (4).gif

然后执行这条指令:

pyuic5 -o mainForm.py using_pyqt_create_ui.ui

如果出现pyuic5不是内部命令的错误,说明pyuic5的路径没有在环境变量里,添加下就好了。执行正常的话,就会生成mainForm.py文件,里面应该包含一个名为”Ui_MainWindow”的类。

6、小结

mainForm.py文件是根据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_())

image.png

本文只是抛砖引玉,介绍了PyQt5的简单使用,想要深入学习,可以参考本文开头的参考资料哦

引用
本节源码 http://t.cn/EiEI3XL

image.png

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Unity教程:GUI 界面开发
UI概述: UI永远是显示在屏幕的最前面上,不受变形、碰撞、光照的影响GUI概述:GUI是Graphical User Interface的缩写。Unity的图形界面系统能容易和快速创建出各种交互界面。
1815 0
怎么设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程
8485 0
Centos6.9虚拟机克隆后简单的网络配置教程
前几天分享了在vSphere Client上如何克隆虚拟机,感兴趣的小伙伴们可以点击进去查看。虚拟机克隆之后,其网络并不能用,需要重新进行配置。配置的方法与常规的网络配置有些不同,具体的操作教程如下。
1035 0
阿里云服务器添加安全组规则(图文教程)
阿里云服务器添加安全组规则(图文教程)
1729 0
SSH远程会话管理工具 - screen使用教程
2010年10月15日 下午 | 作者:VPS侦探 VPS侦探在刚接触Linux时最怕的就是SSH远程登录Linux VPS编译安装程序时(比如安装lnmp)网络突然断开,或者其他情况导致不得不与远程SSH服务器链接断开,远程执行的命令也被迫停止,只能重新连接,重新运行。
909 0
CentOS openssh 升级教程
低版本和centos系统默认安装的openssh版本经常会有高风险漏洞,因此openssh需要保持升级到最新版本。
367 0
超详细hadoop集群服务器安装配置教程
虚拟机以及Linux系统安装在之前的两篇分享中已经详细的介绍了方法,并且每一步的都配图了。如果有朋友还是看不懂,那我也爱莫能助了。本篇主要就hadoop服务器操作系统配置进行详细说明,hadoop安装会在下一篇文章中详细的介绍。
11317 0
+关注
770
文章
584
问答
来源圈子
更多
天池是国内最大的大数据众智平台,面向社会开放高质量脱敏数据集(阿里数据及第三方授权数据)和计算资源,吸引全球高水平人才创造优秀解决方案,有效帮助行业/政府解决业务痛点,并为企业招聘提供人才输送。作为中国产业AI排头兵,天池提供集品牌、生态、人才、算力为一体的数据智能解决方案,为产业创造价值。2014年至今,天池已成功运作400余场高规格数据类竞赛,覆盖全球98个国家和地区的60万数据开发者。天池平台上的竞赛课题以解决实际场景中的业务痛点为主,实战性和应用性强,场景覆盖数字政府、电商、金融、交通、物流、航空、工业、基因、电力、医疗多个领域,让AI普惠各行各业。
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载