交互式ICP

news2024/12/24 3:15:00

        以下程序演示如何编写交互式ICP查看器。该程序将加载点云并对其进行刚性变换。之后,使用ICP算法将变换后的点云与原来的点云对齐。每次用户按下“空格”,进行ICP迭代,刷新可视化界面。

代码实现

资源准备 monkey.ply

#include <string>
#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/time.h>

typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;

bool next_iteration = false;

void print4x4Matrix(const Eigen::Matrix4d& matrix)
{
    printf("Rotation matrix :\n");
    printf("    | %6.3f %6.3f %6.3f | \n", matrix(0, 0), matrix(0, 1), matrix(0, 2));
    printf("R = | %6.3f %6.3f %6.3f | \n", matrix(1, 0), matrix(1, 1), matrix(1, 2));
    printf("    | %6.3f %6.3f %6.3f | \n", matrix(2, 0), matrix(2, 1), matrix(2, 2));
    printf("Translation vector :\n");
    printf("t = < %6.3f, %6.3f, %6.3f >\n\n", matrix(0, 3), matrix(1, 3), matrix(2, 3));
}
/**
 * 此函数是查看器的回调。当查看器窗口位于顶部时,只要按任意键,就会调用此函数。如果碰到“空格”,将布尔值设置为true。
 */
void keyboardEventOccurred(const pcl::visualization::KeyboardEvent& event,void* nothing)
{
    if (event.getKeySym() == "space" && event.keyDown())
        next_iteration = true;
}

