Dexcap复现代码数据预处理全流程(四)——demo_clipping_3d.py

news2025/1/10 15:10:56

此脚本的主要功能是可视化点云数据文件(.pcd 文件),并通过键盘交互选择演示数据的起始帧和结束帧,生成片段标记文件 (clip_marks.json)

主要流程包括:

  • 用户指定数据目录:检查目录是否存在并处理标记文件 ->
  • 加载帧数据并渲染:使用 Open3D 可视化点云数据和几何结构 ->
  • 监听键盘输入:通过键盘动态导航帧,并标记片段的起始和结束 ->
  • 保存标记数据:将标记的片段保存为 JSON 文件

目录

1 库引用

2 主程序入口

3 处理 clip_marks.json 文件

3 初始化 ReplayDataVisualizer

4 键盘交互

5 回放帧可视化


1 库引用

import argparse
import os
import copy
import zmq
import cv2
import sys
import json
import shutil
import open3d as o3d
import numpy as np
import platform
from pynput import keyboard
from transforms3d.quaternions import qmult, quat2mat
from transforms3d.axangles import axangle2mat
from scipy.spatial.transform import Rotation
from transforms3d.euler import quat2euler, mat2euler, quat2mat, euler2mat
from visualizer import *

# 全局变量定义
clip_marks = []  # 存储所有片段标记
current_clip = {}  # 当前片段的起始和结束标记
next_frame = False  # 用于指示是否跳转到下一帧
previous_frame = False  # 用于指示是否跳转到上一帧

2 主程序入口

# 主程序入口
if __name__ == "__main__":
    # 解析命令行参数
    parser = argparse.ArgumentParser(description="Visualize saved frame data.")
    parser.add_argument("--directory", type=str, default="./saved_data", help="Directory with saved data")
    args = parser.parse_args()

    # 检查输入目录是否存在
    assert os.path.exists(args.directory), f"given directory: {args.directory} not exists"

解析命令行参数 -> 检查并处理 clip_marks.json -> 初始化数据可视化器 -> 启动回放帧可视化,并监听用户交互

命令行参数解析与输入校验:

1. 解析命令行参数:--directory 参数指定数据目录,默认为 ./saved_data

2. 校验输入路径:检查指定目录是否存在,否则直接报错退出

3 处理 clip_marks.json 文件

    # 检查是否已有 clip_marks.json 文件,避免误覆盖
    if os.path.exists(os.path.join(args.directory, 'clip_marks.json')):
        response = (
            input(
                f"clip_marks.json already exists. Do you want to override? (y/n): "
            )
            .strip()
            .lower()
        )
        if response != "y":
            print("Exiting program without overriding the existing directory.")
            sys.exit()

如果 clip_marks.json 已存在,提示用户是否覆盖:

  • 输入 y:继续运行,并允许覆盖文件
  • 输入其他内容:程序退出,避免覆盖原有标记数据

3 初始化 ReplayDataVisualizer

    # 数据目录
    dataset_folder = args.directory

    # 初始化可视化器
    visualizer = ReplayDataVisualizer(args.directory)

    # 加载左右手校准数据(偏移和旋转)
    visualizer.right_hand_offset = np.loadtxt("{}/calib_offset.txt".format(args.directory))
    visualizer.right_hand_ori_offset = np.loadtxt("{}/calib_ori_offset.txt".format(args.directory))
    visualizer.left_hand_offset = np.loadtxt("{}/calib_offset_left.txt".format(args.directory))
    visualizer.left_hand_ori_offset = np.loadtxt("{}/calib_ori_offset_left.txt".format(args.directory))

    # 开始帧回放和交互
    visualizer.replay_frames()

初始化自定义类 ReplayDataVisualizer,继承自 DataVisualizer 类

从文件中加载左右手的偏移数据(位置偏移和旋转偏移),用于调整点云的显示

4 键盘交互

