原点云如图所示:
外侧轮廓提取用的方法是经纬线扫描法,全轮廓提取的方法是alpha shapes算法(点云边界提取方法总结),从全轮廓中剔除外侧轮廓得到内侧轮廓。
alpha shapes算法轮廓提取结果:
经纬线扫描法轮廓提取结果:
内侧轮廓:
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/surface/concave_hull.h>
void BoundaryExtraction1(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_boundary, int resolution)
{
pcl::PointXYZ px_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });
pcl::PointXYZ px_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });
float delta_x = (px_max.x - px_min.x) / resolution;
float min_y = INT_MAX, max_y = -INT_MAX;
std::vector<int> indexs_x(2 * resolution);
std::vector<std::pair<float, float>> minmax_x(resolution, { INT_MAX,-INT_MAX });
for (size_t i = 0; i < cloud->size(); ++i)
{
int id = (cloud->points[i].x - px_min.x) / delta_x;
if (cloud->points[i].y < minmax_x[id].first)
{
minmax_x[id].first = cloud->points[i].y;
indexs_x[id] = i;
}
else if (cloud->points[i].y > minmax_x[id].second)
{
minmax_x[id].second = cloud->points[i].y;
indexs_x[id + resolution] = i;
}
}
pcl::PointXYZ py_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });
pcl::PointXYZ py_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });
float delta_y = (py_max.y - py_min.y) / resolution;
float min_x = INT_MAX, max_x = -INT_MAX;
std::vector<int> indexs_y(2 * resolution);
std::vector<std::pair<float, float>> minmax_y(resolution, { INT_MAX,-INT_MAX });
for (size_t i = 0; i < cloud->size(); ++i)
{
int id = (cloud->points[i].y - py_min.y) / delta_y;
if (cloud->points[i].x < minmax_y[id].first)
{
minmax_y[id].first = cloud->points[i].x;
indexs_y[id] = i;
}
else if (cloud->points[i].x > minmax_y[id].second)
{
minmax_y[id].second = cloud->points[i].x;
indexs_y[id + resolution] = i;
}
}
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xboundary(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*cloud, indexs_x, *cloud_xboundary);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_yboundary(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*cloud, indexs_y, *cloud_yboundary);
*cloud_boundary = *cloud_xboundary + *cloud_yboundary;
}
void BoundaryExtraction2(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_boundary, double alpha)
{
pcl::ConcaveHull<pcl::PointXYZ> chull;
chull.setInputCloud(cloud);
chull.setAlpha(alpha);
chull.reconstruct(*cloud_boundary);
}
int main(int argc, char* argv[])
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary1(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary2(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary3(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("test.pcd", *cloud);
BoundaryExtraction1(cloud, cloud_boundary1, 200); //经纬线扫描法
pcl::io::savePCDFile("boundary1.pcd", *cloud_boundary1);
BoundaryExtraction2(cloud, cloud_boundary2, 3); //alpha shapes算法
pcl::io::savePCDFile("boundary2.pcd", *cloud_boundary2);
//求cloud_boundary2-cloud_boundary1
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
kdtree->setInputCloud(cloud_boundary1);
std::vector<int> pointIdxRadiusSearch;
std::vector<float> pointRadiusSquaredDistance;
std::vector<int> indices;
float radius = 3;
for (size_t i = 0; i < cloud_boundary2->size(); i++)
{
if (kdtree->radiusSearch(cloud_boundary2->points[i], radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0)
{
indices.push_back(i);
}
}
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud3(new pcl::PointCloud<pcl::PointXYZ>);
for (size_t i = 0; i < cloud_boundary2->size(); i++)
{
if (find(indices.begin(), indices.end(), i) == indices.end())
cloud_boundary3->push_back(cloud_boundary2->points[i]);
}
pcl::io::savePCDFile("boundary3.pcd", *cloud_boundary3);
return EXIT_SUCCESS;
}