OpenCV-PyQT项目实战(6)项目案例02滚动条应用

news2024/11/15 12:51:29

欢迎关注『OpenCV-PyQT项目实战 @ Youcans』系列,持续更新中
OpenCV-PyQT项目实战(1)安装与环境配置
OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门
OpenCV-PyQT项目实战(3)信号与槽机制
OpenCV-PyQT项目实战(4)OpenCV 与PyQt的图像转换
OpenCV-PyQT项目实战(5)项目案例01图像模糊
OpenCV-PyQT项目实战(6)项目案例02滚动条应用

文章目录

  • OpenCV-PyQT项目实战(6)项目案例02:滚动条应用
    • 1. 调节输入控件
      • 1.1 输入控件
      • 1.2 水平/垂直滚动条(QScrollBar)
      • 1.3 水平/垂直滑动槽(QSlider)
      • 1.4 旋转调节盘(QDial)
      • 1.5 其它输入调节控件
    • 2. 项目实战:使用滑动槽(QSlider)调节模糊尺度
      • 2.1 使用 QtDesigner 开发 PyQt5 图形界面
      • 2.2. 项目主程序的开发
        • 2.2.1 getHorizontalSliderValue 槽函数
        • 2.2.2 getSpinBoxValue槽函数
        • 2.2.3 信号与槽的连接
      • 2.3 主程序
      • 2.4. 项目例程的测试
    • 3. 项目实战:调节图像亮度和对比度
      • 3.1 使用 QtDesigner 开发 PyQt5 图形界面
      • 3.2. 项目主程序的开发
        • 3.2.1 getHorizontalSliderValue 槽函数
        • 3.2.2 getSpinBoxValue槽函数
        • 3.2.3 信号与槽的连接
      • 3.3 完整例程及运行结果


OpenCV-PyQT项目实战(6)项目案例02:滚动条应用

本节介绍两个 OpenCV-PyQt 项目案例,通过案例学习滚动条等调节输入控件。


1. 调节输入控件

1.1 输入控件

在 QtDesigner 左侧的 “WidgetBox” 工具栏中的"Input Widget" 组,设有多种不同类型的输入控件,例如:
文本输入控件:QlineEdit(单行输入)、QTextEdit(多行输入)、QPlainTextEdit(普通多行输入);
数字输入控件:QSpinBox(整型数据输入)、QDoubleSpinBox(浮点数据输入);
调节输入控件:QAbstractSpinBox(步长调节输入)、QDateEdit(日期输入)、QTimeEdit(时间输入)、QDateTimeEdit(日期和时间输入)。

用鼠标将工具栏中的输入控件拖拽到 QtDesigner 中间的图形界面编辑窗口,就可以在图像界面创建了一个按钮控件。

在这里插入图片描述


1.2 水平/垂直滚动条(QScrollBar)

PyQt 中提供了的滚动条组件 QScrollBar,包括水平滚动条(Horizontal Scroll Bar)和垂直滚动条(Vertical Scroll Bar)。
滚动条为当前位置提供视觉指示,用于访问当前不可见的文档或图像区域。滚动条有一个滑块,也可以用于用于调节整数值,但默认不显示数字。

QScrollBar的常用属性:

  • minimum:最小值
  • maximum:最大值
  • singlestep:步距
  • value:初始值
  • sliderposition:位置,如果数字是从1开始且步距是1,则value和sliderposition是一致的。
  • notchesVisible :是否设置刻度
  • wrapping :是否刻度不留缺口
  • notchTarget :设置刻度密度,即单位刻度所代表的大小

QScrollBar的常用方法:

  • setMinimum(): 设置滚动条的最小值
  • setMaximum(): 设置滚动条的最大值
  • setSingleStep(): 设置滚动条的步长
  • setValue(): 设置滚动条的值
  • value(): 获得滚动条控件的值

QScrollBar可以发送的信号(即可以使用这些信号来触发槽函数):

  • valueChanged: 当滑块的值发生改变时发射此信号,此信号是最常用的
  • sliderPressed: 当用户按下滑块时发射此信号
  • sliderMoved: 当用户拖动滑块时发射此信号
  • sliderReleased: 当用户释放滑块时发射此信号

1.3 水平/垂直滑动槽(QSlider)