# 键盘按下事件的回调函数
def on_press(key):
    """
    当键盘按键被按下时触发。
    根据按键执行相应的功能,例如帧跳转、标记起始/结束、保存标记等。
    """
    global next_frame, previous_frame, frame, clip_marks, current_clip

    # 当前帧的文件夹名
    frame_folder = 'frame_{}'.format(frame)

    # 根据键盘按键的类型执行操作
    if key == keyboard.Key.up:  # 按下“上箭头”保存当前所有标记到 JSON 文件
        with open(os.path.join(dataset_folder, 'clip_marks.json'), 'w') as f:
            json.dump(clip_marks, f, indent=4)
    elif key == keyboard.Key.down:  # 按下“下箭头”跳转到上一帧
        previous_frame = True
    elif key == keyboard.Key.page_down:  # 按下“Page Down”键跳转到下一帧
        next_frame = True
    elif key == keyboard.Key.end:  # 按下“End”键标记当前片段结束
        if 'start' in current_clip.keys():  # 只有当前片段有起点时才允许结束
            print("end", frame_folder)
            current_clip['end'] = frame_folder  # 设置结束帧
            clip_marks.append(current_clip)  # 将当前片段添加到标记列表
            current_clip = {}  # 清空当前片段
    elif key == keyboard.Key.insert:  # 按下“Insert”键标记当前片段开始
        print("start", frame_folder)
        current_clip['start'] = frame_folder  # 设置起始帧
    else:
        print("Key error", key)  # 其他按键未定义功能

监听键盘输入,并根据按键触发相应功能:

  • Insert:标记当前帧为片段的起始帧
  • End:标记当前帧为片段的结束帧,并保存到 clip_marks 列表
  • Page Down:跳转到下一帧
  • Down Arrow:跳转到上一帧
  • Up Arrow:将标记的片段保存到 clip_marks.json 文件

5 回放帧可视化

# 定义自定义可视化类,继承 DataVisualizer
class ReplayDataVisualizer(DataVisualizer):
    def __init__(self, directory):
        """
        初始化 ReplayDataVisualizer 类
        :param directory: 数据目录路径
        """
        super().__init__(directory)

    def replay_frames(self):
        """
        帧回放和交互函数。
        可视化点云数据,并支持用户通过键盘交互切换帧和标记片段。
        """
        global next_frame, previous_frame, frame

        # 初始化基准帧
        if self.R_delta_init is None:
            self.initialize_canonical_frame()

        # 加载初始帧数据
        self._load_frame_data(frame)

        # 将几何体添加到可视化场景中
        self.vis.add_geometry(self.pcd)  # 添加点云
        self.vis.add_geometry(self.coord_frame_1)  # 添加坐标系1
        self.vis.add_geometry(self.coord_frame_2)  # 添加坐标系2
        self.vis.add_geometry(self.coord_frame_3)  # 添加坐标系3
        for joint in self.left_joints + self.right_joints:  # 添加所有关节
            self.vis.add_geometry(joint)
        for cylinder in self.left_line_set + self.right_line_set:  # 添加所有连杆
            self.vis.add_geometry(cylinder)

        # 使用键盘监听器处理交互事件
        try:
            with keyboard.Listener(on_press=on_press) as listener:
                while True:
                    # 跳转到下一帧
                    if next_frame:
                        next_frame = False
                        frame += 10  # 每次跳转10帧
                    # 跳转到上一帧
                    if previous_frame:
                        previous_frame = False
                        frame -= 10  # 每次返回10帧

                    # 加载新帧数据
                    self._load_frame_data(frame)

                    # 更新几何体的显示
                    self.vis.update_geometry(self.pcd)
                    self.vis.update_geometry(self.coord_frame_1)
                    self.vis.update_geometry(self.coord_frame_2)
                    self.vis.update_geometry(self.coord_frame_3)
                    for joint in self.left_joints + self.right_joints:
                        self.vis.update_geometry(joint)
                    for cylinder in self.left_line_set + self.right_line_set:
                        self.vis.update_geometry(cylinder)

                    # 渲染场景
                    self.vis.poll_events()
                    self.vis.update_renderer()
                listener.join()
        finally:
            # 输出累积校正信息(如果有)
            print("cumulative_correction ", self.cumulative_correction)

