如何使用PCL将XYZRGB点云转换为彩色mesh模型
如何使用PCL將XYZRGB點云轉(zhuǎn)換為彩色mesh模型
最近完成了一個使用RGBD傳感器,構(gòu)建物體模型的小demo。其中有點難的最后一步是如何將獲得的物體點云變成彩色mesh模型。效果圖如下(從點云變成彩色mesh):
其實整體的步驟可以總結(jié)如下:
[1]計算點云法向,并將法向量指向內(nèi)部
[2]將點云法向信息疊加在原點云上,生成pcl::PointXYZRGBNormal格式的點云
[3]使用泊松重建(poisson reconstruction)建立無顏色mesh。
[4]使用kdtree將原點云的信息映射在無顏色mesh上,并生成彩色mesh。
下面具體介紹一下各步驟:
[1]計算點云法向
使用pcl::NormalEstimationOMP設(shè)定法向估算對象。這里使用的算法實質(zhì)上是主成分分析(PCA)。先設(shè)定每個點周圍選取的臨近點數(shù)和搜索半徑,并用臨近點建立一個協(xié)方差矩陣C。
這里K指的是離點P的最近的K個點,是最近鄰的中心。后面的式子就是特征值和特征向量了。而C的最小特征值對應(yīng)的特征向量就是該點的法向量。
但是泊松重建需要的是指向物體內(nèi)部的法向量,所以我們還要將向量反轉(zhuǎn)過來。
[2]將點云法向信息疊加在原點云上,生成pcl::PointXYZRGBNormal格式的點云
這一步比較簡單,使用的是pcl::concatenateFields,可以將兩種不同格式的點云組合起來。
[3]使用泊松重建(poisson reconstruction)建立無顏色mesh。
泊松重建的原理比較復(fù)雜,我也沒有完全弄清楚。先留下這個坑,以后清楚了再填。大致的算法如下:
1、為點云設(shè)定八叉樹搜索索引,使得每個采樣點都落在深度為D的葉節(jié)點。
2、設(shè)定函數(shù)空間。
3、創(chuàng)建向量場。這一步我理解就是用到了之前算出的法向量。
4、求解泊松方程。
5、提取等值面,從而得到重建表面。
pcl中對應(yīng)的是pcl::Poisson可以設(shè)定泊松處理對象。但是泊松重建后生成的mesh是沒有RGB信息的。目前,PCL官方也說泊松重建不帶有顏色信息,需要我們自己添加。
[4]使用kdtree將原點云的信息映射在無顏色mesh上,并生成彩色mesh。
這一步在網(wǎng)上基本找不到信息,所以我研究了一下。最終還是在一個user mailing的回答里得到了啟發(fā)。我們可以使用kd tree在原RGB點云上建立一個搜索索引,然后對mesh上的每一個點都搜索對應(yīng)原RGB點云上的臨近點。然后求得這些對應(yīng)臨近點的RGB通道均值,作為mesh上那個點的顏色信息就好了。
這里值得注意的是兩個轉(zhuǎn)換函數(shù)pcl::fromPCLPointCloud2和pcl::toPCLPointCloud2,其作用分別是將mesh的信息轉(zhuǎ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;//計算點云法向ne.setNumberOfThreads(8);//設(shè)定臨近點ne.setInputCloud(filtered);ne.setRadiusSearch(0.01);//設(shè)定搜索半徑Eigen::Vector4f centroid;compute3DCentroid(*filtered, centroid);//計算點云中心ne.setViewPoint(centroid[0], centroid[1], centroid[2]);//將向量計算原點置于點云中心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點云和法向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); }
如有問題,還請大家指出。
參考資料:
[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模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 警方通报西北工业大学遭境外黑客攻击,敲响
- 下一篇: OpenCV-python进行多个摄像头