PySide(PyQT)使用场景(QGraphicsScene)进行动态标注的一个demo

news2025/2/19 16:00:24

 用以标注图像的一个基本框架demo

import sys
from PySide6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QMainWindow, QLabel, QGraphicsPixmapItem
from PySide6.QtGui import QPixmap, QPainter, QTransform
from PySide6.QtCore import Qt, QPointF, Slot, Signal


class ImageViewer(QGraphicsView):
    mouse_pos = Signal(int, int)  # 原图像素坐标信号
    def __init__(self, image_path, parent=None):
        super(ImageViewer, self).__init__(parent)

        # 场景的初始化
        def init_scene():
            # 设置场景
            self.scene = QGraphicsScene(self)
            self.setScene(self.scene)
            # 设置渲染提示
            self.setRenderHint(QPainter.Antialiasing)   # 开启抗锯齿
            self.setRenderHint(QPainter.SmoothPixmapTransform)  # 开启平滑缩放
            # 设置缩放锚点
            self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)  # 转换时以鼠标为中心
            self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)  # 缩放时以鼠标为中心
            # 创建基础的显示内容
            pixmap = QPixmap(image_path)
            self.pixmap_item = QGraphicsPixmapItem(pixmap)
            self.scene.addItem(self.pixmap_item)
            # 标记点的数量
            self.mark_count = 0
            # 遮罩的数量
            self.mask_count = 0
        init_scene()
        self.mouse_pos.connect(self.update_mouse_position)


    def add_mark(self, pixmap, pos=(0, 0), scale=1.0):
        """
        添加标记到场景
        :param pixmap: 图像数据
        :param pos:  坐标
        :param scale: 缩放比例
        :return:  None
        """
        # 加载图像
        pixmap_item = QGraphicsPixmapItem(pixmap)  # 创建图形项
        mark_x = pos[0] - pixmap.width() / 2 * scale
        mark_y = pos[1] - pixmap.height() / 2 * scale
        pixmap_item.setPos(mark_x, mark_y)  # 设置位置
        if scale != 1.0:  # 设置比例
            pixmap_item.setScale(scale)
        self.scene.addItem(pixmap_item)
        self.mark_count += 1

    def add_mask(self):
        """
        添加遮罩
        :return:
        """
        pass

    def remove_mark(self):
        """
        删除一个标记
        :return: None
        """
        if self.mark_count > 0:
            self.scene.removeItem(self.scene.items()[0])
            self.mark_count -= 1

    def remove_mask(self):
        """
        删除一个遮罩
        :return: None
        """
        if self.mask_count > 0:
            self.scene.removeItem(self.scene.items()[0])
            self.mask_count -= 1

    def remove_all_marks(self):
        """
        删除所有标记
        :return:
        """
        while self.mark_count > 0:
            self.scene.removeItem(self.scene.items()[0])
            self.mark_count -= 1


    # # 使用变换矩阵(如果需要)
    def transform(self, t):
        # self.transform = t
        # self.pixmap_item.setTransform(self.transform)
        pass

    # 处理滚轮事件以实现缩放
    def wheelEvent(self, event):
        factor = 1.001 ** event.angleDelta().y()  # 滚轮每滚动一格,缩放比例变化
        self.scale(factor, factor)

    # 处理鼠标单击事件以显示原图像素坐标
    def mousePressEvent(self, event):
        super(ImageViewer, self).mousePressEvent(event)
        scene_pos = self.mapToScene(event.position().toPoint())  # 记录当前鼠标在场景中的位置
        pixmap_rect = self.pixmap_item.boundingRect()   # 记录图像的边界矩形
        # 如果鼠标在图像边界矩形内,则显示像素坐标
        if pixmap_rect.contains(scene_pos):
            pos = self.pixmap_item.mapFromScene(scene_pos)  # 计算图像坐标
            x, y = pos.x(), pos.y()
            print(f"鼠标在图像内,坐标为: ({x}, {y})")

            if event.button() == Qt.LeftButton:  # 左键
                if event.modifiers() == Qt.ShiftModifier:  # Shift 键
                    self.remove_mark()    # 删除标记
                else:
                    self.add_mark(pixmap_mark, (x, y), 0.6)  # 添加标记

            elif event.button() == Qt.RightButton:   # 右键
                if event.modifiers() == Qt.ShiftModifier:   # Shift 键
                    self.remove_all_marks()   # 删除所有标记
                else:
                    self.add_mark(pixmap_unmark, (x, y), 0.6)   # 添加标记

    @Slot()
    def update_mouse_position(self, x, y):
        pass
        # 更新状态栏显示
        # self.status_bar.showMessage(f"原图像素坐标: ({x}, {y})")