PyQt 中提供了的滑动槽组件 QSlider,包括水平滑动槽(Horizontal Slider)和垂直滑动槽(Vertical Slider)。
滑动槽组件展示了一个可以移动的滑块,它是控制有界值的部件,常用于调节整数大小。

QSlider的常用属性:

  • minimum:最小值
  • maximum:最大值
  • singlestep:步距
  • value:初始值
  • sliderposition:位置,如果数字是从1开始且步距是1,则value和sliderposition是一致的。
  • notchesVisible :是否设置刻度
  • wrapping :是否刻度不留缺口
  • notchTarget :设置刻度密度,即单位刻度所代表的大小

QSlider的常用方法:

  • setMinimum(): 设置滑动槽的最小值
  • setMaximum(): 设置滑动槽的最大值
  • setSingleStep(): 设置滑动槽的递增步长
  • setTickInterval():将刻度数放在凹槽上
  • setValue(): 设置滑动槽的值
  • value(): 获得滑动槽控件的值

QSlider可以发送的信号(即可以使用这些信号来触发槽函数):

  • valueChanged: 当滑块的值发生改变时发射此信号,此信号是最常用的
  • sliderPressed: 当用户按下滑块时发射此信号
  • sliderMoved: 当用户拖动滑块时发射此信号
  • sliderReleased: 当用户释放滑块时发射此信号

1.4 旋转调节盘(QDial)

QDial是一个圆表盘控件, 也称为旋转调节盘,可以用于处理整数或浮点数值。各种仪表仪盘都可以抽象成一个圆表盘控件,例如汽车仪表盘上的速度计。

QDial的常用属性:

  • minimum:最小值
  • maximum:最大值
  • singlestep:步距
  • value:初始值
  • sliderposition:位置,如果数字是从1开始且步距是1,则value和sliderposition是一致的。
  • notchesVisible :是否设置刻度
  • wrapping :是否刻度不留缺口
  • notchTarget :设置刻度密度,即单位刻度所代表的大小

QDial常用方法:

  • setNotchesVisible(): 设置是否显示刻度
  • notchesVisile(): 刻度可见返回True,不可见返回False
  • setWrapping(): 设置是否回绕
  • wrapping(): 如果回绕则返回True, 否则返回False
  • setNotchTarget(): 设置刻度之间的目标刻度
  • notchTarget(): 返回刻度间的目标宽度
  • notchSize(): 当前刻度尺寸

1.5 其它输入调节控件

步长调节输入(QAbstractSpinBox)

QAbstractSpinBox 将所有步长调节器的通用的功能抽象出了一个父类,也可以直接实例化使用。QAbstractSpinBox包含了一个QLineEdit和两个QPushbutton,数据的更改可以通过点击按钮或使用键盘输入。

日期和时间输入(QDateEdit/QTimeEdit/QDateTimeEdit)

QDateEdit 控件用于编辑日期,QTimeEdit 控件用于编辑时间,QDateTimeEdit同时编辑日期时间的控件。可以使用键盘上的上下键头按钮来增加或减少日期、时间。

整型数字调节框(QSpinBox)

QSpinBox 是一个计数器控件,允许用户选择一个整数通过上下按键递增或者递减,也可以直接输入整数的数值。默认取值范围为 0-99,每次调节的步长为 1。

浮点数字调节框(QDoubleSpinBox)

QDoubleSpinBox 是浮点数据计数器控件,用于处理浮点数值。默认精度为 2位小数。



2. 项目实战:使用滑动槽(QSlider)调节模糊尺度

在uiDemo4.ui中,我们创建了按钮控件:“1 打开”、“2 灰度”、“3 模糊”、“4 帮助”、“5 退出”。其中模糊功能中,模糊尺度是在程序内部设置的。本例中,我们使用滑动槽(QSlider)调节模糊尺度。



2.1 使用 QtDesigner 开发 PyQt5 图形界面

使用 QtDesigner 开发 PyQt5 图形界面的基本步骤是:
(1)使用图形界面设计工具 QtDesigner 进行图形界面设计,生成 .ui 文件;
(2)使用 UI 转换工具 PyUIC 将 .ui 文件转换为 .py 文件;
(3)编写一个 Python 应用程序调用 .py 界面文件,就可以实现 Python 平台的 GUI 编程。