int main(int argc,char* argv[])
{
    // 准备将使用的点云
    PointCloudT::Ptr cloud_in(new PointCloudT);  // Original point cloud
    PointCloudT::Ptr cloud_tr(new PointCloudT);  // Transformed point cloud
    PointCloudT::Ptr cloud_icp(new PointCloudT);  // ICP output point cloud

    // 检查程序的参数,设置初始ICP迭代的次数,然后尝试加载PLY文件
    if (argc < 2)
    {
        printf("Usage :\n");
        printf("\t\t%s file.ply number_of_ICP_iterations\n", argv[0]);
        PCL_ERROR("Provide one ply file.\n");
        return (-1);
    }

    int iterations = 1;  // 默认的ICP迭代次数
    if (argc > 2)
    {
        // 如果用户将迭代次数作为参数传递
        iterations = atoi(argv[2]);
        if (iterations < 1)
        {
            PCL_ERROR("Number of initial iterations must be >= 1\n");
            return (-1);
        }
    }

    pcl::console::TicToc time;
    time.tic();
    if (pcl::io::loadPLYFile(argv[1], *cloud_in) < 0)
    {
        PCL_ERROR("Error loading cloud %s.\n", argv[1]);
        return (-1);
    }
    std::cout << "\nLoaded file " << argv[1] << " (" << cloud_in->size() << " points) in " << time.toc() << " ms\n" << std::endl;

    // 我们使用刚性矩阵变换来变换原始点云。
    // cloud_in包含原始点云。
    // cloud_tr和cloud_icp包含平移/旋转的点云。
    // cloud_tr是我们将用于显示的备份(绿点云)。

    // 定义旋转矩阵和平移向量
    Eigen::Matrix4d transformation_matrix = Eigen::Matrix4d::Identity();

    // 一个旋转矩阵 (see https://en.wikipedia.org/wiki/Rotation_matrix)
    double theta = M_PI / 8;  // 以弧度为单位的旋转角度
    transformation_matrix(0, 0) = std::cos(theta);
    transformation_matrix(0, 1) = -sin(theta);
    transformation_matrix(1, 0) = sin(theta);
    transformation_matrix(1, 1) = std::cos(theta);

    // Z轴上的平移(0.4米)
    transformation_matrix(2, 3) = 0.4;

    // 在终端中显示变换矩阵
    std::cout << "Applying this rigid transformation to: cloud_in -> cloud_icp" << std::endl;
    print4x4Matrix(transformation_matrix);

    // 执行转换
    pcl::transformPointCloud(*cloud_in, *cloud_icp, transformation_matrix);
    *cloud_tr = *cloud_icp;  // 我们将cloud_icp备份到cloud_tr中以备将来使用

    // 这是ICP对象的创建。我们设置ICP算法的参数。
    // setMaximumIterations(iterations)设置要执行的初始迭代次数(默认值为1)。
    // 然后,我们将点云转换为cloud_icp。 第一次对齐后,我们将在下一次使用该ICP对象时(当用户按下“空格”时)将ICP最大迭代次数设置为1。

    // ICP算法
    time.tic();
    pcl::IterativeClosestPoint<PointT, PointT> icp;
    icp.setMaximumIterations(iterations);
    icp.setInputSource(cloud_icp);
    icp.setInputTarget(cloud_in);
    icp.align(*cloud_icp);
    icp.setMaximumIterations(1);  // 下次调用.align()函数时,我们将此变量设置为1
    std::cout << "Applied " << iterations << " ICP iteration(s) in " << time.toc() << " ms" << std::endl;

    // 检查ICP算法是否收敛;否则退出程序。如果返回true,我们将转换矩阵存储在4x4矩阵中,然后打印刚性矩阵转换。
    if (icp.hasConverged())
    {
        std::cout << "\nICP has converged, score is " << icp.getFitnessScore() << std::endl;
        std::cout << "\nICP transformation " << iterations << " : cloud_icp -> cloud_in" << std::endl;
        transformation_matrix = icp.getFinalTransformation().cast<double>();
        print4x4Matrix(transformation_matrix);
    }
    else
    {
        PCL_ERROR("\nICP has not converged.\n");
        return (-1);
    }

    // 可视化
    pcl::visualization::PCLVisualizer viewer("ICP demo");
    // 创建两个垂直分隔的窗口
    int v1(0);
    int v2(1);
    viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v1);
    viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v2);

    // 颜色
    float bckgr_gray_level = 0.0;  // 黑色
    float txt_gray_lvl = 1.0 - bckgr_gray_level;

    // 原始点云为白色
    pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_in_color_h(cloud_in, (int)255 * txt_gray_lvl, (int)255 * txt_gray_lvl,
        (int)255 * txt_gray_lvl);
    viewer.addPointCloud(cloud_in, cloud_in_color_h, "cloud_in_v1", v1);
    viewer.addPointCloud(cloud_in, cloud_in_color_h, "cloud_in_v2", v2);

    // 变换后的点云为绿色
    pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_tr_color_h(cloud_tr, 20, 180, 20);
    viewer.addPointCloud(cloud_tr, cloud_tr_color_h, "cloud_tr_v1", v1);

    // ICP对齐点云为红色
    pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_icp_color_h(cloud_icp, 180, 20, 20);
    viewer.addPointCloud(cloud_icp, cloud_icp_color_h, "cloud_icp_v2", v2);

    // 在每个视口中添加文本描述
    viewer.addText("White: Original point cloud\nGreen: Matrix transformed point cloud", 10, 15, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "icp_info_1", v1);
    viewer.addText("White: Original point cloud\nRed: ICP aligned point cloud", 10, 15, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "icp_info_2", v2);

    std::stringstream ss;
    ss << iterations;
    std::string iterations_cnt = "ICP iterations = " + ss.str();
    viewer.addText(iterations_cnt, 10, 60, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "iterations_cnt", v2);

    // 设置背景颜色
    viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v1);
    viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v2);

    // 设置相机位置和方向
    viewer.setCameraPosition(-3.68332, 2.94092, 5.71266, 0.289847, 0.921947, -0.256907, 0);
    viewer.setSize(1280, 1024);  // 可视化窗口的尺寸

    // 注册键盘回调:
    viewer.registerKeyboardCallback(&keyboardEventOccurred, (void*)NULL);

    //可视化
    while (!viewer.wasStopped())
    {
        viewer.spinOnce();

        // 如果用户按下空格:
        if (next_iteration)
        {
            // ICP算法
            time.tic();
            // 如果用户按下键盘上的任意键,则会调用keyboardEventOccurred函数。 此功能检查键是否为“空格”。
            // 如果是,则全局布尔值next_iteration设置为true,从而允许查看器循环输入代码的下一部分:调用ICP对象以进行对齐。
            // 记住,我们已经配置了该对象输入/输出云,并且之前通过setMaximumIterations将最大迭代次数设置为1。

            icp.align(*cloud_icp);
            std::cout << "Applied 1 ICP iteration in " << time.toc() << " ms" << std::endl;

            // 和以前一样,我们检查ICP是否收敛,如果不收敛,则退出程序。
            if (icp.hasConverged())
            {
                // printf(“ 033 [11A”); 在终端增加11行以覆盖显示的最后一个矩阵是一个小技巧。
                // 简而言之,它允许替换文本而不是编写新行; 使输出更具可读性。 我们增加迭代次数以更新可视化器中的文本值。
                printf("\033[11A");  // Go up 11 lines in terminal output.
                printf("\nICP has converged, score is %+.0e\n", icp.getFitnessScore());

                // 这意味着,如果您已经完成了10次迭代,则此函数返回矩阵以将点云从迭代10转换为11。
                std::cout << "\nICP transformation " << ++iterations << " : cloud_icp -> cloud_in" << std::endl;

                // 函数getFinalTransformation()返回在迭代过程中完成的刚性矩阵转换(此处为1次迭代)。
                transformation_matrix *= icp.getFinalTransformation().cast<double>();  // WARNING /!\ This is not accurate! For "educational" purpose only!
                print4x4Matrix(transformation_matrix);  // 打印原始姿态和当前姿态之间的转换

                ss.str("");
                ss << iterations;
                std::string iterations_cnt = "ICP iterations = " + ss.str();
                viewer.updateText(iterations_cnt, 10, 60, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "iterations_cnt");
                viewer.updatePointCloud(cloud_icp, cloud_icp_color_h, "cloud_icp_v2");
            }
            else
            {
                PCL_ERROR("\nICP has not converged.\n");
                return (-1);
            }
            //这不是我们想要的。如果我们将最后一个矩阵与新矩阵相乘,那么结果就是从开始到当前迭代的转换矩阵。
        }
        next_iteration = false;
    }
    return (0);
}

