文章目录
- 1.设计UI
- 2.编写功能代码
- 2.1 初始化ui界面及类成员参数
- 2.2 添加菜单栏
- 2.3 建立信号/槽连接
- 3.主要功能代码及效果
- 4.设置图像自动调节长宽尺寸但不改变长宽比例
- 参考文献
1.设计UI
对于UI的设计可以通过qt designer直接绘制,也可以通过编写python代码实现。当然,一般情况下还是建议使用designer绘制,然后转换为py代码后再进行微调。
在qt designer中绘制ui,其中使用label控件用于图像的显示,整体布局如下:
设计好ui后将其保存,并导出为py文件。
2.编写功能代码
2.1 初始化ui界面及类成员参数
class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承 QMainWindow 类和 Ui_MainWindow 界面类
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent) # 初始化父类
self.setupUi(self) # 继承 Ui_MainWindow 界面类
self.setWindowTitle('图像处理工具箱V1.0')
# 添加菜单项
t = self.menu_2
t.addAction('EqualHist')
t.addAction('Gamma transform')
t.addAction('Binary')
t.addAction('Edge detect')
t.addAction('Bilateral')
t = self.menu_3
t.addAction('About')
t.addAction('Other')
menu = self.menu_4
self.actExit = QAction("Exit", self)
self.actExit.triggered.connect(self.close)
menu.addAction(self.actExit)
# 添加可点击执行的菜单
self.mTest = QAction("其他", self)
self.mTest.triggered.connect(self.trigger_actHelp)
t = self.menuBar()
t.addAction(self.mTest)
# 菜单栏
self.actionopen.triggered.connect(self.openSlot) # 连接并执行 openSlot 子程序
self.action_save.triggered.connect(self.saveSlot) # 连接并执行 saveSlot 子程序
self.menu_3.triggered.connect(self.trigger_actHelp) # 连接并执行 trigger_actHelp 子程序
self.menu_4.triggered.connect(self.close) # 连接并执行 close 子程序
self.pushButton.clicked.connect(self.click_pushButton_1) # # 按钮触发:导入图像
self.pushButton_2.clicked.connect(self.click_pushButton_2) # # 按钮触发:灰度显示
self.pushButton_3.clicked.connect(self.click_pushButton_3) # # 按钮触发:伽马变换
self.pushButton_4.clicked.connect(self.click_pushButton_4) # # 按钮触发:二值化
self.pushButton_5.clicked.connect(self.click_pushButton_5) # 点击 # 按钮触发:边缘检测
self.pushButton_6.clicked.connect(self.click_pushButton_6) # 点击 # 按钮触发:双边滤波
self.pushButton_7.clicked.connect(self.saveSlot) # 点击 # 按钮触发:保存图像
# 初始化
self.img1 = np.ndarray(())
self.img2 = np.ndarray(())
self.img1 = cv.imread("./images/image.png") # OpenCV 读取图像
self.refreshShow(self.img1, self.label_1)
self.refreshShow(self.img1, self.label_2)
return
2.2 添加菜单栏
from PyQt5 import QtCore
from PyQt5.QtWidgets import QMainWindow, QWidget, QHBoxLayout, QPushButton, QApplication, QMenuBar, QLabel
class MainWin(QMainWindow):
def __init__(self):
super().__init__()
m_bar = QMenuBar()
f = m_bar.addMenu('File')
f.addAction('New')
t = m_bar.addMenu('Tool')
t.addAction('Copy')
t.addAction('Paste')
sub = t.addMenu('Sub')
sub1 = sub.addAction('sub1')
sub.addAction('sub2')
sub1.triggered.connect(self.sub1_trigger)
self.setMenuBar(m_bar)
self.label = QLabel()
self.label.setText('label text')
self.setCentralWidget(self.label)
def copy_msg(self):
print('Copy')
def sub1_trigger(self):
self.label.setText('sub1_trigger')
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = MainWin()
win.show()
sys.exit(app.exec())
2.3 建立信号/槽连接
self.pushButton.clicked.connect(self.click_pushButton_1) # # 按钮触发:导入图像
self.pushButton_2.clicked.connect(self.click_pushButton_2) # # 按钮触发:灰度显示
self.pushButton_3.clicked.connect(self.click_pushButton_3) # # 按钮触发:伽马变换
self.pushButton_4.clicked.connect(self.click_pushButton_4) # # 按钮触发:二值化
self.pushButton_5.clicked.connect(self.click_pushButton_5) # 点击 # 按钮触发:边缘检测
self.pushButton_6.clicked.connect(self.click_pushButton_6) # 点击 # 按钮触发:双边滤波
self.pushButton_7.clicked.connect(self.saveSlot) # 点击 # 按钮触发:保存图像
3.主要功能代码及效果
# _*_ coding:utf-8 _*_
import sys
import cv2 as cv
import numpy as np
from PyQt5.QtCore import qDebug
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from ImageProV1 import Ui_MainWindow # 导入 ImageProV1.py 中的 Ui_MainWindow 界面类
class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承 QMainWindow 类和 Ui_MainWindow 界面类
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent) # 初始化父类
self.setupUi(self) # 继承 Ui_MainWindow 界面类
self.setWindowTitle('图像处理工具箱V1.0')
# 添加菜单项
t = self.menu_2
t.addAction('EqualHist')
t.addAction('Gamma transform')
t.addAction('Binary')
t.addAction('Edge detect')
t.addAction('Bilateral')
t = self.menu_3
t.addAction('About')
t.addAction('Other')
menu = self.menu_4
self.actExit = QAction("Exit", self)
self.actExit.triggered.connect(self.close)
menu.addAction(self.actExit)
# 添加可点击执行的菜单
self.mTest = QAction("其他", self)
self.mTest.triggered.connect(self.trigger_actHelp)
t = self.menuBar()
t.addAction(self.mTest)
# 菜单栏
self.actionopen.triggered.connect(self.openSlot) # 连接并执行 openSlot 子程序
self.action_save.triggered.connect(self.saveSlot) # 连接并执行 saveSlot 子程序
self.menu_3.triggered.connect(self.trigger_actHelp) # 连接并执行 trigger_actHelp 子程序
self.menu_4.triggered.connect(self.close) # 连接并执行 close 子程序
# 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButton
self.pushButton.clicked.connect(self.click_pushButton_1) # # 按钮触发:导入图像
self.pushButton_2.clicked.connect(self.click_pushButton_2) # # 按钮触发:灰度显示
self.pushButton_3.clicked.connect(self.click_pushButton_3) # # 按钮触发:伽马变换
self.pushButton_4.clicked.connect(self.click_pushButton_4) # # 按钮触发:二值化
self.pushButton_5.clicked.connect(self.click_pushButton_5) # 点击 # 按钮触发:边缘检测
self.pushButton_6.clicked.connect(self.click_pushButton_6) # 点击 # 按钮触发:双边滤波
self.pushButton_7.clicked.connect(self.saveSlot) # 点击 # 按钮触发:保存图像
# 初始化
self.img1 = np.ndarray(())
self.img2 = np.ndarray(())
self.img1 = cv.imread("./images/image.png") # OpenCV 读取图像
self.refreshShow(self.img1, self.label_1)
self.refreshShow(self.img1, self.label_2)
return
def click_pushButton_1(self): # 点击 pushButton_1 触发
try:
self.img1 = self.openSlot() # 读取图像
self.img2 = self.img1.copy()
print("click_pushButton_1", self.img1.shape)
self.refreshShow(self.img1, self.label_1) # 刷新显示
except:
print('open file failed.')
return
def click_pushButton_2(self): # 点击 pushButton_2 触发
print("pushButton_2")
self.img2 = cv.cvtColor(self.img1, cv.COLOR_BGR2GRAY) # 图片格式转换:BGR -> Gray
self.img2 = cv.equalizeHist(self.img2, None)
self.refreshShow(self.img2, self.label_2) # 刷新显示
return
def click_pushButton_3(self): # 点击 pushButton_3 伽马变换
print("pushButton_3")
self.img2 = self.adjust_gamma(self.img1)
self.refreshShow(self.img2, self.label_2) # 刷新显示
return
def click_pushButton_4(self): # 点击 pushButton_3 触发
print("pushButton_4")
temp = cv.cvtColor(self.img1, cv.COLOR_BGR2GRAY) # 图片格式转换:BGR -> Gray
_, self.img2 = cv.threshold(temp, 0, 255, cv.THRESH_OTSU + cv.THRESH_BINARY)
self.refreshShow(self.img2, self.label_2) # 刷新显示
return
def click_pushButton_5(self):
print("pushButton_5")
temp = cv.cvtColor(self.img1, cv.COLOR_BGR2GRAY) # 图片格式转换:BGR -> Gray
self.img2 = cv.Canny(self.img1, 10, 60)
self.refreshShow(self.img2, self.label_2) # 刷新显示
return
def click_pushButton_6(self):
print("pushButton_6")
self.img2 = cv.bilateralFilter(self.img1, 11, 20, 20, None)
self.refreshShow(self.img2, self.label_2) # 刷新显示
return
# 默认gamma值为1.0,默认不变化
def adjust_gamma(self, image, gamma=1.5):
brighter_image = np.array(np.power((image / 255), gamma) * 255, dtype=np.uint8)
return brighter_image
def refreshShow(self, img, label):
print('shape: ', img.shape, label)
qImg = self.cvToQImage(img) # OpenCV 转为 PyQt 图像格式
label.setScaledContents(True) # 需要在图片显示之前进行设置
# 当窗口大小改变时,调整图像大小以适应 QLabel,同时保持长宽比例
# qImg = qImg.scaled(label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
label.setPixmap((QPixmap.fromImage(qImg))) # 加载 PyQt 图像
return
def openSlot(self, flag=1): # 读取图像文件
try:
# OpenCV 读取图像文件
fileName, _ = QFileDialog.getOpenFileName(self, "Open Image", "../images/", "*.png *.jpg *.tif")
if flag == 0 or flag == "gray":
img = cv.imread(fileName, cv.IMREAD_GRAYSCALE) # 读取灰度图像
else:
img = cv.imread(fileName, cv.IMREAD_COLOR) # 读取彩色图像
print(fileName, img.shape)
return img
except:
return None
def saveSlot(self): # 保存图像文件
# 选择存储文件 dialog
try:
fileName, tmp = QFileDialog.getSaveFileName(self, "Save Image", "../images/", '*.png; *.jpg; *.tif')
if self.img2.size == 1:
return
# OpenCV 写入图像文件
ret = cv.imwrite(fileName, self.img2)
if ret:
print(fileName, self.img2.shape)
except:
print('save failed.')
return
def cvToQImage(self, image):
if image.dtype == np.uint8:
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 3: # CV_8UC3
qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_RGB888)
return qImg.rgbSwapped()
elif channels == 1:
qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)
return qImg
else:
qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])
return QImage()
def qPixmapToCV(self, qPixmap):
qImg = qPixmap.toImage()
shape = (qImg.height(), qImg.bytesPerLine() * 8 // qImg.depth())
shape += (4,)
ptr = qImg.bits()
ptr.setsize(qImg.byteCount())
image = np.array(ptr, dtype=np.uint8).reshape(shape)
image = image[..., :3]
return image
def trigger_actHelp(self): # 动作 actHelp 触发
QMessageBox.about(self, "About", """数字图像处理工具箱 v1.0""")
return
if __name__ == '__main__':
app = QApplication(sys.argv) # 在 QApplication 方法中使用,创建应用程序对象
myWin = MyMainWindow() # 实例化 MyMainWindow 类,创建主窗口
myWin.show() # 在桌面显示控件 myWin
sys.exit(app.exec_()) # 结束进程,退出程序
4.设置图像自动调节长宽尺寸但不改变长宽比例
# 当窗口大小改变时,调整图像大小以适应 QLabel,同时保持长宽比例
scaledImage = self.image.scaled(self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.label.setPixmap(scaledImage)
参考文献
[1] python-在PyCharm中使用PyQt5
[2] PyQt5使用QLabel显示OpenCV图像的自适应调节(只改变大小不改变比例)
[3] OpenCV-PyQT项目实战(7)项目案例03:鼠标框选