关于PCL读取OBJ文件到Texture并可视化的问题


文章目录

  • 问题描述:
  • 问题所在:
  • 解决问题的历程
    • Round1
    • Round2
    • Round3
  • 解决方案
  • 注意事项
    • 解决方法不完美
    • 包含头文件的问题


类似的问题有:

  1. loadOBJFile()不能读取Texture
  2. loadPolygonFIleOBJ()读取不到tex_materials
  3. PCL可视化OBJ文件,纹理贴图

最近做点云相关的事情,然而就PCL自带函数读取OBJ文件并且显示这个事情卡了我几天,在这里记录一下,希望能帮到别人。

问题描述:

读入obj文件时,使用:
loadOBJFile( const std::string& file_name, pcl::TextureMesh &mesh)

此时,使用如下代码读取并显示:

int main(int argc,char** argv)
{
    string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
    //读取
    pcl::TextureMesh mesh;
    pcl::io::loadOBJFile(objPath, mesh);

	//显示
	boost::shared_ptrviewer(new pcl::visualization::PCLVisualizer("3D viewer A"));

	viewer->addTextureMesh(mesh,"mesh");

    while (!viewer->wasStopped ())  // 在按下 "q" 键之前一直会显示窗口
    {    
        viewer->spinOnce ();
    }
    return 0;
}

编译运行后
只显示一个窗口,里面没有任何东西,而且命令行提示:
[PCLVisualizer::addTextureMesh] No textures coordinates found!
即:没有找到纹理的坐标。

经过调试发现,loadOBJFile()能够正常读入点坐标数据,贴图文件,但不能读入每个点对应贴图文件的坐标,经过查阅资料和论坛讨论,发现这是一个PCL库的bug。

问题所在:

这是PCL库的bug,在最新版的PCL库(PCL 1.9.1)中还没有修复,PCL的github页上有人已经收集,整理提交了这个bug。详情可以点击这里查看。

解决问题的历程

这一块是流水账,想要解决方案的,直接看最后一部分。 点这里

Round1

为解决上面问题,第一时间去查看了官方文档,发现了还有另外一个函数也可以加载OBJ文档,而且看起来更符合我的目的(读取网格到PCL提供的纹理类型),这个函数就是:
loadPolygonFileOBJ(const std::string& file_name, pcl::TextureMesh &mesh)

这个函数是对另外一个函数的补充,重载了一下。功能从函数名上可以看出,就是从OBJ中读取网格数据。
于是赶紧换了函数,尝试一下:

int main(int argc,char** argv)
{
    string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
    //读取
    pcl::TextureMesh mesh;
    pcl::io::loadPolygonFileOBJ(objPath, mesh);

	//显示
	boost::shared_ptrviewer(new pcl::visualization::PCLVisualizer("3D viewer A"));

	viewer->addTextureMesh(mesh,"mesh");

    while (!viewer->wasStopped ())  // 在按下 "q" 键之前一直会显示窗口
    {    
        viewer->spinOnce ();
    }
    return 0;
}

编译运行
只显示一个窗口,里面没有任何东西,而且命令行提示:

[PCLVisualizer::textureFromTexMaterial] No texture file given for material !
[PCLVisualizer::addTextureMesh] Failed to load texture , skipping!

也就是没有纹理贴图的数据
经过调试发现,loadPolygonFileOBJ()能够读入点云数据和对应点云的纹理贴图的坐标,但不能读入贴图文件的数据。
这个函数也有bug!

Round2

接下来,就开始开脑洞了,毕竟两个函数读OBJ是数据到同一个类的对象中了,那能不能优势互补一下:
定义两个TextureMesh对象,分别是loadOBJFile()和loadPolygonFileOBJ()读到的结果,把loadPolygonFileOBJ()读到的对应点云的纹理贴图的坐标赋值给loadOBJFile(),这样,其中的一个不就完美了么?
于是,尝试一下:

