快速鲁棒的ICP(一)

一、代码下载以、修改以及使用

链接:OpenGP/sparseicp: Automatically exported from code.google.com/p/sparseicp (github.com)

解压之后:

快速鲁棒的ICP是其他人在这份Sparse ICP代码基础上改写出来的:

我这里已经下载好了:快速鲁棒ICP.zip资源-CSDN文库

首先visual studio项目,配置好PCL环境;首先把上述图片中看得到的以cpp、h、hpp结尾的文件全都放入你的项目中;再把include文件夹的nanoflann.hpp放入你的项目就行了。

其中view_show.hpp是我为了可视化新建的文件 ,源文件里面并没有

修改 :

由于该代码有几处头文件使用的是绝对路径,你需要修改成自己的路径,而且源码没有可视化,我这里修改了下,使得可以进行可视化

1、头文件修改

Types.h、median.h、io_pc.h

把这行改成#include <Eigen/Dense>

 FRICP.h

把这行改成#include <unsupported/Eigen/MatrixFunctions>

这样头文件的问题就解决了

2、可视化的修改

我增加了一个文件view_show.hpp

#ifndef VIEW_SHOW_H
#define VIEW_SHOW_H
#include <Eigen/Dense>
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/console/print.h>
class ViewShow
{
public:
    ViewShow()
    {

    }
	int readOBJ(const std::string objFilePath, Eigen::MatrixXf& points)
	{
        cout << objFilePath << endl;
        std::ifstream objFile(objFilePath);
        std::string line;
        std::vector<Eigen::Vector3f> vertexList; // 用于临时存储顶点数据的列表

        if (!objFile.is_open()) {
            std::cerr << "Unable to open OBJ file." << std::endl;
            return -1;
        }

        while (std::getline(objFile, line)) {
            std::stringstream ss(line);
            std::string lineType;
            ss >> lineType;

            // 只处理顶点数据行
            if (lineType == "v") {
                float x, y, z;
                //cout << x << y << z << endl;
                ss >> x >> y >> z;
                vertexList.push_back(Eigen::Vector3f(x, y, z));
            }
        }
        //cout << vertexList.size() << endl;

        objFile.close();

        // 将顶点数据从vector转移到Eigen::MatrixXf
        points.resize(vertexList.size(), 3); // 初始化points矩阵
        for (size_t i = 0; i < vertexList.size(); ++i) {
            points(i, 0) = vertexList[i][0];
            points(i, 1) = vertexList[i][1];
            points(i, 2) = vertexList[i][2];

            //cout << points.row(i) << endl;

        }

        // 此时points矩阵已填充完成,可以继续使用它进行其他操作
        std::cout << "Loaded " << points.rows() << " vertices into the points matrix." << std::endl;
        return 1;
	}

    void transOBJToPLY(const std::string objFilePath,const std::string plyFilePath,Eigen::MatrixXf& points)
    {

        // 创建一个PointCloud对象
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

        // 填充点云数据
        cloud->width = points.rows();
        cloud->height = 1;
        cloud->is_dense = false;
        cloud->points.resize(cloud->width * cloud->height);

        for (size_t i = 0; i < cloud->points.size(); ++i) {
            cloud->points[i].x = points(i, 0);
            cloud->points[i].y = points(i, 1);
            cloud->points[i].z = points(i, 2);
        }

        // 保存PLY文件
        pcl::PLYWriter writer;
        writer.write(plyFilePath, *cloud);

        std::cout << "Successfully converted OBJ to PLY." << std::endl;
    }

    void saveOBJToPLY(const std::string file_source_reg, std::string& resultF, const std::string out_path, const std::string name)
    {
        if (strcmp(file_source_reg.substr(file_source_reg.size() - 4, 4).c_str(), ".obj") == 0)
        {
            Eigen::MatrixXf points0;
            int res = readOBJ(file_source_reg, points0);
            if (res)
            {
                resultF = out_path + name + "reg_pc.ply";
                transOBJToPLY(file_source_reg, resultF, points0);
            }
            else
            {
                std::cout << "读取" << file_source_reg << "失败" << std::endl;
            }


        }
    }
    void view_display(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_target, pcl::PointCloud<pcl::PointXYZ>::Ptr result)
    {
        boost::shared_ptr<pcl::visualization::PCLVisualizer>viewer(new pcl::visualization::PCLVisualizer("PCL Viewer"));
        viewer->setBackgroundColor(0, 0, 0);
         对目标点云着色可视化 (red).
        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>target_color(cloud_target, 255, 0, 0);//红色
        viewer->addPointCloud<pcl::PointXYZ>(cloud_target, target_color, "target cloud");
        viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "target cloud");
        // 对配准点云着色可视化 (green).
        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>output_color(result, 0, 255, 0);//绿色
        viewer->addPointCloud<pcl::PointXYZ>(result, output_color, "output_color");
        viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "output_color");


        while (!viewer->wasStopped())
        {
            viewer->spinOnce(100);
            boost::this_thread::sleep(boost::posix_time::microseconds(1000));
        }
    }
    ~ViewShow()
    {

    }
};






