PySide(PyQt)实现鼠标画框局部放大

按住鼠标左键画框,裁切画面并局部放大,可以用来生成ROI

1、在QtDesigner中创建ui文件,命名为crop.ui:

2、自定义脚本ImageLabel.py

python 复制代码
from PySide6.QtCore import Qt, QRect, Signal, QPoint
from PySide6.QtGui import QPixmap, QPainter, QPen, QColor
from PySide6.QtWidgets import QLabel


class ImageLabel(QLabel):
    src_info = Signal(str)
    crop_info = Signal(str)
    show_info = Signal(str)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.scale = 1.0  # 显示比例,其含义为当前的显示窗口的每个像素代表原始图几个像素
        self.src_width = 1
        self.src_height = 1
        self.showing_pixmap = QPixmap()  # 当前显示的图像内容(从原图原比例裁切而来)
        self.scaled_image = QPixmap()  # 当前显示的图像内容(经过缩放适应窗口的)
        self.scaled_image_x0 = 0  # 显示内容的x起点
        self.scaled_image_y0 = 0  # 显示内容的y起点
        self.erase = False  # 擦除已有的方框

        self.start_pos = QPoint()  # 鼠标起始点
        self.end_pos = QPoint()  # 鼠标结束点

        self.show_rect = QRect(0, 0, 1, 1)  # 显示窗口维度的方框
        self.crop_rect = QRect(0, 0, 1, 1)  # 实际像素维度的方框

    # 重新定义鼠标按下事件
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.start_pos = event.position().toPoint()
            self.erase = False

    # 重新定义鼠标移动事件
    def mouseMoveEvent(self, event):
        if self.start_pos:
            self.end_pos = event.position().toPoint()
            self.show_rect = QRect(self.start_pos, self.end_pos)
            self.update()

    # 重新定义鼠标松开事件
    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.end_pos = event.position().toPoint()
            self.show_rect = QRect(self.start_pos, self.end_pos)
            self.erase = True  # 这一行决定了松开鼠标后方框是否擦除
            self.normalize_rect()  # 将方框参数转成正数
            self.update()

            self.crop_image(self.showing_pixmap, self.show_rect)  # 裁切图像

    # 重新定义绘画事件
    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self)
        if not self.erase:  # 不擦除
            painter.setPen(QPen(QColor(255, 0, 0, 255), 1, Qt.SolidLine))

        else:  # 擦除(用透明色画一遍)
            painter.setPen(QPen(QColor(0, 0, 0, 0), 2, Qt.SolidLine))

        painter.drawRect(self.show_rect)

    # 将得到的方框的长宽尺寸转换成正数
    def normalize_rect(self):
        x = self.show_rect.x()
        y = self.show_rect.y()
        width = self.show_rect.width()
        height = self.show_rect.height()

        if width < 0:
            x += width
            width = abs(width)
        if height < 0:
            y += height
            height = abs(height)

        # 最小方框
        self.show_rect = QRect(x, y, max(width, 3), max(height, 3))

        # 裁切并显示画面

    def crop_image(self, real_pixmap, showing_rect):  # 注意:pixmap是需要显示的真实像素维度的图像,rect是鼠标在屏幕显示维度上的方框
        # 将需要显示的图像进行满幅不变形缩放,并存储为显示图像
        self.scaled_image = real_pixmap.scaled(self.width(), self.height(), Qt.KeepAspectRatio,
                                               Qt.SmoothTransformation)
        # 显示倍率,含义为显示的窗口中,每像素相当于几个实际的像素
        self.scale = float(real_pixmap.width() / self.scaled_image.width())
        # 坐标变换,当在像素窗口中画方框,映射到显示图像中的相对坐标
        self.scaled_image_x0 = (self.width() - self.scaled_image.width()) / 2
        self.scaled_image_y0 = (self.height() - self.scaled_image.height()) / 2

        # 用以在实际像素的原图中裁切的方框
        self.crop_rect = QRect((showing_rect.x() - self.scaled_image_x0) * self.scale,
                               (showing_rect.y() - self.scaled_image_y0) * self.scale,
                               (showing_rect.width()) * self.scale,
                               (showing_rect.height()) * self.scale
                               )
        # 裁切到的用以显示的图像
        self.showing_pixmap = self.showing_pixmap.copy(self.crop_rect)
        # 再次缩放裁切到的的图像
        self.scaled_image = self.showing_pixmap.scaled(self.width(), self.height(), Qt.KeepAspectRatio,
                                                       Qt.SmoothTransformation)  # 将需要显示的图像进行满幅不变形缩放,并存储为显示图像
        self.setPixmap(self.scaled_image)  # 显示图像
        self.setAlignment(Qt.AlignCenter)  # 居中显示

        # 再次计算显示倍率
        self.scale = self.showing_pixmap.height() / self.scaled_image.height()
        self.crop_info.emit(f'裁切到的尺寸:{str(self.showing_pixmap.width())} * {str(self.showing_pixmap.height())}')
        self.show_info.emit(
            f'显示像素:{str(self.scaled_image.width())} * {str(self.scaled_image.height())},显示倍率:{self.scale}')

    # 初始化,设置原图像
    def set_src(self, pixmap):
        self.src_width = pixmap.width()
        self.src_height = pixmap.height()
        self.showing_pixmap = pixmap.copy()
        # 满幅裁切
        self.crop_rect = QRect(0, 0, self.width(), self.height())
        self.crop_image(self.showing_pixmap, self.crop_rect)
        self.src_info.emit(f'原图尺寸:{str(self.src_width)} * {str(self.src_height)}')