本例在 uiDemo4.ui 的基础上,添加一个滑动槽控件 QSlider:

从 QtDesigner 左侧控件栏的 “Input Widgets” 中选择 滑动槽控件 QSlider,移动鼠标将滑动槽 QSlider拖动到新建图形窗口内的任意位置,就在图形窗口位置生成了一个 QSlider 对象。

  • 鼠标左键选中图形窗口中的这个 QSlider滑动槽对象,拖动鼠标可以调整控件的位置。
  • 鼠标选中 QSlider滑动槽对象,可以在右侧的 “属性编辑器” 内对对象的属性进行编辑和修改,例如:
    • 将 QSlider 对象的高度修改为 80,宽度修改为 40;
    • 设置QSlider 对象的最小值minimum=1、最大值maximum=64和步长singleStep=1。

类似地,添加一个整型数字调节框(QSpinBox)控件。


在这里插入图片描述

于是,我们就完成了本项目的图形界面设计,将其保存为 uiDemo5.ui文件。

在 PyCharm中,使用 PyUIC 将选中的 uiDemo5.ui 文件转换为 .py 文件,就得到了 uiDemo5.py 文件。



2.2. 项目主程序的开发

2.2.1 getHorizontalSliderValue 槽函数

getHorizontalSliderValue 槽函数,由valueChanged信号触发。

    def getHorizontalSliderValue(self):
        self.sigma = max(self.horizontalSlider.value(),3)  # 修改模糊尺度
        ksize = (self.sigma, self.sigma)  # 高斯滤波器核的尺寸
        print("HorizontalSlider=", self.sigma)

2.2.2 getSpinBoxValue槽函数

getSpinBoxValue槽函数,由valueChanged信号触发。

    def getSpinBoxValue(self):
        self.sigma = self.spinBox.value()  # 修改模糊尺度
        print("SpinBoxValue=", self.sigma)
        ksize = (self.sigma, self.sigma)  # 高斯滤波器核的尺寸
        blur = cv.GaussianBlur(self.img1, ksize, 0)  # sigma 由 ksize 计算
        self.refreshShow(blur, self.label_2)  # 刷新显示

2.2.3 信号与槽的连接

        # 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButton
        # self.pushButton_1.clicked.connect(self.openSlot)  # 点击 pushButton_1 触发
        self.pushButton_1.clicked.connect(self.click_pushButton_1)  # 点击 pushButton_1 触发
        # self.pushButton_2.clicked.connect(self.click_pushButton_2)  # 点击 pushButton_2 触发
        self.pushButton_3.clicked.connect(self.click_pushButton_3)  # 点击 pushButton_3 触发
        # self.pushButton_4.clicked.connect(self.trigger_actHelp)  # 点击 pushButton_4 触发
        self.pushButton_5.clicked.connect(self.close)  # 点击 pushButton_5 关闭窗口
        self.spinBox.valueChanged.connect(self.getSpinBoxValue)  # spinBox控件valueChanged信号与getSpinBoxValue槽函数关联
        self.horizontalSlider.valueChanged.connect(self.getHorizontalSliderValue)  # horizontalSlider控件valueChanged信号触发

2.3 主程序

# OpenCVPyqt06.py
# Demo06 of GUI by PyQt5
# Copyright 2023 Youcans, XUPT
# Crated:2023-02-03

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象
    myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口
    myWin.show()  # 在桌面显示控件 myWin
    sys.exit(app.exec_())  # 结束进程,退出程序

2.4. 项目例程的测试

测试应用程序 OpenCVPyqt06.py的各项功能:

(1)运行 OpenCVPyqt06,弹出程序窗口,自动加载图像 Fig0301。

(2)点击 “1 打开”,运行 click_pushButton_1 槽函数。选择目录路径和图像文件,读取图像并在窗口显示彩色图像。

(3)点击 "3 模糊"按钮,运行 click_pushButton_3 槽函数。在窗口右侧显示高斯模糊处理的彩色图像。

(4)调节滑动槽或整型数字调节框,用户设置模糊尺度,则在 Label_2 刷新模糊图像。

在这里插入图片描述


(5)点击 "5 退出"按钮,关闭窗口。



3. 项目实战:调节图像亮度和对比度