使用方式

windows下执行以下命令:

.\xxx.exe ...\monkey.ply 1

随着迭代次数增加,效果如下:

迭代次数=1

迭代次数=19

迭代次数=41

官方文档:https://pcl-tutorials.readthedocs.io/en/latest/interactive_icp.html 

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

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

相关文章

用Python简单的实现一下六大主流小说平台小说下载(附源码)

很多小伙伴学习Python的初衷就是为了爬取小说&#xff0c;方便又快捷~ 辣么今天咱们来分享6个主流小说平台的爬取教程~ 一、流程步骤 流程基本都差不多&#xff0c;只是看网站具体加密反爬&#xff0c;咱们再进行解密。 实现爬虫的第一步? 1、去抓包分析&#xff0c;分析数…

从零开始:制作出色的产品原型图的详细教程

在设计产品的初始版本或模型时&#xff0c;产品原型起着非常重要的作用&#xff0c;可以帮助设计师和团队更好地了解产品需求和用户需求&#xff0c;优化和改进设计&#xff0c;确保设计最终满足用户的需求和期望。如果你不知道如何绘制产品原型图&#xff0c;绘制产品原型图的…

应用开发平台集成工作流系列之14——流程表单实现示例

背景 流程审批&#xff0c;通常对应一个业务表单。这个表单一般有两种实现模式&#xff0c;一是不同的环节对应不同表单&#xff0c;二是做一张大表单&#xff0c;分为不同的区域&#xff0c;从业务角度考虑&#xff0c;前者通常对应复杂业务处理&#xff1b;后面一种则更常见…

苹果手机如何设置软件提醒功能?苹果手机哪个软件有提醒功能?

作为苹果手机用户&#xff0c;我们都希望能够准时接收待办事项或日程提醒&#xff0c;高效完成各项任务和待办事项&#xff0c;例如无论是工作中的重要会议&#xff0c;还是生活中的约会安排&#xff0c;我们都不能够忘记。那么苹果手机如何设置软件的提醒功能呢&#xff1f;苹…

解决 Git:This is not a valid source path/URL

由于sourcetree 可以获取不同仓库的代码&#xff0c;而我的用户名密码比较杂乱&#xff0c;导致经常会修改密码&#xff0c;在新建拉去仓库代码的时候sourcetree 不会提示你密码错误&#xff0c;直接提示 This is not a valid source path/URL。 在已存在的代码仓库&#xff0…

QT UI控件汇总介绍

按钮 ToolButton 和pushbutton没什么区别&#xff0c;可以用来设置图标 设置展示策略 RadioButton 一般用Container可以将其框起来设置互斥域&#xff0c;推荐选用GroupBox 使用方法 qDebug()<<ui->radioButton_3->isChecked(); CheckBox 可以勾选三态 stat…

动态代理IP常见超时原因及解决方法

在使用动态代理IP时&#xff0c;常常会遇到代理超时的问题。网络环境的不稳定性以及代理IP的质量问题&#xff0c;都可能会引起代理超时。这种情况下&#xff0c;代理服务器无法在规定时间内响应我们的请求&#xff0c;导致请求失败。 使用动态代理IP时&#xff0c;哪些原因会引…

SpringBoot和Hibernate——如何提高数据库性能

摘要&#xff1a;本文由葡萄城技术团队发布。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 在软件开发领域&#xff0c;性能是重中之重。无论您是构建小型 Web 应用程序还是大型企业系统…

MySQL详解 七:数据库高级语句(视图表、存储过程)

文章目录 1. create view ---- (视图表)1.1 视图的简单介绍1.2 基本语法1.2.1 创建视图表1.2.2 查看视图表1.2.3 删除视图表1.2.4 修改视图表 1.3 通过视图表得出无交集 2. case语句3. 空值&#xff08;null&#xff09; 和 无值&#xff08; &#xff09; 的区别4. 正则表达式…