class MainWindow(QMainWindow):
    def __init__(self, image_path):
        super(MainWindow, self).__init__()
        self.setGeometry(100, 100, 800, 600)

        # 创建状态栏
        self.status_bar = self.statusBar()

        # 创建 ImageViewer 实例
        self.image_viewer = ImageViewer(image_path, self)
        self.setCentralWidget(self.image_viewer)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    # 生成标记图像
    pixmap_mark = QPixmap("mark.png")   # 标记图像(前景)
    pixmap_unmark = QPixmap("un_mark.png")   # 未标记图像(背景)

    image_path = "IMG_PP.jpg"      # 基础图像路径

    # 创建主窗口
    window = MainWindow(image_path)
    window.show()

    sys.exit(app.exec())

截图:

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

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

相关文章

w206基于Spring Boot的农商对接系统的设计与实现

🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…

C++类和对象进阶:拷贝构造函数深度详解

拷贝构造函数 拷贝构造函数前言引入拷贝构造函数特征拷贝构造函数建议参数加上const 拷贝构造函数参数传值会引发无穷递归的解释内置类型传参拷贝自定义类型传参拷贝详细解释 编译器生成的默认拷贝构造函数默认构造函数做了什么?深拷贝与浅拷贝简单实现一个深拷贝。…

像取快递一样取文件?

看到一个很有意思的项目,像我们做软件分享的感觉会有用,就是现在服务器费用太贵了,如果自建的话感觉不是很值得。 FileCodeBox FileCodeBox 是一个轻量级的文件分享系统,它基于匿名口令分享文本和文件,无需注册登录&…

DeepSeek 助力 Vue 开发:打造丝滑的返回顶部按钮(Back to Top)

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…

【前端开发学习笔记15】Vue_8

手动添加Pinia到Vue项目: 在实际开发中,Pinia配置可在项目创建时自动添加。初次学习从零开始: 1. 用Vite创建空的Vue3项目,命令为npm create vuelatest。 2. 按官方文档将pinia安装到项目中。 import { createApp } from vue im…

通过docker启用rabbitmq插件

创建文件,docker-compose.yml services:rabbitmq:image: rabbitmq:4.0-managementports:- "5672:5672"- "15672:15672"volumes:- ./data/rabbitmq/data:/var/lib/rabbitmq # 持久化数据- ./data/rabbitmq/plugins/rabbitmq_delayed_message_ex…

对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 与基于 openEuler 构建 LVS-DR 群集

一、 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 NAT 模式 部署简单:NAT 模式下,所有的服务器节点只需要连接到同一个局域网内,通过负载均衡器进行网络地址转换,就可以实现负载均衡功能。不需要对…

C++17 中 std::lcm:从入门到精通