本项目模拟 PhotoShop 与 AcdSee 中的图像亮度和对比度方法,通过手动调节阴影、中间调和高光参数设置,调节图像的亮度和对比度。相关方法的原理和OpenCV算法实现,详见:【OpenCV 例程300篇】206. Photoshop 色阶调整算法。



3.1 使用 QtDesigner 开发 PyQt5 图形界面

本例在 uiDemo4.ui 的基础上,添加滑动槽控件 QSlider:

从 QtDesigner 左侧控件栏的 “Input Widgets” 中选择 滑动槽控件 QSlider,移动鼠标将滑动槽 QSlider拖动到新建图形窗口内的任意位置,就在图形窗口位置生成了一个 QSlider 对象。

  • 鼠标左键选中图形窗口中的这个 QSlider滑动槽对象,拖动鼠标可以调整控件的位置。
  • 鼠标选中 QSlider滑动槽对象,可以在右侧的 “属性编辑器” 内对对象的属性进行编辑和修改,例如:
    • 将 QSlider 对象的高度修改为 80,宽度修改为 40;
    • 设置QSlider 对象的最小值minimum=1、最大值maximum=64和步长singleStep=1。

类似地,添加一个整型数字调节框(QSpinBox)控件。


在这里插入图片描述

于是,我们就完成了本项目的图形界面设计,将其保存为 uiDemo7.ui文件。

在 PyCharm中,使用 PyUIC 将选中的 uiDemo7.ui 文件转换为 .py 文件,就得到了 uiDemo7.py 文件。



3.2. 项目主程序的开发

3.2.1 getHorizontalSliderValue 槽函数

getHorizontalSliderValue 槽函数,由valueChanged信号触发。

    def getHorizontalSliderValue_1(self):  # 最小值
        slideValue1 = self.horizontalSlider_1.value()
        self.spinBox_1.setValue(slideValue1)  # 同步修改
        self.spinBox_3.setMinimum(slideValue1)  # 关联修改
        self.horizontalSlider_3.setMinimum(slideValue1)  # 关联修改
        print("HorizontalSliderValue_1 = ", slideValue1)

    def getHorizontalSliderValue_2(self):
        slideValue2 = self.horizontalSlider_2.value()
        self.spinBox_2.setValue(slideValue2)
        print("HorizontalSliderValue_2 = ", slideValue2)

    def getHorizontalSliderValue_3(self):
        slideValue3 = self.horizontalSlider_3.value()
        self.spinBox_3.setValue(slideValue3)  # 同步修改
        self.spinBox_1.setMaximum(slideValue3)  # 关联修改
        self.horizontalSlider_1.setMaximum(slideValue3)  # 关联修改
        print("HorizontalSliderValue_3 = ", slideValue3)

3.2.2 getSpinBoxValue槽函数