3、在QtDesigner中添加"提升为"的自定义脚本,并且将显示用的窗口提升:

4、用pyui工具将ui文件生成py文件,并编写主程序:

python 复制代码
# encoding: utf-8

from PySide6.QtCore import QObject
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtGui import QPixmap, Qt
import sys

import crop_rc  # 导入需要显示的画面


# 定义需要显示的画面类
class MainWindow(QMainWindow, crop_rc.Ui_MainWindow):
    def __init__(self):
        super().__init__()


# 系统的初始化
def start_todo():
    pass


# #############################主程序##################################
if __name__ == '__main__':
    app = QApplication(sys.argv)

    # #######################项目级别的定义#############################
    class UI(QObject):  # 将项目定义为QObject,用来管理项目级别的信号和变量
        # ###########__init__###############
        def __init__(self):
            super().__init__()


    # ########################本项目的实例化############################
    ui = UI()  # 项目实例化

    # ########################实例化画面###############################
    window1 = MainWindow()  # 画面实例化

    window1.show()  # 显示画面
    window1.setupUi(window1)  # 画面初始化

    # ###########################信号的连接和槽函数#####################
    # "保存文件"按钮点击的槽函数
    def window1_btn_save_clicked():
        pass

    # "保存文件"按钮点击的连接
    window1.btn_save.clicked.connect(window1_btn_save_clicked)

    # "恢复原图"按钮点击的槽函数
    def window1_btn_refresh_clicked():
        window1.label_show.set_src(ui.src_img)    # 设置并显示原始图像

    # "恢复原图"按钮点击的连接
    window1.btn_refresh.clicked.connect(window1_btn_refresh_clicked)

    # 几个信息显示标签的文字更新槽和连接
    def src_info_changed(txt):
        window1.src_info.setText(txt)

    def crop_info_changed(txt):
        window1.crop_info.setText(txt)

    def show_info_changed(txt):
        window1.show_info.setText(txt)

    window1.label_show.src_info.connect(src_info_changed)
    window1.label_show.crop_info.connect(crop_info_changed)
    window1.label_show.show_info.connect(show_info_changed)

    # ###########################系统的初始化#######################
    start_todo()
    ui.src_img = QPixmap('../PYS/9.png')  # 读取和定义原图像文件
    window1.label_show.set_src(ui.src_img)  # 设置并显示原图像

    sys.exit(app.exec())
相关推荐
我是华为OD~HR~栗栗呀4 分钟前
华为OD-Java面经-21届考研
java·c++·后端·python·华为od·华为·面试
千禧皓月12 分钟前
【Diffusion Model】发展历程
人工智能·深度学习·diffusion model·1024程序员节
猫头虎29 分钟前
大模型训练中的关键技术与挑战:数据采集、微调与资源优化
人工智能·爬虫·数据挖掘·数据分析·网络爬虫·aigc·1024程序员节
刺客-Andy29 分钟前
Python 第二十节 正则表达式使用详解及注意事项
python·mysql·正则表达式
yanxing.D1 小时前
penCV轻松入门_面向python(第七章 图像平滑处理)
图像处理·人工智能·opencv·计算机视觉
新子y1 小时前
【小白笔记】「while」在程序语言中的角色
笔记·python
骥龙1 小时前
1.1、开篇:AI如何重塑网络安全攻防格局?
人工智能·安全·web安全
微学AI1 小时前
国产数据库替代MongoDB的技术实践过程:金仓多模数据库在电子证照系统中的深度应用
数据库·人工智能·1024程序员节
java1234_小锋1 小时前
[免费]基于Python的YOLO深度学习垃圾分类目标检测系统【论文+源码】
python·深度学习·yolo·垃圾分类·垃圾分类检测
gddkxc2 小时前
AI驱动的客户管理:悟空AI CRM的核心功能与优势
人工智能