文章目录 一、引言二、std::lcm 的基本概念三、入门示例四、计算多个整数的最小公倍数五、std::lcm 的实现原理六、在实际项目中的应用七、注意事项八、总结 一、引言 在 C 编程中,处理数学运算时,计算最小公倍数(Least Common Multiple&…

html 点击弹出视频弹窗

一、效果: 点击视频按钮后,弹出弹窗 播放视频 二、代码 <div class="index_change_video" data-video-src="</

代码随想录算法【Day44】

Day44 1143.最长公共子序列 class Solution { public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size() 1, vector<int>(text2.size() 1, 0));for (int i 1; i < text1.size(); i) {for (int j 1; …

项目总结:java agent的使用

测试团队会做java agent的事&#xff0c;实现测试模拟&#xff0c;各种数据采集等等工作&#xff0c;而这些不需要开发改代码来做到&#xff0c;只需要挂载下agent。 目录 javaagent认识和例子代码例子&#xff1a;java.lang.instrument自定义实现一个javaagentagent jar测试 回…

如何借助NoETL指标平台实现数据分析、决策的提效?

通常&#xff0c;企业通过明确分析目标、定位所需分析的数据&#xff0c;再通过多渠道汇集销售数据、客户反馈、市场调研等信息&#xff0c;经过数据清洗、缺失值处理及格式标准化等手段&#xff0c;运用描述性统计、回归分析、聚类分析及关联规则挖掘等多样分析方法&#xff0…

大模型语言简介

大模型语言能做什么 信息提取 将长段文字中的信息抽取出来并且以结构化的方式输出。相比起传统NLP的方式&#xff0c;大模型在泛化能力上有非常大的提升&#xff0c;并且开发成本要低2个数量级。应用场景包括&#xff1a;论文论点论据提取、用户画像提取、舆情分析、病例结构…

手动配置IP

手动配置IP&#xff0c;需要考虑四个配置项&#xff1a; 四个配置项 IP地址、子网掩码、默认网关、DNS服务器 IP地址&#xff1a;格式表现为点分十进制&#xff0c;如192.168.254.1 子网掩码&#xff1a;用于区分网络位和主机位 【子网掩码的二进制表达式一定是连续的&#…

Golang 进阶训练营

一、Golang 的 slice、map、channel 1.1 slice vs array a : make([]int, 100) //切片 b : [100]int{} //数组array需指明长度&#xff0c;长度为常量且不可改变 array长度为其类型中的组成部分&#xff08;给参数为长度100的数组的方法传长度为101的会报错&#xff09; array在…

2-使用wifidog实现portal

wifidog是openwrt上面实现portal认证的一个开源工具&#xff0c;从网关端到服务器都帮你搭建好&#xff0c;通过学习wifidog的原理&#xff0c;后面就可以改造成自己需要的逻辑。 1. openwrt安装wifidog 添加源 vim 14.07/feeds.conf.defaultsrc-git wifidog https://github.c…

AI时代前端开发的创造力:解放还是束缚?

在人工智能&#xff08;AI&#xff09;快速发展的时代&#xff0c;AI技术的影响已经渗透到各个领域&#xff0c;从医疗保健到金融服务&#xff0c;再到创意产业。AI工具的出现&#xff0c;为前端开发带来了前所未有的效率提升&#xff0c;但也引发了人们对创造力的担忧&#xf…

有哪些免费的SEO软件优化工具

随着2025年互联网的不断发展&#xff0c;越来越多的企业意识到在数字营销中&#xff0c;网站的曝光度和排名至关重要。无论是想要提高品牌知名度&#xff0c;还是想要通过在线销售增加收益&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;都是一项不可忽视的关键策略。而要…

FastExcel + Java:打造高效灵活的Excel数据导入导出解决方案

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 基于AOP的数据字典实现…

node.js + html调用ChatGPTApi实现Ai网站demo(带源码)

文章目录 前言一、demo演示二、node.js 使用步骤1.引入库2.引入包 前端HTML调用接口和UI所有文件总结 前言 关注博主&#xff0c;学习每天一个小demo 今天是Ai对话网站 又到了每天一个小demo的时候咯&#xff0c;前面我写了多人实时对话demo、和视频转换demo&#xff0c;今天…