加载并可视化当前帧数据:渲染点云数据和其他几何体(坐标系、关节、连杆等)

键盘监听:根据用户输入跳转到下一帧或上一帧

更新渲染:每次跳转后重新加载当前帧数据,并更新渲染场景

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

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

相关文章

MBM指尖六维力触觉传感器:高灵敏度、低漂移,精准掌控力学世界

MBM指尖六维力触觉传感器是一种专为机器人设计的高性能传感器。它通过集成三轴力和三轴力矩的感知能力,能够精准捕捉复杂的力学信息。传感器采用MEMS与应变体复合测量技术,具备数字输出功能,显著降低漂移并减少安装偏移的影响。其紧凑轻便的设…

C#,图论与图算法,任意一对节点之间最短距离的弗洛伊德·沃肖尔(Floyd Warshall)算法与源程序

一、弗洛伊德沃肖尔算法 Floyd-Warshall算法是图的最短路径算法。与Bellman-Ford算法或Dijkstra算法一样,它计算图中的最短路径。然而,Bellman Ford和Dijkstra都是单源最短路径算法。这意味着他们只计算来自单个源的最短路径。另一方面,Floy…

为答疑机器人扩展问题分类与路由功能

1.意图识别 2. 构建路由模块 简单的意图识别 from chatbot import llmfrom config.load_key import load_key load_key()prompt 【角色背景】 你是一个问题分类路由器,需要识别问题的类型。 --- 【任务要求】 问题的类型目前有:公司内部文档查询、内…

spring boot启动源码分析(三)之Environment准备

上一篇《spring-boot启动源码分析(二)之SpringApplicationRunListener》 环境介绍: spring boot版本:2.7.18 主要starter:spring-boot-starter-web 本篇开始讲启动过程中Environment环境准备,Environment是管理所有…

Pandas-RFM会员价值度模型

文章目录 一. 会员价值度模型介绍二. RFM计算与显示1. 背景2. 技术点3. 数据4. 代码① 导入模块② 读取数据③ 数据预处理Ⅰ. 数据清洗, 即: 删除缺失值, 去掉异常值.Ⅱ. 查看清洗后的数据Ⅲ. 把前四年的数据, 拼接到一起 ④ 计算RFM的原始值⑤ 确定RFM划分区间⑥ RFM计算过程⑦…

【理论】测试框架体系TDD、BDD、ATDD、DDT介绍

一、测试框架是什么 测试框架是一组用于创建和设计测试用例的指南或规则。框架由旨在帮助 QA 专业人员更有效地测试的实践和工具的组合组成。 这些指南可能包括编码标准、测试数据处理方法、对象存储库、存储测试结果的过程或有关如何访问外部资源的信息。 A testing framewo…

FreeU: Free Lunch in Diffusion U-Net 笔记

FreeU: Free Lunch in Diffusion U-Net 摘要 作者研究了 U-Net 架构对去噪过程的关键贡献,并发现其主干部分主要在去噪方面发挥作用,而其跳跃连接主要是向解码器模块引入高频特征,这使得网络忽略了主干部分的语义信息。基于这一发现&#…

JAVA 使用apache poi实现EXCEL文件的输出;apache poi实现标题行的第一个字符为红色;EXCEL设置某几个字符为别的颜色