getSpinBoxValue槽函数,由valueChanged信号触发。

    def getSpinBoxValue_1(self):
        spinBoxValue1 = self.spinBox_1.value()
        self.horizontalSlider_1.setValue(spinBoxValue1)
        self.spinBox_3.setMinimum(spinBoxValue1)  # 关联修改
        self.horizontalSlider_3.setMinimum(spinBoxValue1)  # 关联修改
        print("SpinBoxValue_1 = ", spinBoxValue1)

    def getSpinBoxValue_2(self):
        spinBoxValue2 = self.spinBox_2.value()
        self.horizontalSlider_2.setValue(spinBoxValue2)
        print("SpinBoxValue_2 = ", spinBoxValue2)

    def getSpinBoxValue_3(self):
        spinBoxValue3 = self.spinBox_3.value()
        self.horizontalSlider_3.setValue(spinBoxValue

3.2.3 信号与槽的连接

        # 菜单栏
        self.actionOpen.triggered.connect(self.openSlot)  # 连接并执行 openSlot 子程序
        self.actionSave.triggered.connect(self.saveSlot)  # 连接并执行 saveSlot 子程序
        self.actionHelp.triggered.connect(self.trigger_actHelp)  # 连接并执行 trigger_actHelp 子程序
        self.actionQuit.triggered.connect(self.close)  # 连接并执行 trigger_actHelp 子程序

        # 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButton
        self.pushButton_1.clicked.connect(self.click_pushButton_1)  # 按钮触发:导入图像
        self.pushButton_3.clicked.connect(self.click_pushButton_3)  # # 按钮触发:调整色阶
        self.pushButton_5.clicked.connect(self.close)  # 点击 # 按钮触发:关闭
        self.horizontalSlider_1.valueChanged.connect(self.getHorizontalSliderValue_1)  # horizontalSlider控件valueChanged信号触发
        self.horizontalSlider_2.valueChanged.connect(self.getHorizontalSliderValue_2)
        self.horizontalSlider_3.valueChanged.connect(self.getHorizontalSliderValue_3)
        self.spinBox_1.valueChanged.connect(self.getSpinBoxValue_1)  # spinBox控件valueChanged信号与getSpinBoxValue槽函数关联
        self.spinBox_2.valueChanged.connect(self.getSpinBoxValue_2)
        self.spinBox_3.valueChanged.connect(self.getSpinBoxValue_3)

3.3 完整例程及运行结果

# OpenCVPyqt07.py
# Demo07 of GUI by PyQt5
# Copyright 2023 Youcans, XUPT
# Crated:2023-02-08

import sys
import cv2 as cv
import numpy as np
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from uiDemo7 import Ui_MainWindow  # 导入 uiDemo5.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.img1 = np.ndarray(())  # 初始化图像 ndarry,用于存储图像
        # self.img2 = np.ndarray(())

        # 菜单栏
        self.actionOpen.triggered.connect(self.openSlot)  # 连接并执行 openSlot 子程序
        self.actionSave.triggered.connect(self.saveSlot)  # 连接并执行 saveSlot 子程序
        self.actionHelp.triggered.connect(self.trigger_actHelp)  # 连接并执行 trigger_actHelp 子程序
        self.actionQuit.triggered.connect(self.close)  # 连接并执行 trigger_actHelp 子程序

        # 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButton
        self.pushButton_1.clicked.connect(self.click_pushButton_1)  # 按钮触发:导入图像
        self.pushButton_3.clicked.connect(self.click_pushButton_3)  # # 按钮触发:调整色阶
        self.pushButton_5.clicked.connect(self.close)  # 点击 # 按钮触发:关闭
        self.horizontalSlider_1.valueChanged.connect(self.getHorizontalSliderValue_1)  # horizontalSlider控件valueChanged信号触发
        self.horizontalSlider_2.valueChanged.connect(self.getHorizontalSliderValue_2)
        self.horizontalSlider_3.valueChanged.connect(self.getHorizontalSliderValue_3)
        self.spinBox_1.valueChanged.connect(self.getSpinBoxValue_1)  # spinBox控件valueChanged信号与getSpinBoxValue槽函数关联
        self.spinBox_2.valueChanged.connect(self.getSpinBoxValue_2)
        self.spinBox_3.valueChanged.connect(self.getSpinBoxValue_3)

        # 初始化
        self.img1 = cv.imread("../images/Fig0301.png")  # OpenCV 读取图像
        self.refreshShow(self.img1, self.label_1)
        self.refreshShow(self.img1, self.label_2)
        return

    def getHorizontalSliderValue_1(self):  # 最小值
        slideValue1 = self.horizontalSlider_1.value()
        self.spinBox_1.setValue(slideValue1)  # 同步修改
        self.spinBox_3.setMinimum(slideValue1)  # 关联修改
        self.horizontalSlider_3.setMinimum(slideValue1)  # 关联修改
        print("HorizontalSliderValue_1 = ", slideValue1)

    def getHorizontalSliderValue_2(self):
        slideValue2 = self.horizontalSlider_2.value()
        self.spinBox_2.setValue(slideValue2)
        print("HorizontalSliderValue_2 = ", slideValue2)

    def getHorizontalSliderValue_3(self):
        slideValue3 = self.horizontalSlider_3.value()
        self.spinBox_3.setValue(slideValue3)  # 同步修改
        self.spinBox_1.setMaximum(slideValue3)  # 关联修改
        self.horizontalSlider_1.setMaximum(slideValue3)  # 关联修改
        print("HorizontalSliderValue_3 = ", slideValue3)

    def getSpinBoxValue_1(self):
        spinBoxValue1 = self.spinBox_1.value()
        self.horizontalSlider_1.setValue(spinBoxValue1)
        self.spinBox_3.setMinimum(spinBoxValue1)  # 关联修改
        self.horizontalSlider_3.setMinimum(spinBoxValue1)  # 关联修改
        print("SpinBoxValue_1 = ", spinBoxValue1)

    def getSpinBoxValue_2(self):
        spinBoxValue2 = self.spinBox_2.value()
        self.horizontalSlider_2.setValue(spinBoxValue2)
        print("SpinBoxValue_2 = ", spinBoxValue2)

    def getSpinBoxValue_3(self):
        spinBoxValue3 = self.spinBox_3.value()
        self.horizontalSlider_3.setValue(spinBoxValue3)
        print("SpinBoxValue_3 = ", spinBoxValue3)

    def click_pushButton_1(self):  # 点击 pushButton_1 触发
        self.img1 = self.openSlot()  # 读取图像
        print("click_pushButton_1", self.img1.shape)
        self.refreshShow(self.img1, self.label_1)  # 刷新显示
        return

    def click_pushButton_2(self):  # 点击 pushButton_2 触发
        print("pushButton_2")
        self.img2 = cv.cvtColor(self.img1, cv.COLOR_BGR2GRAY)  # 图片格式转换:BGR -> Gray
        self.refreshShow(self.img2, self.label_2)  # 刷新显示
        return

    def click_pushButton_3(self):  # 点击 pushButton_3 触发 调整色阶
        print("pushButton_3")
        Sin = self.horizontalSlider_1.value()
        Mt = self.horizontalSlider_2.value()
        Hin = self.horizontalSlider_3.value()
        Sin = min(max(Sin, 0), Hin - 2)  # Sin, 黑场阈值, 0<=Sin<Hin
        Hin = min(Hin, 255)  # Hin, 白场阈值, Sin<Hin<=255
        Mt = min(max(Mt, 0.01), 9.99)  # Mt, 灰场调节值, 0.01~9.99
        Sout = 0#min(max(Sout, 0), Hout - 2)  # Sout, 输出黑场阈值, 0<=Sout<Hout
        Hout = 255#min(Hout, 255)  # Hout, 输出白场阈值, Sout<Hout<=255
        print("Sin={},Hin={},Mt={}".format(Sin, Hin, Mt))

        difIn = Hin - Sin
        difOut = Hout - Sout
        table = np.zeros(256, np.uint8)
        for i in range(256):
            V1 = min(max(255 * (i - Sin) / difIn, 0), 255)  # 输入动态线性拉伸
            V2 = 255 * np.power(V1 / 255, 1 / Mt)  # 灰场伽马调节
            table[i] = min(max(Sout + difOut * V2 / 255, 0), 255)  # 输出线性拉伸

        imgTone = cv.LUT(self.img1, table)
        self.refreshShow(imgTone, self.label_2)  # 刷新显示
        return

    def refreshShow(self, img, label):
        print(img.shape, label)
        qImg = self.cvToQImage(img)  # OpenCV 转为 PyQt 图像格式
        label.setPixmap((QPixmap.fromImage(qImg)))  # 加载 PyQt 图像
        return

    def openSlot(self, flag=1):  # 读取图像文件
        # 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

    def saveSlot(self):  # 保存图像文件
        # 选择存储文件 dialog
        fileName, tmp = QFileDialog.getSaveFileName(self, "Save Image", "../images/", '*.png; *.jpg; *.tif')
        if self.img1.size == 1:
            return
        # OpenCV 写入图像文件
        ret = cv.imwrite(fileName, self.img1)
        if ret:
            print(fileName, self.img.shape)
        return

    def cvToQImage(self, image):
        # 8-bits unsigned, NO. OF CHANNELS=1
        if image.dtype == np.uint8:
            channels = 1 if len(image.shape) == 2 else image.shape[2]
        if channels == 3:  # CV_8UC3
            # Create QImage with same dimensions as input Mat
            qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_RGB888)
            return qImg.rgbSwapped()
        elif channels == 1:
            # Create QImage with same dimensions as input Mat
            qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)
            return qImg
        else:
            QtCore.qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])
            return QImage()

    def qPixmapToCV(self, qPixmap):  # PyQt图像 转换为 OpenCV图像
        qImg = qPixmap.toImage()  # QPixmap 转换为 QImage
        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)  # 定义 OpenCV 图像
        image = image[..., :3]
        return image

    def trigger_actHelp(self):  # 动作 actHelp 触发
        QMessageBox.about(self, "About",
                          """数字图像处理工具箱 v1.0\nCopyright YouCans, XUPT 2023""")
        return

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象
    myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口
    myWin.show()  # 在桌面显示控件 myWin
    sys.exit(app.exec_())  # 结束进程,退出程序



