如何使用PCL将XYZRGB点云转换为彩色mesh模型
如何使用PCL將XYZRGB點(diǎn)云轉(zhuǎn)換為彩色mesh模型
最近完成了一個(gè)使用RGBD傳感器,構(gòu)建物體模型的小demo。其中有點(diǎn)難的最后一步是如何將獲得的物體點(diǎn)云變成彩色mesh模型。效果圖如下(從點(diǎn)云變成彩色mesh):
其實(shí)整體的步驟可以總結(jié)如下:
[1]計(jì)算點(diǎn)云法向,并將法向量指向內(nèi)部
[2]將點(diǎn)云法向信息疊加在原點(diǎn)云上,生成pcl::PointXYZRGBNormal格式的點(diǎn)云
[3]使用泊松重建(poisson reconstruction)建立無(wú)顏色mesh。
[4]使用kdtree將原點(diǎn)云的信息映射在無(wú)顏色mesh上,并生成彩色mesh。
下面具體介紹一下各步驟:
[1]計(jì)算點(diǎn)云法向
使用pcl::NormalEstimationOMP設(shè)定法向估算對(duì)象。這里使用的算法實(shí)質(zhì)上是主成分分析(PCA)。先設(shè)定每個(gè)點(diǎn)周?chē)x取的臨近點(diǎn)數(shù)和搜索半徑,并用臨近點(diǎn)建立一個(gè)協(xié)方差矩陣C。
這里K指的是離點(diǎn)P的最近的K個(gè)點(diǎn),是最近鄰的中心。后面的式子就是特征值和特征向量了。而C的最小特征值對(duì)應(yīng)的特征向量就是該點(diǎn)的法向量。
但是泊松重建需要的是指向物體內(nèi)部的法向量,所以我們還要將向量反轉(zhuǎn)過(guò)來(lái)。
[2]將點(diǎn)云法向信息疊加在原點(diǎn)云上,生成pcl::PointXYZRGBNormal格式的點(diǎn)云
這一步比較簡(jiǎn)單,使用的是pcl::concatenateFields,可以將兩種不同格式的點(diǎn)云組合起來(lái)。
[3]使用泊松重建(poisson reconstruction)建立無(wú)顏色mesh。
泊松重建的原理比較復(fù)雜,我也沒(méi)有完全弄清楚。先留下這個(gè)坑,以后清楚了再填。大致的算法如下:
1、為點(diǎn)云設(shè)定八叉樹(shù)搜索索引,使得每個(gè)采樣點(diǎn)都落在深度為D的葉節(jié)點(diǎn)。
2、設(shè)定函數(shù)空間。
3、創(chuàng)建向量場(chǎng)。這一步我理解就是用到了之前算出的法向量。
4、求解泊松方程。
5、提取等值面,從而得到重建表面。
pcl中對(duì)應(yīng)的是pcl::Poisson可以設(shè)定泊松處理對(duì)象。但是泊松重建后生成的mesh是沒(méi)有RGB信息的。目前,PCL官方也說(shuō)泊松重建不帶有顏色信息,需要我們自己添加。
[4]使用kdtree將原點(diǎn)云的信息映射在無(wú)顏色mesh上,并生成彩色mesh。
這一步在網(wǎng)上基本找不到信息,所以我研究了一下。最終還是在一個(gè)user mailing的回答里得到了啟發(fā)。我們可以使用kd tree在原RGB點(diǎn)云上建立一個(gè)搜索索引,然后對(duì)mesh上的每一個(gè)點(diǎn)都搜索對(duì)應(yīng)原RGB點(diǎn)云上的臨近點(diǎn)。然后求得這些對(duì)應(yīng)臨近點(diǎn)的RGB通道均值,作為mesh上那個(gè)點(diǎn)的顏色信息就好了。
這里值得注意的是兩個(gè)轉(zhuǎn)換函數(shù)pcl::fromPCLPointCloud2和pcl::toPCLPointCloud2,其作用分別是將mesh的信息轉(zhuǎn)為點(diǎn)云和將點(diǎn)云信息轉(zhuǎn)進(jìn)mesh。
最后,就放一下源碼啦。
void poisson_reconstruction(pcl::PointCloud<pcl::PointXYZRGBA>::Ptr object_cloud) {PointCloud<PointXYZRGB>::Ptr cloud(new PointCloud<PointXYZRGB>());pcl::copyPointCloud(*object_cloud, *cloud);PointCloud<PointXYZRGB>::Ptr filtered(new PointCloud<PointXYZRGB>());PassThrough<PointXYZRGB> filter;filter.setInputCloud(cloud);filter.filter(*filtered);cout << "passthrough filter complete" << endl;cout << "begin normal estimation" << endl;NormalEstimationOMP<PointXYZRGB, Normal> ne;//計(jì)算點(diǎn)云法向ne.setNumberOfThreads(8);//設(shè)定臨近點(diǎn)ne.setInputCloud(filtered);ne.setRadiusSearch(0.01);//設(shè)定搜索半徑Eigen::Vector4f centroid;compute3DCentroid(*filtered, centroid);//計(jì)算點(diǎn)云中心ne.setViewPoint(centroid[0], centroid[1], centroid[2]);//將向量計(jì)算原點(diǎn)置于點(diǎn)云中心PointCloud<Normal>::Ptr cloud_normals (new PointCloud<Normal>());ne.compute(*cloud_normals);cout << "normal estimation complete" << endl;cout << "reverse normals' direction" << endl;//將法向量反向for(size_t i = 0; i < cloud_normals->size(); ++i){cloud_normals->points[i].normal_x *= -1;cloud_normals->points[i].normal_y *= -1;cloud_normals->points[i].normal_z *= -1;}//融合RGB點(diǎn)云和法向cout << "combine points and normals" << endl;PointCloud<PointXYZRGBNormal>::Ptr cloud_smoothed_normals(new PointCloud<PointXYZRGBNormal>());concatenateFields(*filtered, *cloud_normals, *cloud_smoothed_normals);//泊松重建cout << "begin poisson reconstruction" << endl;Poisson<PointXYZRGBNormal> poisson;//poisson.setDegree(2);poisson.setDepth(8);poisson.setSolverDivide (6);poisson.setIsoDivide (6);poisson.setConfidence(false); poisson.setManifold(false); poisson.setOutputPolygons(false); poisson.setInputCloud(cloud_smoothed_normals);PolygonMesh mesh;poisson.reconstruct(mesh);cout << "finish poisson reconstruction" << endl;//給mesh染色PointCloud<PointXYZRGB> cloud_color_mesh; pcl::fromPCLPointCloud2(mesh.cloud, cloud_color_mesh); pcl::KdTreeFLANN<pcl::PointXYZRGB> kdtree;kdtree.setInputCloud (cloud);// K nearest neighbor searchint K = 5;std::vector<int> pointIdxNKNSearch(K);std::vector<float> pointNKNSquaredDistance(K);for(int i=0;i<cloud_color_mesh.points.size();++i){uint8_t r = 0;uint8_t g = 0;uint8_t b = 0;float dist = 0.0; int red = 0;int green = 0;int blue = 0;uint32_t rgb;if ( kdtree.nearestKSearch (cloud_color_mesh.points[i], K, pointIdxNKNSearch, pointNKNSquaredDistance) > 0 ){for (int j = 0; j < pointIdxNKNSearch.size (); ++j) { r = cloud->points[ pointIdxNKNSearch[j] ].r;g = cloud->points[ pointIdxNKNSearch[j] ].g;b = cloud->points[ pointIdxNKNSearch[j] ].b;red += int(r);green += int(g);blue += int(b);dist += 1.0/pointNKNSquaredDistance[j]; std::cout<<"red: "<<int(r)<<std::endl; std::cout<<"green: "<<int(g)<<std::endl; std::cout<<"blue: "<<int(b)<<std::endl; cout<<"dis:"<<dist<<endl;} }cloud_color_mesh.points[i].r = int(red/pointIdxNKNSearch.size ()+0.5); cloud_color_mesh.points[i].g = int(green/pointIdxNKNSearch.size ()+0.5); cloud_color_mesh.points[i].b = int(blue/pointIdxNKNSearch.size ()+0.5);}toPCLPointCloud2(cloud_color_mesh, mesh.cloud);io::savePLYFile("/home/xxx/3d_model_building/src/make_model/result/object_mesh.ply", mesh); }
如有問(wèn)題,還請(qǐng)大家指出。
參考資料:
[1]http://blog.csdn.net/qq_25491201/article/details/51100073
[2]http://blog.csdn.net/jennychenhit/article/details/52126156?locationNum=8
[3]https://wenku.baidu.com/view/23c5637a31b765ce050814d2.html
總結(jié)
以上是生活随笔為你收集整理的如何使用PCL将XYZRGB点云转换为彩色mesh模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 警方通报西北工业大学遭境外黑客攻击,敲响
- 下一篇: 一篇文章让你学会绘画十一种常见数据分析图