当前位置: 首页 > news >正文

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

欢迎关注『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项目实战(7)项目案例03:鼠标框选
OpenCV-PyQT项目实战(8)项目案例04:鼠标定位
OpenCV-PyQT项目实战(9)项目案例04:视频播放
OpenCV-PyQT项目实战(10)项目案例06:键盘事件与视频抓拍
OpenCV-PyQT项目实战(11)项目案例07:摄像头操作与拍摄视频

文章目录

  • 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-03if __name__ == '__main__':app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口myWin.show()  # 在桌面显示控件 myWinsys.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_pushButtonself.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-08import 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_pushButtonself.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)returndef 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)  # 刷新显示returndef click_pushButton_2(self):  # 点击 pushButton_2 触发print("pushButton_2")self.img2 = cv.cvtColor(self.img1, cv.COLOR_BGR2GRAY)  # 图片格式转换:BGR -> Grayself.refreshShow(self.img2, self.label_2)  # 刷新显示returndef 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<HinHin = min(Hin, 255)  # Hin, 白场阈值, Sin<Hin<=255Mt = min(max(Mt, 0.01), 9.99)  # Mt, 灰场调节值, 0.01~9.99Sout = 0#min(max(Sout, 0), Hout - 2)  # Sout, 输出黑场阈值, 0<=Sout<HoutHout = 255#min(Hout, 255)  # Hout, 输出白场阈值, Sout<Hout<=255print("Sin={},Hin={},Mt={}".format(Sin, Hin, Mt))difIn = Hin - SindifOut = Hout - Souttable = 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)  # 刷新显示returndef refreshShow(self, img, label):print(img.shape, label)qImg = self.cvToQImage(img)  # OpenCV 转为 PyQt 图像格式label.setPixmap((QPixmap.fromImage(qImg)))  # 加载 PyQt 图像returndef 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 imgdef saveSlot(self):  # 保存图像文件# 选择存储文件 dialogfileName, 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)returndef cvToQImage(self, image):# 8-bits unsigned, NO. OF CHANNELS=1if 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 MatqImg = 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 MatqImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)return qImgelse: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 转换为 QImageshape = (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 imagedef trigger_actHelp(self):  # 动作 actHelp 触发QMessageBox.about(self, "About","""数字图像处理工具箱 v1.0\nCopyright YouCans, XUPT 2023""")returnif __name__ == '__main__':app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口myWin.show()  # 在桌面显示控件 myWinsys.exit(app.exec_())  # 结束进程,退出程序

运行结果:

在这里插入图片描述


【本节完】


版权声明:

Copyright 2023 youcans, XUPT

Crated:2023-2-8


相关文章:

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

欢迎关注『OpenCV-PyQT项目实战 Youcans』系列&#xff0c;持续更新中 OpenCV-PyQT项目实战&#xff08;1&#xff09;安装与环境配置 OpenCV-PyQT项目实战&#xff08;2&#xff09;QtDesigner 和 PyUIC 快速入门 OpenCV-PyQT项目实战&#xff08;3&#xff09;信号与槽机制 …...

3 决策树及Python实现

1 主要思想 1.1 数据 1.2 训练和使用模型 训练&#xff1a;建立模型&#xff08;树&#xff09; 测试&#xff1a;使用模型&#xff08;树&#xff09; Weka演示ID3&#xff08;终端用户模式&#xff09; 双击weka.jar选择Explorer载入weather.arff选择trees–>ID3构建树…...

小程序和Vue+uniapp+unicloud培训课件

文章目录**一、什么是小程序****1.1** **小程序简介****1.2** **小程序的特点****1.3** **小程序的开发流程**个人小程序和企业小程序的区别1.4 小程序代码构成1.4.1 JSON 配置1.4.2 WXML 模板**数据绑定**逻辑语法条件逻辑列表渲染模板引用共同属性1.4.3 WXSS 样式1.4.4 JS 逻…...

C语言--指针进阶2