运行结果:

在这里插入图片描述


【本节完】


版权声明:

Copyright 2023 youcans, XUPT

Crated:2023-2-8


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

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

相关文章

JumpServer开源堡垒机v3.0版本设计重点解读

编者注&#xff1a;在1月17日的JumpServer开源堡垒机v3.0预发布恳谈会直播中&#xff0c;JumpServer创始人广宏伟与大家分享了JumpServer v3.0版本的设计思路与功能亮点。在v3.0版本正式发布之前&#xff0c;JumpServer开源项目组基于此次直播内容为大家整理总结了JumpServer v…

C++11可变模板参数

C11可变模板参数一、简介二、语法三、可变模版参数函数3.1、递归函数方式展开参数包3.2、逗号表达式展开参数包一、简介 C11的新特性–可变模版参数&#xff08;variadic templates&#xff09;是C11新增的最强大的特性之一&#xff0c;它对参数进行了高度泛化&#xff0c;它能…

STM32单片机红外遥控

红外遥控接口电路STM32单片机红外遥控程序源代码#include "sys.h"#define LED_RED PBout(12) //红色发光二极管控制管脚初始化PB12#define LED_GREEN PBout(13) //绿色发光二极管控制管脚初始化PB13#define LED_YELLOW PBout(14) //黄色发光二极管控制管脚初始化PB14…

