按住鼠标左键画框,裁切画面并局部放大,可以用来生成ROI
1、在QtDesigner中创建ui文件,命名为crop.ui:
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())