大型语言模型:DistilBERT — 更小、更快、更便宜、更轻

一、介绍 近年来&#xff0c;大型语言模型的演进速度飞速发展。BERT成为最流行和最有效的模型之一&#xff0c;可以高精度地解决各种NLP任务。在BERT之后&#xff0c;一组其他模型随后出现在现场&#xff0c;也展示了出色的结果。 很容易观察到的明显趋势是&#xff0c;随着时间…

Vulnhub系列靶机-The Planets Earth

文章目录 Vulnhub系列靶机-The Planets: Earth1. 信息收集1.1 主机扫描1.2 端口扫描1.3 目录爆破 2. 漏洞探测2.1 XOR解密2.2 解码 3. 漏洞利用3.1 反弹Shell 4. 权限提升4.1 NC文件传输 Netcat&#xff08;nc&#xff09;文件传输 Vulnhub系列靶机-The Planets: Earth 1. 信息…

工矿企业电力运维云平台:提升效率与降低成本的关键

针对工矿企业用户&#xff0c;聚焦供配电领系统“安全监控、能耗管理、智能运维” 的三大需求&#xff0c;研发推出了“电易云”--智慧电力物联网&#xff0c;为工矿企业用户提供智慧电力数字化解决方案及数据服务&#xff0c;实现供配电系统的数字化、云端化、智能化、绿色化&…

笔记36:CNN的多通道卷积到底是什么样的

总结&#xff1a; &#xff08;1&#xff09;输入卷积层的feature_map的通道数&#xff0c;就是该卷积层每个卷积核所含有的通道数 &#xff08;2&#xff09;输出卷积层的feature_map的通道数&#xff0c;就是该卷积层所含有的卷积核的个数 a a a a 解释&#xff1a;【…

【数据结构C/C++】稀疏矩阵的压缩

文章目录 什么是稀疏矩阵&#xff1f;使用C语实现对稀疏矩阵的压缩408考研各数据结构C/C代码&#xff08;Continually updating&#xff09; 什么是稀疏矩阵&#xff1f; 稀疏矩阵&#xff08;Sparse Matrix&#xff09;是一种矩阵&#xff0c;其中大多数元素都是零。与稠密矩…

Floorplanning with Graph Attention

Floorplanning with Graph Attention DAC ’22 目录 Floorplanning with Graph Attention摘要1.简介2.相关工作3.问题公式化4. FLORA的方法4.1 解决方案概述4.2 C-谱聚类算法 4.3 基于GAT的模型4.4 合成训练数据集生成 摘要 布图规划一直是一个关键的物理设计任务&#xff0…

mac(M1)安装anaconda3

首先下载 然后正常安装即可&#xff0c;之所以我现在测试了anaconda,因为我发现miniconda后&#xff0c;jupyter notebook的安装就出现问题&#xff0c;所以就直接卸载miniconda&#xff0c;而直接安装anaconda了 (base) yxkbogon ~ % pip list Package …

算法通过村第十三关-术数|白银笔记|术数高频问题

文章目录 前言数组实现加法专题数组实现整数加法字符串加法二进制加法 幂运算专题求2的次幂求3的次幂求4的次幂 总结 前言 提示&#xff1a;人心本易趋死寂&#xff0c;苦难之后&#xff0c;焕然重建&#xff0c;激荡一阵&#xff0c;又趋麻木。 --苏枕书《有鹿来》 我们继续看…

连续子数组的最大和

这其实用到的是一个dp的动态规划数组来描写的。 用两个变量就能解决了&#xff0c;一个是max(记录前i个数中子数组的最大的和), 一个是sum是记录前i个数组的和最大值和自己去比较&#xff0c;就是前i-1个和是8&#xff0c;自己是-2&#xff0c; 8 - 2 > -2&#xff0c;所以…

Qt内置的图标与图标字体库加载应用实例(内置图标与加载外置的图标字体库)

一、前言 当涉及到创建应用程序的图形用户界面(GUI)时,图标是不可或缺的一部分。Qt作为一个流行的跨平台开发框架,不仅提供了丰富的UI组件和功能,还内置了许多精美的图标供开发者使用。这些内置图标提供了一种简便的方式,使开发者可以在应用程序中轻松地添加各种图标,提…

科技云报道:联络中心效能与体验齐飞,容联云AICC是如何做到的?

科技云报道原创。 AI与大模型为千行万业带来的进化与改造&#xff0c;远比想象来得更加猛烈。作为数字化升级改造的核心场景之一&#xff0c;联络中心在AI与大模型加持下&#xff0c;正在从基础云通讯迈入智能化的3.0时代。 身处行业智能化浪潮之中&#xff0c;容联云AICC作为…