反射,枚举,lambda表达式

目录 1、反射 1.1 基本概念 1.2 反射相关的类 1.3 创建 Class 对象 1.4 反射的使用 1.4.1 通过反射创建对象&#xff1a; 1.4.2 获取私有的构造方法 1.4.3 获取私有的成员变量 1.4.4 获取私有的方法 1.5 总结 2、枚举 2.1 认识枚举 2.2 使用枚举 2.3 枚举与反射…

第4章 流程控制-if-else,Switch,For循环(循环守卫,循环步长,倒叙打印),While循环,多重循环...

第 4 章 流程控制-if-else,Switch,For循环(循环守卫&#xff0c;循环步长&#xff0c;倒叙打印)&#xff0c;While循环&#xff0c;多重循环 4.1 分支控制 if-else 让程序有选择的的执行&#xff0c;分支控制有三种&#xff1a;单分支、双分支、多分支 4.1.1 单分支 1)基本语法…

Leetcode-每日一题1234. 替换子串得到平衡字符串(滑动窗口 + 哈希表)

题目链接&#xff1a;https://leetcode.cn/problems/replace-the-substring-for-balanced-string/description/ 思路 题目意思 这题意思是一个只含有[Q, W, E, R] 四个字符的字符串s且长度一定是 4的倍数&#xff0c; 需要你通过替换子串&#xff0c;使他变成一个「平衡字符…

【C++设计模式】学习笔记(6):Bridge 桥模式

目录 简介动机(Motivation)模式定义结构(Structure)要点总结笔记结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金…

【C++设计模式】学习笔记(2):模式分类与模版方法 Template Method

目录 简介模式分类GOF-23 模式分类从封装变化角度对模式分类重构获得模式 Refactoring to Patterns重构关键技法“组件协作”模式Template Method 模式动机(Motivation)结构化软件设计流程面向对象软件设计流程早绑定与晚绑定模式的定义结构(Structure)要点总结结语简介 He…

Unity(一)--通过简单例子了解属性、脚本等基础操作

目录新建工程保存视图创建游戏对象调整场景视图角度添加物理运动组件更换材质更改颜色添加脚本点击向上跳跃变色旋转UGUI的使用修改文字内容新建工程 ps:最好不使用中文文件路径及名称&#xff0c;可能会报错。 保存视图 进工程后&#xff0c;此时只有空文件夹&#xff0c;可…

《痞子衡嵌入式半月刊》 第 71 期

痞子衡嵌入式半月刊&#xff1a; 第 71 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻&#xff0c;农历年分二十四节气&#xff0c;希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly)&#xff0c;欢迎提交 issue&#xff0c…

新手做跨境电商,选对平台很重要

据数据统计&#xff0c;我国跨境电商早在2013年&#xff0c;发展态势就十分迅猛&#xff0c;交易规模达到2.7万亿元&#xff1b;而到2015年&#xff0c;仅仅两年时间&#xff0c;交易规模就突破到5万亿元&#xff1b;再过两年&#xff0c;2017年跨境电商交易规模增长到7.6万亿元…

okcc呼叫中心怎样搭建?

随着企业对服务逐渐重视&#xff0c;开始意识到呼叫中心作为客户服务水平和沟通效率的基本平台&#xff0c;并且有越来越多的企业开始利用呼叫中心外呼方式销售自己的产品。在此背景下&#xff0c;很多企业&#xff0c;无论规模大小&#xff0c;都有建立自己呼叫中心的想法。 …

docker的资源控制管理——Cgroups

引言&#xff1a;docker 使用cgrqup控制资源&#xff0c;K8S 里面也有limit&#xff08;使用上限&#xff09;docker通过cgroup来控制容器使用的资源配额&#xff0c;包括CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配额和使用量控制。Cgroup 是 Control group 的…

Jboss EAP 7.4.8配置jacoco 端口无法开启 启动报错

项目场景&#xff1a; servers: Jboss EAP 7.4.8 JDK: JDK17 jacoco: 测试jacoco-0.7.9 jacoco-0.8.5 jacoco-0.8.8 问题描述 问题1: Jboss 启动 VM arguments追加, 如下启动参数&#xff1a; -javaagent:F:\CoverageReport\jacoco-0.7.9\lib\jacocoagent.jarincludes*,o…

别具一格,原创唯美浪漫情人节表白专辑,(复制就可用)(html5,css3,svg)表白爱心代码(1)

别具一格&#xff0c;原创唯美浪漫情人节表白专辑&#xff0c; (复制就可用)&#xff08;html5,css3,svg)表白爱心代码(1) 一、 前言 回眸之间&#xff0c;丰盈了岁月&#xff0c;涟漪了思绪&#xff0c;轻轻落笔&#xff0c;不写伤痕&#xff0c;不写仇怨&#xff0c;只写岁月…

反光板导航SLAM(三)反光柱导航开发与实验

在上一章中简单了解了VEnus算法对于反光柱导航的基本思路。其主要分为了高反点提取、高反点聚类查找中心、高反点与已知反光柱位姿匹配以及调用ceres库进行位姿优化等步骤。然后在这个算法的基础上&#xff0c;再进行一定的开发达到一个比较稳定且可视化的版本。 使用&#xff…

json对象和formData相互转换

前言 大家都知道&#xff0c;前端在和后台进行交互联调时&#xff0c;肯定避免不了要传递参数&#xff0c;一般情况下&#xff0c;params 在 get 请求中使用&#xff0c;而 post 请求下&#xff0c;我们有两种常见的传参方式&#xff1a; JSON 对象格式和 formData 格式&#x…

《MySQL学习》 索引 下 覆盖索引,MRR,联合索引

一. 覆盖索引 有一张表T1&#xff0c;它的建表语句如下 mysql> create table T1 ( ID int primary key, k int NOT NULL DEFAULT 0, s varchar(16) NOT NULL DEFAULT , index k(k)) engineInnoDB;insert into T1 values(100,1, aa),(200,2,bb),(300,3,cc),(500,5,ee),(60…

为什么子进程要继承处理器亲缘性?

请先考虑一个典型的程序为什么需要启动一个子进程。(当然资源管理器不算一个典型的程序) 这是因为手头的任务被分解为子任务&#xff0c;无论出于何种原因&#xff0c;这些子任务都被放入子流程中。例如&#xff0c;在实现多次遍历型编译器/链接器时&#xff0c;其中每次遍历都…

虹科新品 | 万兆车载以太网媒体转换器-实现更加快捷、高效的连接

多千兆车载以太网 媒体转换器 —— Technica Engineering —— 2.5/5/10GBASE-T1多千兆 Media Converter Media Converter 是一种硬件设备&#xff0c;可在汽车以太网连接&#xff08;100BASE-T1 或 1000BASE-T1&#xff09;和任何具有带 RJ-45 连接器的标准以太网网络接口卡 …