int main(int argc,char** argv)
{
    string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
    //读取
  	pcl::TextureMesh mesh;
    pcl::io::loadOBJFile(objPath, mesh);
	pcl::TextureMesh mesh2;
    pcl::io::loadPolygonFileOBJ(objPath, mesh2);

	mesh.tex_coordinates = mesh2.tex_coordinates; 
	//显示
	boost::shared_ptr
	viewer(new pcl::visualization::PCLVisualizer("3D viewer A"));

	viewer->addTextureMesh(mesh,"mesh");

    while (!viewer->wasStopped ())  // 在按下 "q" 键之前一直会显示窗口
    {    
        viewer->spinOnce ();
    }
    return 0;
}

编译运行后
终于显示模型了,但贴图全贴错了
经过查阅发现,PCL库的贴图坐标点的按照顺序读入的,而不是和点云的坐标点做关联。即如果点云点和贴图坐标点不是同时读入的话,极有可能出现贴图坐标点和点云坐标对应错误,那么最后渲染出来的3D模型的纹理贴图也就是错误的。

在这次测试中,loadOBjFile()和loadPolygonFileOBJ()读入点云和坐标点的方式不一样,导致不能正常显示。

Round3

经过上面的测试,最终的方案也就基本形成了:
两个函数分别读到的数据:
loadOBJFile( const std::string& file_name, pcl::TextureMesh &mesh)
点坐标数据,贴图文件
loadPolygonFileOBJ(const std::string& file_name, pcl::TextureMesh &mesh)
点云数据,点云坐标对应的纹理贴图的坐标

因为loadPolygonFileOBJ这个函数读到的点云坐标和纹理贴图坐标相互对于,那么,将loadOBJFile()中的纹理贴图数据赋值给loadPolygonFileOBJ()的变量即可:
测试一下:


int main(int argc,char** argv)
{
    string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
    //读取
  	pcl::TextureMesh mesh;
    pcl::io::loadOBJFile(objPath, mesh);
	pcl::TextureMesh mesh2;
    pcl::io::loadPolygonFileOBJ(objPath, mesh2);

	mesh2.tex_materials= mesh.tex_materials;
	
	//显示
	boost::shared_ptr
	viewer(new pcl::visualization::PCLVisualizer("3D viewer A"));

	viewer->addTextureMesh(mesh2,"mesh");

    while (!viewer->wasStopped ())  // 在按下 "q" 键之前一直会显示窗口
    {    
        viewer->spinOnce ();
    }
    return 0;
}

完美显示!

解决方案

  1. 用loadPolygenFileOBJ()读点数据和点对应纹理贴图的坐标数据;
    pcl::TextureMesh mesh;
    pcl::io::loadPolygonFileOBJ(objPath,mesh);
    
  2. 用loadOBJFile()读取点数据和纹理贴图文件数据;
    pcl::TextureMesh mesh2;
    pcl::io::loadOBJFile(objPath,mesh2);
    
  3. 将mesh2中的纹理贴图文件数据拷贝给mesh中:
    mesh.tex_materials= mesh2.tex_materials;
  4. 显示mesh
    boost::shared_ptr 
    viewer(new pcl::visualization::PCLVisualizer("3D viewer A"));    
    viewer->addTextureMesh(mesh,"mesh");
    
    这样就可以显示obj文件了。
    想要代码的话,上文中已经提到过,点击这里

注意事项

解决方法不完美

这里的解决方法是简单粗暴的,是不完美的。
我测试通过的只是当纹理贴图只有一张的时候,至于多张纹理贴图是否能够正确显示,我没有做过测试。所以不敢妄言解决

最后,最好的方式还是等PCL修复上面这些bug。

包含头文件的问题

虽然这两个函数都是pcl::io命名空间中定义的,但在不同的头文件中。

loadOBjFile()的头文件比较好找,函数说明下面就有:
关于PCL读取OBJ文件到Texture并可视化的问题_第1张图片
loadPolygonFileOBJ()下面没有:
关于PCL读取OBJ文件到Texture并可视化的问题_第2张图片
网上也找不到资料,最后还是在ros的官方文档中找到蛛丝马迹,顺藤摸瓜,在PCL源码的 vtk_lib_io.h 中找到了这个函数的定义:
关于PCL读取OBJ文件到Texture并可视化的问题_第3张图片


完结撒花~


你可能感兴趣的:(SLAM)