目录前言函数指针函数指针数组指向函数指针数组的指针回调函数前言 本篇文章我们将继续学习指针进阶的有关内容 函数指针 我们依然用类比的方法1来理解函数指针这一全新的概念&#xff0c;如图1 我们用一段代码来验证一下&#xff1a; int Add(int x, int y) {return xy;…...

【步进电机和 Arduino】

【步进电机和 Arduino】 前言1. 什么是步进电机及其工作原理?1.1 步进电机结构1.2 绕线方式1.3 通电方式2. 如何使用Arduino和A17步进驱动器控制NEMA4988步进电机2.1 A4988 和 Arduino 连接2.2 测量AB相2.3 A4988 限流3. 步进电机和 Arduino3.1 示例代码 13.2 示例代码 24. 使…...

【面试一:|和||、和区别】

相同点&#xff1a; ||和&&都是逻辑运算符&#xff0c;而|和&是位运算符。位运算符的优先级要比逻辑运算符的优先级高。 &和&&的区别 &和&&都可以用作逻辑与的运算符&#xff0c;表示逻辑与&#xff08;and&#xff09;&#xff0c;当运…...

【一天一门编程语言】使用汇编语言实现斐波那契数列

文章目录使用汇编语言实现斐波那契数列一、什么是斐波那契数列二、如何用汇编语言实现斐波那契数列一、汇编语言概念1.1 什么是汇编语言1.2 汇编语言的特点二、汇编语言指令2.1 简单指令2.2 复杂指令汇编语言程序结构代码实例指令集常用指令指令代码实例使用汇编语言实现斐波那…...

RabbitMQ实现死信队列

目录死信队列是什么怎样实现一个死信队列说明实现过程导入依赖添加配置编写mq配置类添加业务队列的消费者添加死信队列的消费者添加消息发送者添加消息测试类测试死信队列的应用场景总结死信队列是什么 “死信”是RabbitMQ中的一种消息机制&#xff0c;当你在消费消息时&#…...

【Linux】安装Tomcat教程

目录 1.上传安装包 2.解压安装包 3.启动Tomcat 4.查看启动日志 5.查看进程 6.开放端口 7.停止Tomcat 1.上传安装包 使用FinalShell自带的上传工具将Tomcat的二进制发布包上传到Linux(与前面上传JDK安装包步骤 一致)。 2.解压安装包 将上传上来的安装包解压到指定目录…...

学习笔记之Vuex(五)

Vuex&#xff08;五&#xff09;Vuex一、什么是Vuex二、Vuex工作原理三、搭建Vuex环境四、求和案例分析4.1 求和案例——vue实现4.2 求和案例——vuex实现&#xff08;五&#xff09;Vuex 一、什么是Vuex 1.概念 在Vue中实现集中式状态&#xff08;数据&#xff09;管理的一…...

SSM知识快速复习

SSM知识快速复习SpringIOCDIIOC容器在Spring中的实现常用注解Autowired注解的原理AOP相关术语作用动态代理实现原理事务Transactional事务属性&#xff1a;只读事务属性&#xff1a;超时事务属性&#xff1a;回滚策略事务属性&#xff1a;事务隔离级别事务属性&#xff1a;事务…...

【Linux】安装MySQL

目录 1.检测当前系统是否安装过MySQL相关数据库 2. 卸载现有的MySQL数据库 3.上传解压 4.顺序安装rpm包 5.启动MySQL 6.查看临时密码 7.登录MySQL 8.开放端口 1.检测当前系统是否安装过MySQL相关数据库 需要通过rpm相关指令&#xff0c;来查询当前系统中是否存在已安…...

【深度学习】手把手教你开发自己的深度学习模板

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言1数据相关1.1 数据初探1.2.数据处理1.3 数据变形2 定义网络&#xff0c;优化函数3. 训练前言 入坑2年后&#xff0c;重新梳理之前的知识&#xff0c;发现其实需…...

一个诡异的 Pulsar InterruptedException 异常

背景 今天收到业务团队反馈线上有个应用往 Pulsar 中发送消息失败了&#xff0c;经过日志查看得知是发送消息时候抛出了 java.lang.InterruptedException 异常。 和业务沟通后得知是在一个 gRPC 接口中触发的消息发送&#xff0c;大约持续了半个小时的异常后便恢复正常了&…...

