【计算机视觉】OpenCV篇(4) - Pycharm+PyQt5+Python小项目实战
1.下载安装
(1)Pycharm:下载链接
(2)推荐使用Qt Designer来设计界面,如果你装的是Anaconda的话,就已经自带了designer.exe,我这里使用的是Pycharm的虚拟环境,通过以下的方式安装:
(venv) G:\GitWorkspace\PycharmProjects\PythonStudy>pip install pyqt5
Collecting pyqt5
Downloading https://files.pythonhosted.org/packages/b2/87/219cd547c0deb902edd3fdbf1af01409f7f9d251344b9154d878203cf841/PyQt5-5.12.1-5.12.2-cp35.cp36.cp37.cp38-none-win_amd64.whl (47.4MB)
100% |████████████████████████████████| 47.4MB 40kB/s
Collecting PyQt5_sip<4.20,>=4.19.14 (from pyqt5)
Downloading https://files.pythonhosted.org/packages/ee/03/a11da2a5dd75b0ccd795ddbd944c18ce663ffb08d59a1e7f1ee5a597488c/PyQt5_sip-4.19.15-cp36-none-win_amd64.whl (51kB)
100% |████████████████████████████████| 61kB 35kB/s
Installing collected packages: PyQt5-sip, pyqt5
Successfully installed PyQt5-sip-4.19.15 pyqt5-5.12.1 (venv) G:\GitWorkspace\PycharmProjects\PythonStudy>pip install pyqt5-tools
Collecting pyqt5-tools
Downloading https://files.pythonhosted.org/packages/21/ff/7578d6d124926ff6cd1ef13c8c095063ad4c3811f4506788a915607aee81/pyqt5_tools-5.11.3.1.4-cp37-none-win32.whl (47.9MB)
100% |████████████████████████████████| 47.9MB 275kB/s
Collecting click (from pyqt5-tools)
Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
100% |████████████████████████████████| 81kB 836kB/s
Collecting pyqt5==5.11.3 (from pyqt5-tools)
Downloading https://files.pythonhosted.org/packages/26/78/c215008e70982944272b6c329a76cc11259e7378a3e23418fbe0bd48de96/PyQt5-5.11.3-5.11.2-cp35.cp36.cp37.cp38-none-win32.whl (39.4MB)
100% |████████████████████████████████| 39.4MB 206kB/s
Collecting python-dotenv (from pyqt5-tools)
Downloading https://files.pythonhosted.org/packages/8c/14/501508b016e7b1ad0eb91bba581e66ad9bfc7c66fcacbb580eaf9bc38458/python_dotenv-0.10.1-py2.py3-none-any.whl
Requirement already satisfied: PyQt5_sip<4.20,>=4.19.11 in c:\mzhu_working1023\python-workspace\study\venv\lib\site-packages (from pyqt5==5.11.3->pyqt5-tools) (4.19.15)
Installing collected packages: click, pyqt5, python-dotenv, pyqt5-tools
Found existing installation: PyQt5 5.12.1
Uninstalling PyQt5-5.12.1:
Successfully uninstalled PyQt5-5.12.1
Successfully installed click-7.0 pyqt5-5.11.3 pyqt5-tools-5.11.3.1.4 python-dotenv-0.10.1
2.环境配置
在Pycharm中,点击File->Settings->Tools->External Tools点击+添加以下工具:
(1)qtdesinger
(2)pyuic5,这个是把qt的UI文件转换成.py文件的工具
(3)pyrcc, 这个是将资源文件如图片等转成python代码能识别的文件,这个参数基本和pyuic5的是一样的
这样就基本完成PYQT5在Pycharm环境中的配置:
3.测试
(这里拿一个OpenCV的小练习来测试一下------)
打开qtdesinger,新建一个窗体:
界面的左侧是Qt的常用控件”Widget Box”,右侧有一个控件属性窗口”Property Editor”,其余暂时用不到。本例中我们只用到了”Push Button”控件和”Label”控件:最上面的三个Label控件用于显示图片,可以在属性窗口调整它的大小,我们统一调整到150×150:
另外,控件上显示的文字”text”属性和控件的名字”objectName”属性需要修改,便于显示和代码调用:
点击Designer工具栏的”Edit Signals/Slots”按钮,进入槽函数编辑界面,点击旁边的”Edit Widgets”可以恢复正常视图:
然后点击按钮并拖动,当产生类似于电路中的接地符号时释放鼠标,在弹出的配置窗口中,可以看到左侧是按钮的常用事件,我们选择点击事件”clicked()”,然后添加一个名为”btnOpenCamera_Clicked()”的槽函数:
重复上面的步骤,给五个按钮添加五个槽函数,最终结果如下:
到此,我们就完成了界面设计的所有工作,按下Ctrl+S保存当前窗口为.ui文件。.ui文件其实是按照XML格式标记的内容,可以用文本编辑器将.ui文件打开看看。
UI文件转py代码
因为我们是用Designer工具设计出的界面,并不是用Python代码敲出来的,所以要想真正运行,需要使用pyuic5将ui文件转成py文件。pyuic5.exe默认在%\Scripts\下,比如我的是在:G:\GitWorkspace\PycharmProjects\PythonStudy\venv\Scripts\
打开cmd命令行,切换到ui文件的保存目录。Windows下有个小技巧,可以在目录的地址栏输入cmd,一步切换到当前目录. 然后执行这条指令:
pyuic5 -o mainForm.py pyqt_create_ui.ui
执行正常的话,就会生成mainForm.py文件,里面应该包含一个名为”Ui_MainWindow”的类。
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'pyqt_create_ui.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(859, 474)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.btnOpenCamera = QtWidgets.QPushButton(self.centralwidget)
self.btnOpenCamera.setGeometry(QtCore.QRect(60, 370, 100, 30))
self.btnOpenCamera.setObjectName("btnOpenCamera")
self.btnCaptureImg = QtWidgets.QPushButton(self.centralwidget)
self.btnCaptureImg.setGeometry(QtCore.QRect(220, 370, 100, 30))
self.btnCaptureImg.setObjectName("btnCaptureImg")
self.btnOpenImg = QtWidgets.QPushButton(self.centralwidget)
self.btnOpenImg.setGeometry(QtCore.QRect(370, 370, 93, 28))
self.btnOpenImg.setObjectName("btnOpenImg")
self.btnGraying = QtWidgets.QPushButton(self.centralwidget)
self.btnGraying.setGeometry(QtCore.QRect(520, 370, 100, 30))
self.btnGraying.setObjectName("btnGraying")
self.btnThresholdSegment = QtWidgets.QPushButton(self.centralwidget)
self.btnThresholdSegment.setGeometry(QtCore.QRect(670, 370, 150, 30))
self.btnThresholdSegment.setObjectName("btnThresholdSegment")
self.labelCamera = QtWidgets.QLabel(self.centralwidget)
self.labelCamera.setGeometry(QtCore.QRect(70, 100, 150, 150))
self.labelCamera.setObjectName("labelCamera")
self.labelCapture = QtWidgets.QLabel(self.centralwidget)
self.labelCapture.setGeometry(QtCore.QRect(330, 100, 150, 150))
self.labelCapture.setObjectName("labelCapture")
self.labelResult = QtWidgets.QLabel(self.centralwidget)
self.labelResult.setGeometry(QtCore.QRect(630, 100, 150, 150))
self.labelResult.setObjectName("labelResult")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 859, 26))
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.btnOpenCamera.clicked.connect(MainWindow.btnOpenCamera_Clicked)
self.btnCaptureImg.clicked.connect(MainWindow.btnCaptureImg_Clicked)
self.btnOpenImg.clicked.connect(MainWindow.btnOpenImg_Clicked)
self.btnGraying.clicked.connect(MainWindow.btnGrayingImg_Clicked)
self.btnThresholdSegment.clicked.connect(MainWindow.btnThresholdSegment_Clicked)
QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.btnOpenCamera.setText(_translate("MainWindow", "Open Camera"))
self.btnCaptureImg.setText(_translate("MainWindow", "Capture Image"))
self.btnOpenImg.setText(_translate("MainWindow", "Open Image"))
self.btnGraying.setText(_translate("MainWindow", "Graying"))
self.btnThresholdSegment.setText(_translate("MainWindow", "Threshold Segmentation"))
self.labelCamera.setText(_translate("MainWindow", "Camera"))
self.labelCapture.setText(_translate("MainWindow", "Captured Image"))
self.labelResult.setText(_translate("MainWindow", "Result Image"))
[注: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(1) # 0-调用笔记本内置摄像头,1-调用usb摄像头
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("Close Camera")
self._timer.start()
else:
self.btnOpenCamera.setText("Open Camera")
self._timer.stop() def btnCaptureImg_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 btnOpenImg_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 btnGrayingImg_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 btnThresholdSegment_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_())
运行结果:
参考文章:
http://ex2tron.wang/opencv-python-using-pyqt5-create-gui/#%E6%8C%91%E6%88%98%E5%86%85%E5%AE%B9
【计算机视觉】OpenCV篇(4) - Pycharm+PyQt5+Python小项目实战的更多相关文章
- Python 小案例实战 —— 简易银行存取款查询系统
Python 小案例实战 -- 简易银行存取款查询系统 涉及知识点 包的调用 字典.列表的混合运用 列表元素索引.追加 基本的循环与分支结构 源码 import sys import time ban ...
- pycharm+PyQt5+python最新开发环境配置
Python 3.6https://www.python.org/downloads/windows/========================================PyQt5 pip ...
- pycharm+PyQt5+python最新开发环境配置,踩坑过程详解
安装工具:Pycharm 专业版2017.3PyQT5python3 pyqt5-tools 设置扩展工具的参数找到setting->tools->external tools,点击加号新 ...
- Python小项目四:实现简单的web服务器
https://blog.csdn.net/u010103202/article/details/74002538 本博客是整理在学习实验楼的课程过程中记录下的笔记形成的,参考:https://www ...
- Python小项目之五子棋
1.项目简介 在刚刚学习完python套接字的时候做的一个五子棋小游戏,可以在局域网内双人对战,也可以和电脑对战 2.实现思路 局域网对战 对于局域网功能来说,首先建立连接(tcp),然后每次下棋时将 ...
- [IT学习]Python 小项目 通讯录 思路
建立一个通讯录查询软件,暂时只支持按姓名检索.出发点:无需登录企业门户,即可检索.要注意保护员工手机号,除非他自己同意显示. 欢迎您访问www.cnblogs.com/viphhs.转载请联系作者授权 ...
- python小项目之文本编辑器
高考完后这么久才想起这系列教程,实在抱歉,现在该来继续教程了. 本节利用前面所学知识,来完成一个小工具--文本编辑器! tkinter 在实现文本编辑器之前,先来了解下tkinter这个python库 ...
- 第一个Python小项目:图片转换成字符图片
实现的效果: ...
- python小项目练习之转换像素图片为字符图
实例来源实验楼网站,没事可以多逛逛,在此多谢实验楼的无私分享 from PIL import Image import argparse """ description: ...
随机推荐
- learning java AWT 布局管理器CardLayout
import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; public class CardLayo ...
- admin站点管理
admin中的显示 class Saltstack_GroupAdmin(admin.ModelAdmin): list_display = ['group_name','salt_minion_id ...
- 关灯问题II 状压DP
关灯问题II 状压DP \(n\)个灯,\(m\)个按钮,每个按钮都会对每个灯有不同影响,问最少多少次使灯熄完. \(n\le 10,m\le 100\) 状压DP的好题,体现了状压的基本套路与二进制 ...
- Codeforces 1172E Nauuo and ODT [LCT]
Codeforces ZROI那题是这题删掉修改的弱化版--ZROI还我培训费/px 思路 按照套路,我们考虑每种颜色的贡献,然后发现不包含某种颜色的路径条数更容易数,就是删掉该颜色的点后每个连通块大 ...
- shell编程题(四)
编译当前目录下的所有.c文件 #!/bin/bash ] ;] 输入参数个数 echo "Please follow up file.c!" echo "eg: ./ma ...
- Leetcode32. 最长有效括号
32. 最长有效括号 做法 \(f_{i}\)以\(i\)结尾的最长匹配 前提为\(s[i]=')'\) \(s[i-1]='('\),则\(f[i]=f[i-2]+2\) \(s[i-1]=')'\ ...
- 我为什么选择Vim
总看到一些飞快敲击键盘而不用鼠标的时候你可以很羡慕和佩服,其实这完全没有必要.就像一个吉他手熟练地弹吉他有必要羡慕吗?一个瓦匠熟练地砌砖有必要羡慕吗?这些都是他们赖以生存的工具而已,熟练地运用工具是理 ...
- Java中判断数组是否为空
一维数组// 一维数组: int[] arrayif(array == null || array.length == 0) return true; 二维数组//二维数组: int[][] a ...
- localstorage 必知必会
做项目中发现localstorage在不同的域名下是不能相互访问的,于是找到了以下这篇文章,对localStorage做一个深入的了解 HTML API localstorage在浏览器的API有两个 ...
- 消息队列Rabbit MQ 学习第一篇
1 介绍 1.1RabbitMQ MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队 ...