#endif

 在main.cpp增加了调用,同时取消了从命令行读取参数,改成固定的参数,方便测试,当然你想用也可以,直接把if(argc == 5)这段代码解开注释就行

#ifdef _HAS_STD_BYTE
#undef _HAS_STD_BYTE
#endif
#define _HAS_STD_BYTE 0
#include <iostream>
#include "ICP.h"
#include "io_pc.h"
#include "FRICP.h"
#include <time.h>
#include "view_show.hpp"
#include <io.h>
#include <direct.h>
int main(int argc, char const** argv)
{
    clock_t start, end;
    double totaltime;
    start = clock();
    typedef double Scalar;
    typedef Eigen::Matrix<Scalar, 3, Eigen::Dynamic> Vertices;
    typedef Eigen::Matrix<Scalar, 3, 1> VectorN;
    std::string file_source;
    std::string file_target;
    std::string file_init = "./data/";
    std::string res_trans_path;
    std::string out_path;
    bool use_init = false;
    MatrixXX res_trans;
    enum Method { ICP, AA_ICP, FICP, RICP, PPL, RPPL, SparseICP, SICPPPL } method = RICP;
    /*file_target = "./data/bunny_side2.obj";
    file_source = "./data/bunny_side1.obj";
    out_path = "./data/res/";
    method = Method::ICP;*/
    file_target = "./data/target.ply";
    file_source = "./data/source.ply";
    out_path = "./data/res2/";
    method = Method::ICP;
    if (_access(out_path.c_str(), 0) == -1)	//如果文件夹不存在
        _mkdir(out_path.c_str());
    /*if (argc == 5)
    {
        file_target = argv[1];
        file_source = argv[2];
        out_path = argv[3];
        method = Method(std::stoi(argv[4]));
    }
    else if (argc == 4)
    {
        file_target = argv[1];
        file_source = argv[2];
        out_path = argv[3];
    }
    else
    {
        std::cout << "Usage: target.ply source.ply out_path <Method>" << std::endl;
        std::cout << "Method :\n"
            << "0: ICP\n1: AA-ICP\n2: Our Fast ICP\n3: Our Robust ICP\n4: ICP Point-to-plane\n"
            << "5: Our Robust ICP point to plane\n6: Sparse ICP\n7: Sparse ICP point to plane" << std::endl;
        exit(0);
    }*/
    int dim = 3;
    //--- Model that will be rigidly transformed 将被严格转换的模型
    Vertices vertices_source, normal_source, src_vert_colors;
    read_file(vertices_source, normal_source, src_vert_colors, file_source);
    std::cout << "source: " << vertices_source.rows() << "x" << vertices_source.cols() << std::endl;

    //--- Model that source will be aligned to 源点云将与之对齐的模型
    Vertices vertices_target, normal_target, tar_vert_colors;
    read_file(vertices_target, normal_target, tar_vert_colors, file_target);
    std::cout << "target: " << vertices_target.rows() << "x" << vertices_target.cols() << std::endl;

    // scaling
    Eigen::Vector3d source_scale, target_scale;
    source_scale = vertices_source.rowwise().maxCoeff() - vertices_source.rowwise().minCoeff();
    target_scale = vertices_target.rowwise().maxCoeff() - vertices_target.rowwise().minCoeff();
    double scale = std::max(source_scale.norm(), target_scale.norm());
    std::cout << "scale = " << scale << std::endl;
    vertices_source /= scale;
    vertices_target /= scale;

    /// De-mean
    VectorN source_mean, target_mean;
    source_mean = vertices_source.rowwise().sum() / double(vertices_source.cols());
    target_mean = vertices_target.rowwise().sum() / double(vertices_target.cols());
    vertices_source.colwise() -= source_mean;
    vertices_target.colwise() -= target_mean;

    double time;
    // set ICP parameters
    ICP::Parameters pars;

    // set Sparse-ICP parameters
    SICP::Parameters spars;
    spars.p = 0.4;
    spars.print_icpn = false;

    /// Initial transformation
    if (use_init)
    {
        MatrixXX init_trans;
        read_transMat(init_trans, file_init);
        init_trans.block(0, dim, dim, 1) /= scale;
        init_trans.block(0, 3, 3, 1) += init_trans.block(0, 0, 3, 3) * source_mean - target_mean;
        pars.use_init = true;
        pars.init_trans = init_trans;
        spars.init_trans = init_trans;
    }

    ///--- Execute registration
    std::cout << "begin registration..." << std::endl;
    FRICP<3> fricp;
    double begin_reg = omp_get_wtime();
    double converge_rmse = 0;
    switch (method)
    {
    case ICP:
    {
        pars.f = ICP::NONE;
        pars.use_AA = false;
        fricp.point_to_point(vertices_source, vertices_target, source_mean, target_mean, pars);
        res_trans = pars.res_trans;
        break;
    }
    case AA_ICP:
    {
        AAICP::point_to_point_aaicp(vertices_source, vertices_target, source_mean, target_mean, pars);
        res_trans = pars.res_trans;
        break;
    }
    case FICP:
    {
        pars.f = ICP::NONE;
        pars.use_AA = true;
        fricp.point_to_point(vertices_source, vertices_target, source_mean, target_mean, pars);
        res_trans = pars.res_trans;
        break;
    }
    case RICP:
    {
        pars.f = ICP::WELSCH;
        pars.use_AA = true;
        fricp.point_to_point(vertices_source, vertices_target, source_mean, target_mean, pars);
        res_trans = pars.res_trans;
        break;
    }
    case PPL:
    {
        pars.f = ICP::NONE;
        pars.use_AA = false;
        fricp.point_to_plane(vertices_source, vertices_target, normal_source, normal_target, source_mean, target_mean, pars);
        res_trans = pars.res_trans;
        break;
    }
    case RPPL:
    {
        pars.nu_end_k = 1.0 / 6;
        pars.f = ICP::WELSCH;
        pars.use_AA = true;
        fricp.point_to_plane_GN(vertices_source, vertices_target, normal_source, normal_target, source_mean, target_mean, pars);
        res_trans = pars.res_trans;
        break;
    }
    case SparseICP:
    {
        SICP::point_to_point(vertices_source, vertices_target, source_mean, target_mean, spars);
        res_trans = spars.res_trans;
        break;
    }
    case SICPPPL:
    {
        SICP::point_to_plane(vertices_source, vertices_target, normal_target, source_mean, target_mean, spars);
        res_trans = spars.res_trans;
        break;
    }
    }
    std::cout << "Registration done!" << std::endl;
    double end_reg = omp_get_wtime();
    time = end_reg - begin_reg;
    vertices_source = scale * vertices_source;

    out_path = out_path + "m" + std::to_string(method);
    Eigen::Affine3d res_T;
    res_T.linear() = res_trans.block(0, 0, 3, 3);
    res_T.translation() = res_trans.block(0, 3, 3, 1);
    res_trans_path = out_path + "trans.txt";
    std::ofstream out_trans(res_trans_path);
    res_trans.block(0, 3, 3, 1) *= scale;
    out_trans << res_trans << std::endl;
    out_trans.close();

    ///--- Write result to file
    //std::string file_source_reg = out_path + "reg_pc.obj";
    std::string file_source_reg = out_path + "reg_pc.ply";
    write_file(file_source, vertices_source, normal_source, src_vert_colors, file_source_reg);
    end = clock();
    totaltime = (double)(end - start) / CLOCKS_PER_SEC;
    std::cout << "Time:" << totaltime << "s" << std::endl;


    //不管是obj还是ply文件,统一转换成ply进行可视化
    ViewShow v;
    std::string resultF = file_source_reg;
    std::string sourceF = file_source;
    std::string targetF = file_target;
    pcl::console::setVerbosityLevel(pcl::console::L_ERROR);
    pcl::PointCloud<pcl::PointXYZ>::Ptr result(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_target(new pcl::PointCloud<pcl::PointXYZ>);
    v.saveOBJToPLY(file_source_reg, resultF, out_path, "Result");
    v.saveOBJToPLY(file_target, targetF, out_path, "Target");
    v.saveOBJToPLY(file_source, sourceF, out_path, "Source");

 
    if (pcl::io::loadPLYFile<pcl::PointXYZ>(resultF, *result) == -1)
    {
        PCL_ERROR("加载点云失败\n");
    }
    if (pcl::io::loadPLYFile<pcl::PointXYZ>(sourceF, *cloud) == -1)
    {
        PCL_ERROR("加载点云失败\n");
    }
    if (pcl::io::loadPLYFile<pcl::PointXYZ>(targetF, *cloud_target) == -1)
    {
        PCL_ERROR("加载点云失败\n");
    }
    v.view_display(cloud_target, cloud);
    v.view_display(cloud_target, result);
    return 0;
}

使用:

如果不从命令行读取数据,那就在这份代码的main.cpp里面修改,首先第一行定义了一个枚举类型,里面有很多配准方法FICP和RICP就是快速鲁棒ICP,并创建了method这个枚举类型的变量;第二行就是目标点云的路径,第三行就是输入点云的路径;第四行是你的输出文件夹路径,你要配准不同组的点云,最好换个名字,不然上一组点云的实验结果会被覆盖;第五行是我自己用来修改method的,方便测试不同的ICP。

这份代码可支持obj和ply点云文件

当你目标点云和输入点云都是ply点云文件时,这行代码的结尾也是ply,当你目标点云和输入点云都是obj点云文件时,这行代码的结尾也得是obj ,否则会出现报错

 当你目标点云和输入点云都是obj点云文件时,data/res文件夹里面会有这些文件

如果不加入我的可视化代码,只是修改了头文件以及参数,那么只会产生配准完成的文件m0reg_pc.obj和m0trans.txt,但是由于我的可视化代码,是把文件都转换成ply再进行可视化,所以会多出三个ply文件,m0Resultreg_pc.ply对应的就是 m0reg_pc.obj,m0Sourcereg_pc.ply对应的就是输入点云,m0Targetreg_pc.ply对应的就是目标点云。

 当你目标点云和输入点云都是ply点云文件时,data/res2文件夹里面会有这些文件

由于生成的是ply文件,我的可视化代码并不会对其做出改变

结果 :

这是一组obj点云文件

配准前,为了方便观察,我用鼠标转了个方向

配准后,为了方便观察,我用鼠标转了个方向

 

 这是一组ply点云文件

配准前

配准后

 

感觉效果不错,你可以尝试选用其他ICP方法进行配准 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C Primer Plus第六版 学习笔记】 第十七章 高级数据表示

有基础&#xff0c;进阶用&#xff0c;个人查漏补缺 链表&#xff1a;假设要编写一个程序&#xff0c;让用户输入一年内看过的所有电影&#xff0c;要储存每部影片的片名和评级。 #include <stdio.h> #include <stdlib.h> /* 提供malloc()的原型 */ #include <s…

​LeetCode解法汇总106. 从中序与后序遍历序列构造二叉树

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 给定两个…

jetson是什么?

jetson是NVIDIA推出的一系列嵌入式计算板&#xff0c;用于边缘计算。这些板卡专门设计用于加速机器学习和计算机视觉等人工智能应用&#xff0c;它们是低功耗设备&#xff0c;非常适合在无人机、机器人、智能相机和其他自动化设备中使用。 Jetson板卡包括多种型号&#xff0c;…

前端知识复习

1.symbol类型 Symbol 是 ECMAScript 6 中引入的一种新的基本数据类型&#xff0c;它表示独一无二的值。Symbol 值是通过 Symbol() 函数创建的。 Symbol 值具有以下特点&#xff1a; 独一无二性&#xff08;唯一性&#xff09;&#xff1a;每个通过 Symbol() 函数创建的 Symb…

2024 年,向量数据库的性能卷到什么程度了?

对于数据库&#xff08;尤其是向量数据库&#xff09;而言&#xff0c;“性能”是一个十分关键的指标&#xff0c;其用于衡量数据库是否能够在有限资源内&#xff0c;高效处理大量用户请求。对于向量数据库用户而言&#xff0c;尽管可能在某些情况下对延时的要求不高&#xff0…

模板(类模板)---C++

模板目录 2.类模板2.1 类模板语法2.2 类模板与函数模板区别2.3 类模板中成员函数创建时机2.4 类模板对象做函数参数2.5 类模板与继承2.6 类模板成员函数类外实现2.7 类模板分文件编写2.8 类模板与友元2.9 类模板案例 2.类模板 2.1 类模板语法 类模板作用&#xff1a; 建立一个…

pikachu靶场-XSS

XSS&#xff1a; XSS&#xff08;跨站脚本&#xff09;概述 Cross-Site Scripting 简称为“CSS”&#xff0c;为避免与前端叠成样式表的缩写"CSS"冲突&#xff0c;故又称XSS。一般XSS可以分为如下几种常见类型&#xff1a; 1.反射性XSS; 2.存储型XSS; 3.DOM型XSS; …

猫头虎分享已解决Bug || Spring Error: Request method ‘POST‘ not supported

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

上海交大研究团队成功开发可读大模型指纹 /苹果连续17年蝉联全球最受赞赏公司榜首 |魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; &#x1f525;资讯预览 上海交大研究团队成功开发可读大模型指纹&#xff0c;实现模型血统识别 苹果…

C++从入门到精通 第十七章(终极案例)

写在前面&#xff1a; 本系列专栏主要介绍C的相关知识&#xff0c;思路以下面的参考链接教程为主&#xff0c;大部分笔记也出自该教程&#xff0c;笔者的原创部分主要在示例代码的注释部分。除了参考下面的链接教程以外&#xff0c;笔者还参考了其它的一些C教材&#xff08;比…

SD-WAN如何降低运维成本、简化运维工作?

在当今数字化浪潮中&#xff0c;企业对网络的需求愈发迫切&#xff0c;要求网络在安全性、可靠性和灵活性方面都能够得到保障。然而&#xff0c;随着企业上云和远程办公等需求的不断增加&#xff0c;传统的WAN网络已经难以满足企业的多样化需求&#xff0c;所需的运维成本也越来…

数据结构之链表经典算法QJ题目

目录 单链表经典算法题目1. 单链表相关经典算法OJ题&#xff1a;移除链表元素思路一&#xff1a;思路二&#xff1a; 2. 单链表相关经典算法QI题&#xff1a;链表的中间节点思路一思路二 3. 单链表相关经典算法QJ题&#xff1a;反转链表思路一思路二 4. 单链表相关经典算法QJ题…

微信小程序 ---- 慕尚花坊 项目初始化

目录 项目介绍 01. 项目概述 02. 项目演示 03. 项目技术栈 04. 接口文档 申请开发权限 项目初始化 01. 创建项目与项目初始化 02. 自定义构建 npm 集成Sass 03. 集成项目页面文件 04. VsCode 开发小程序项目 项目介绍 01. 项目概述 [慕尚花坊] 是一款 同城鲜花订购…

文心一言 VS 讯飞星火 VS chatgpt (199)-- 算法导论15.2 1题

一、用go语言&#xff0c;对矩阵规模序列(5&#xff0c;10&#xff0c;3&#xff0c;12&#xff0c;5&#xff0c;50&#xff0c;6)&#xff0c;求矩阵链最优括号化方案。 文心一言&#xff0c;代码正常运行&#xff1a; 在Go语言中&#xff0c;为了找到矩阵链乘法的最优括号…

【鸿蒙 HarmonyOS 4.0】TypeScript开发语言

一、背景 HarmonyOS 应用的主要开发语言是 ArkTS&#xff0c;它由 TypeScript&#xff08;简称TS&#xff09;扩展而来&#xff0c;在继承TypeScript语法的基础上进行了一系列优化&#xff0c;使开发者能够以更简洁、更自然的方式开发应用。值得注意的是&#xff0c;TypeScrip…

普中51单片机学习(串口通信)

串口通信 原理 计算机通信是将计算机技术和通信技术的相结合&#xff0c;完成计算机与外部设备或计算机与计算机之间的信息交换 。可以分为两大类&#xff1a;并行通信与串行通信。并行通信通常是将数据字节的各位用多条数据线同时进行传送 。控制简单、传输速度快&#xff1…

QT-Day3

思维导图 作业 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码…

minium-小程序自动化测试框架

提起 UI 自动化测试&#xff0c;web 端常用 Selenium&#xff0c;手机端常用 Appium&#xff0c;那么很火的微信小程序可以用什么工具来进行自动化测试&#xff1f;本篇将介绍一款专门用于微信小程序的自动化测试工具 - minium。 简介 minium 是为小程序专门开发的自动化框架…

职业技能鉴定服务中心前端静态页面(官网+证书查询)

有个朋友想做职业技能培训&#xff0c;会发证书&#xff0c;证书可以在自己网站可查。想做一个这样的网站&#xff0c;而且要特别土&#xff0c;一眼看上去像xxx官方网站&#xff0c;像jsp .net技术开发的网站。用htmlcssjquery还原了这样子一个前端页面&#xff0c;这里分享给…

字节一面 : post为什么会发送两次请求?

同源策略 在浏览器中&#xff0c;内容是很开放的&#xff0c;任何资源都可以接入其中&#xff0c;如 JavaScript 文件、图片、音频、视频等资源&#xff0c;甚至可以下载其他站点的可执行文件。 但也不是说浏览器就是完全自由的&#xff0c;如果不加以控制&#xff0c;就会出…