Java岗面试题--Java并发(volatile 专题)

目录1. 面试题一&#xff1a;谈谈 volatile 的使用及其原理补充&#xff1a;内存屏障volatile 的原理2. 面试题二&#xff1a;volatile 为什么不能保证原子性3. 面试题三&#xff1a;volatile 的内存语义4. 面试题四&#xff1a;volatile 的实现机制5. 面试题五&#xff1a;vol…...

Java---打家劫舍ⅠⅡ

目录 打家劫舍Ⅰ 题目分析 代码一 代码二 打家劫舍Ⅱ 打家劫舍Ⅰ 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被…...

MySQL Lesson4

1&#xff1a;关于查询结果集的去重&#xff08;distinct&#xff09; select distinct job from emp; **distinct只能出现在所有字段的最前面。所表示的含有是所有的结果联合起来去重。 select distinct deptno,job from emp order by deptno; select count(distinct job)from…...

浅谈权限获取方法之文件上传

概述 文件上传漏洞是发生在有上传功能的应用中&#xff0c;如果应用程序对用户的上传文件没有控制或者存在缺陷&#xff0c;攻击者可以利用应用上传功能存在的缺陷&#xff0c;上传木马、病毒等有危害的文件到服务器上面&#xff0c;控制服务器。 漏洞成因及危害 文件上传漏…...

资产设备防拆标签安全防护和资产定位解决方案

随着社会经济的发展和高新技术的日新月异&#xff0c;对各方面的安全要求也在不断地提高&#xff0c;以物联网安防、入侵报警和出入口控制、应急系统等为主的安全防范系统日益成为各类文物场所智能化弱电工程不可或缺的组成部分&#xff0c;是重点资产管理场所内加强管理和安全…...

企业电子招标采购源码之电子招标投标全流程!

随着各级政府部门的大力推进&#xff0c;以及国内互联网的建设&#xff0c;电子招投标已经逐渐成为国内主流的招标投标方式&#xff0c;但是依然有很多人对电子招投标的流程不够了解&#xff0c;在具体操作上存在困难。虽然各个交易平台的招标投标在线操作会略有不同&#xff0…...

【考研408】计算机网络笔记

文章目录计算机网络体系结构计算机网络概述计算机网络的组成计算机网络的功能计算机网络的分类计算机网络的性能指标课后习题计算机网络体系结构与参考模型计算机网络协议、接口、服务的概念ISO/OSI参考模型和TCP/IP模型课后习题物理层通信基础基本概念奈奎斯特定理与香农定理编…...

[C++]继承

&#x1f941;作者&#xff1a; 华丞臧 &#x1f4d5;​​​​专栏&#xff1a;【C】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449;LeetCode 文章目录一、继承…...

优化知识管理方法丨整理零碎信息,提高数据价值

信息流时代&#xff0c;知识成集合倍数增长&#xff0c;看似我们学习了很多知识&#xff0c;但知识零碎无系统&#xff0c;知识之间缺乏联系&#xff0c;没有深度&#xff0c;所以虽然你很努力&#xff0c;但你发现自己的能力增长特别缓慢&#xff0c;你需要整理知识将零散的知…...

Windows操作系统的体系结构、运行环境和运行状态

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Windows这个我们熟悉的不能再熟悉的系统。说Windows操作系统的运行环境和运行状态&#xff0c;首先要介绍一下Windows操作系统的体系结构&#xff0c;然后再要说到最重要的两个概念:核…...

【工作笔记】Http响应头过长

起因 突然有测试小伙伴反馈进公司官网主页会白屏&#xff0c;但只是个例不是普遍现象 查监控发现没监控到异常问题 查了很久&#xff08;这个很久单指对于线上问题来说&#xff09;才定位是请求的异常&#xff0c;因为这套系统的异常用的是 ExceptionHandler&#xff0c;这也导…...

hive建分区表,分桶表,内部表,外部表

