python-PyQt项目实战案例:制作一个简单的图像处理工具

news2024/11/28 6:46:32

文章目录

    • 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:鼠标框选

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2206629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10万+收藏!10万转发!AI绘画如何助力育儿赛道引爆短视频平台?

在自媒体领域,内容创作的竞争日益激烈,但育儿自媒体一直是一块热门且持久的“金矿”,吸引了无数创作者投身其中。然而,如何在这片红海中脱颖而出,成为了许多创作者面临的难题。而AI绘画的出现,无疑解决了创…

约束使用方法:

设置主键: 该列的值用来唯一标识表中每一行,用于强制表的实体完整性。这样的列定义为表的主键,也就是说主键的列中不允许有相同的数据。 设置默认: CREATE TABLE pet( #not null 非空 ZEROFILL 补零 先补零,在非空 id…

解锁机器人视觉与人工智能的潜力,从“盲人机器”改造成有视觉能力的机器人(上)

正如人类依赖眼睛和大脑来解读世界,机器人也需要自己的视觉系统来有效运作。没有视觉,机器人就如同蒙上双眼的人类,仅能执行预编程的命令,容易碰撞障碍物,并犯下代价高昂的错误。这正是机器人视觉发挥作用的地方&#…

k8s1.27部署ingress 1.11.2

k8s1.27部署ingress 1.11.2 要求: 1、使用主机网络。 2、多节点部署,以来标签:isingressistrue ingress1.11.2支持版本 官方参考链接: https://github.com/kubernetes/ingress-nginx/ 官网yaml https://raw.githubuserconten…

DYNPRO_SYNTAX_ERROR 主屏幕调用子屏幕,程序运行时错误

文章目录 问题描述问题查找和解决 问题描述 问题查找和解决

比特币社区心心念念的BTCFi进展如何了?——比特币与DeFi的未来

比特币在去中心化金融(DeFi)中的角色正在发生深刻变革。作为全球首个加密货币,比特币的主要用途从最初的点对点支付正逐渐转向更复杂的金融应用。通过BTCFi(比特币与DeFi的结合),比特币生态系统正加速崛起&…

QD1-P16 HTML 按钮标签(button)

本节学习 HTML 常用标签&#xff1a;button ‍ 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p16 ‍ ​<button>​ 标签在 HTML 中用于创建按钮&#xff0c;它是一个交互式元素&#xff0c;通常用于提交表单或触发某个脚本。以下是 <button>​ 标签的一些基本…

基于FPGA的DDS信号发生器(图文并茂+深度原理解析)

篇幅有限&#xff0c;本文详细源文件已打包 至个人主页资源&#xff0c;需要自取...... 前言 DDS&#xff08;直接数字合成&#xff09;技术是先进的频率合成手段&#xff0c;在数字信号处理与硬件实现领域作用关键。它因低成本、低功耗、高分辨率以及快速转换时间等优点备受认…

C++ stack和queue的使用介绍和模拟实现

内容摘要&#xff1a; 本文介绍了stack和queue的构造函数和一些成员函数&#xff0c;并模拟实现了stack和queue&#xff0c;分析了为什么选择deque作为适配器默认封装的对象 stack的介绍 栈是只能够在一端进行插入和删除的&#xff0c;这就是我们一直常说的“后进先出”&#x…

未来10年,哪些行业将被AI彻底颠覆?

随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;许多行业的工作方式正在发生显著变化。一些原本依赖人工处理的任务&#xff0c;正逐渐由AI接手并优化。在未来&#xff0c;AI将不仅仅是辅助工具&#xff0c;它可能会彻底改变某些行业的运作模式&#xff0c;…

通过AI技术克服自动化测试难点(下)

前面的文章里我们对可以应用到测试中的AI技术做了整体介绍&#xff0c;详细介绍了OpenCV技术、OCR技术和神经网络&#xff0c;本文我们继续为大家介绍卷积神经网络、数据集以及AI技术在其他方面和测试相关的创新。 卷积神经网络整体上的原理是这样的&#xff0c;首先在底层特征…

筛选因数快速法+map

前言&#xff1a;老是忘记怎么快速筛选因数&#xff0c;我们只需要枚举小于sqrt&#xff08; num &#xff09; 的数&#xff0c;这样可以降低很多复杂度&#xff0c;而且我们的因数一定是成对出现的&#xff0c;所以我们遇到一个因数的时候x&#xff0c;判断 x 2 x^2 x2 是否…

Java基础知识全面总结

第一章&#xff1a;类与对象 第一课&#xff1a;什么是面向对象编程 1.面向对象编程和面向过程编程的区别 无论是面向过程编程还是面向对象编程都是用于解决一个实际问题&#xff0c;当面向过程编程在解决一个问题时&#xff0c;更多的情况下是不会做出重用的设计思考的&…

FreeRTOS-内存管理

FreeRTOS-内存管理 一、内存管理简介二、内存管理实验 一、内存管理简介 在使用FreeRTOS创建任务、队列、信号量等对象时&#xff0c;有动态创建和静态创建(本质上内存分配的问题)FreeRTOS提供了5种动态内存管理算法&#xff0c;分别为heap_1、heap_2、heap_3、heap_4、heap_5…

二叉树的遍历 and 基本操作实现

二叉树的遍历 and 基本操作实现 1.二叉树的遍历前序遍历中序遍历后序遍历层序遍历 2.基本操作实现2.1 获取节点个数2.2 获取叶子节点的个数2.3 获取第K层节点的个数2.4 获取二叉树的高度2.5 检测值为value的元素是否存在 1.二叉树的遍历 二叉树由于其特殊结构&#xff0c;有四…

qt的5.sql和opencv 2024.10.11

1.QSqlQuery的增删改查 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QSqlQueryModel> #include <QTableView> #include <QLineEdit> #include <QSpinBox> #include <QPushButton> #includ…

如何阻止Chrome自动登录到网站

在数字化时代&#xff0c;浏览器的便利性极大地提高了我们的在线体验。然而&#xff0c;这种便利性有时也会带来隐私和安全方面的担忧。例如&#xff0c;Chrome浏览器可能会在某些网站上自动登录&#xff0c;这对于共享设备或公共计算机来说可能是个问题。本文将指导您如何阻止…

【C++】第三节:类与对象(中)

1、类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器…

【网络安全】将两个 Self-XSS 转变为可利用的 XSS

未经许可,不得转载。 文章目录 Self-XSS-1Self-XSS-2Self-XSS-1 目标应用程序为某在线商店,在其注册页面的First Name字段中注入XSS Payload: 注册成功,但当我尝试登录我的帐户时,我得到了403 Forbidden,即无法登录我的帐户。 我很好奇为什么我无法登录我的帐户,所以我…

SpringBoot集成Redis基础知识

Redis是干什么用的 Redis是一个开源的内存数据库&#xff0c;因其高性能、高可用性和丰富的数据结构&#xff0c;被广泛应用于多种场景下的数据存储和处理需求。以下是Redis的主要用途&#xff1a; 缓存&#xff1a;Redis最常用的用途是作为高性能缓存层&#xff0c;以减轻数…