设置输出文件的列宽,防止文件过于丑陋 Sheet sheet workbook.createSheet(FileConstants.ERROR_FILE_SHEET_NAME); sheet.setColumnWidth(0, 40 * 256); sheet.setColumnWidth(1, 20 * 256); sheet.setColumnWidth(2, 20 * 256); sheet.setColumnWidth(3, 20 * 25…

【STM32】无源蜂鸣器播放音乐《千与千寻》,HAL库

目录 一、工程链接 二、简单介绍 主要特点: 应用: 驱动电路: 三、原理图 四、cubeMX配置 时钟配置 五、keil配置 六、驱动编写 演奏函数 主函数编写 七、效果展示 八、驱动附录 music.h music.c 一、工程链接 STM32无源蜂鸣…

在 Vue 3 集成 e签宝电子合同签署功能

实现 Vue 3 e签宝电子合同签署功能,需要使用 e签宝提供的实际 SDK 或 API。 e签宝通常提供针对不同平台(如 Web、Android、iOS)的 SDK,而 Web 端一般通过 WebView 或直接使用嵌入式 iframe 来加载合同签署页面。 下面举个 &…

04、Redis深入数据结构

一、简单动态字符串SDS 无论是Redis中的key还是value,其基础数据类型都是字符串。如,Hash型value的field与value的类型,List型,Set型,ZSet型value的元素的类型等都是字符串。redis没有使用传统C中的字符串而是自定义了…

如何用Python编程实现自动整理XML发票文件

传统手工整理发票耗时费力且易出错,而 XML 格式发票因其结构化、标准化的特点,为实现发票的自动化整理与保存提供了可能。本文将详细探讨用python来编程实现对 XML 格式的发票进行自动整理。 一、XML 格式发票的特点 结构化数据:XML 格式发票…

Linux——修改USB网卡设备节点名称

修改驱动: 测试: 参考资料: https://blog.csdn.net/ablexu2018/article/details/144868950

(STM32笔记)十二、DMA的基础知识与用法 第三部分

我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话,用的也是这个板子和教程。 DMA的基础知识与用法 三、DMA程序验证1、DMA 存储器到存储器模式实验(1)DMA结构体解释(2…

论文笔记(六十一)Implicit Behavioral Cloning

Implicit Behavioral Cloning 文章概括摘要1 引言2 背景:隐式模型的训练与推理3 隐式模型与显式模型的有趣属性4 policy学习成果5 理论见解:隐式模型的通用逼近性6 相关工作7 结论 文章概括 引用: inproceedings{florence2022implicit,titl…

高斯函数Gaussian绘制matlab

高斯 约翰卡尔弗里德里希高斯,(德语:Johann Carl Friedrich Gau,英语:Gauss,拉丁语:Carolus Fridericus Gauss)1777年4月30日–1855年2月23日,德国著名数学家、物理学家…

vue的路由守卫逻辑处理不当导致部署在nginx上无法捕捉后端异步响应消息等问题

近期对前端的路由卫士有了更多的认识。 何为路由守卫?这可能是一种约定俗成的名称。就是VUE中的自定义函数,用来处理路由跳转。 import { createRouter, createWebHashHistory } from "vue-router";const router createRouter({history: cr…

如何在 Ubuntu 22.04 上使用 LEMP 安装 WordPress 教程

简介: 本教程旨在指导你如何在 Ubuntu 22.04 上使用 LEMP 栈安装 WordPress。 WordPress 是一个用 PHP 编写的开源内容管理系统。LEMP 栈是 Linux,NGINX,MySQL 和 PHP 的缩写。WordPress 非常用户友好,并提供了多种选项&#xff…

PySide6基于QSlider实现QDoubleSlider

我在写小工具的时候,需要一个支持小数的滑动条。 我QSpinBox都找到了QDoubleSpinBox,QSlider愣是没找到对应的东西。 网上有好多对QSlider封装实现QDoubleSlider的文章。 似乎Qt真的没有这个东西,需要我们自行实现。 于是我也封装了一个&…

升级 Spring Boot 3 配置讲解 —— 支持断点传输的文件上传和下载功能

学会这款 🔥全新设计的 Java 脚手架 ,从此面试不再怕! 在现代 Web 应用中,文件上传和下载是非常常见的需求。然而,当文件较大时,传统的上传下载方式可能会遇到网络不稳定或传输中断的问题。为了解决这些问题…