hive建分区表&#xff0c;分桶表&#xff0c;内部表&#xff0c;外部表 一、概念介绍 Hive是基于Hadoop的一个工具&#xff0c;用来帮助不熟悉 MapReduce的人使用SQL对存储在Hadoop中的大规模数据进行数据提取、转化、加载。Hive数据仓库工具能将结构化的数据文件映射为一张数…...

【分享】灌溉制度设计小程序VB源代码

说明 根据作物需水特性和当地气候、土壤、农业技术及灌水技术等因素制定的灌水方案。主要内容包括灌水次数、灌水时间、灌水定额和灌溉定额。灌溉制度是规划、设计灌溉工程和进行灌区运行管理的基本资料&#xff0c;是编制和执行灌区用水计划的重要依据。 1—计划湿润土层允…...

PR9268/300-000库存现货振动传感器 雄霸工控

PR9268/300-000库存现货振动传感器 雄霸工控PR9268/300-000库存现货振动传感器 雄霸工控SDM010PR9670/110-100PR9670/010-100PR9670/003-000PR9670/002-000PR9670/001-000PR9670/000-000PR9600/014-000PR9600/011-000PR9376/010-021PR9376/010-011PR9376/010-011PR9376/010-001…...

浅谈模型评估选择及重要性

作者&#xff1a;王同学 来源&#xff1a;投稿 编辑&#xff1a;学姐 模型评估作为机器学习领域一项不可分割的部分&#xff0c;却常常被大家忽略&#xff0c;其实在机器学习领域中重要的不仅仅是模型结构和参数量&#xff0c;对模型的评估也是至关重要的&#xff0c;只有选择那…...

多线程的初识和创建

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;知不足而奋进&#xff0c;望远山而前行。 目 录&#x1f4a4;一. 认识线程&#xff08;Thread&#xff09;&#x1f34e;1. 线程的引入&#x1f34f;2. 线程…...

开锁公司网站模板/广东seo网站推广

之前七娃&#xff0c;整理过用css实现鼠标左右键禁用&#xff1a;静态页面js防止抽离 今天新增一个通过css设置body的样式&#xff0c;将鼠标左键禁用了&#xff0c;禁止用户选择&#xff01; body{-webkit-touch-callout: none;-webkit-user-select: none;-khtml-user-selec…...

做早餐的网站/seo关键词优化推广

1、查看物理CPU的个数[rootMysqlCluster01 ~]# cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l12、查看逻辑CPU的个数[rootMysqlCluster01 ~]# cat /proc/cpuinfo |grep "processor"|wc -l43、查看CPU是几核(即&#xff0c;核心数)[rootMysqlClu…...

霸州网站制作/安卓优化大师老版本下载

今天我们来学习&#xff1a;码云&#xff08;Gitee&#xff09;授权第三方登录&#xff0c;相比之前 支付宝登录、腾讯QQ登录 以及 新浪微博登录 来说&#xff0c;相对于比较简单 一、准备工作 1、登录 码云官网 官网地址&#xff1a;https://gitee.com/注册、登录我们的账号…...

厦门市翔安建设局网站/网站统计分析平台

DNS 配置篇二一、子域配置1、基本概念子域的作用是在本地DNS下再划分一个小的&#xff08;子&#xff09;DNS。作用的方便集中管理&#xff0c;不过问题是要配置转发。父DNS可以知道解析子DNS&#xff0c;子DNS 则只可以解析自己本地记录&#xff0c;不能解析父DNS。正向子域授…...

电商网站有哪些类型/网站seo排名公司

在攻克了围棋以后&#xff0c;人工智能研究者们似乎不约而同地把电子竞技游戏作为了下一个练兵场。雷锋网(公众号&#xff1a;雷锋网)上周报道&#xff0c;在Dota2国际邀请赛TI7上&#xff0c;OpenAI率先展示了自己的成果&#xff0c;在西雅图让AI在1v1比赛中击败了职业选手&am…...

wordpress文章采集插件/广告投放网站平台

在使用word2vec对文本进行分析时能产生一个bin文件&#xff0c;可以用ANSJ调用该文件得到相近词&#xff0c;等同于替换 ./distance vectors.bin命令。 代码如下&#xff1a; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.FileInputSt…...