网上大部分寻找重叠区域都是对一个点云建立kdtree,然后在r半径内搜索另外一个点云的点。这种方法适合两个点云完全一样。一般的点云数据并不完全一样,例如两条航带的点云,并不完全相同,如果应用这方法会损失很多点,造成特征计算的不准确性。下面介绍两种方法:
写在前面的结论:第二种方法速度和精度上均优于第一种方法(octree yyds!!!)
方法1:
(1)假设有两组点云A,B,计算A的OBB包围盒
(2)寻找点云A的OBB包围盒内的点云B,这样就得到点云B相对A的重叠区域
(3)反之计算,得到点云A相对于B的重叠区域
(4)综上可以得到一组重叠区域的点云,可用于后续分析。
show the codes
// 提取点云重叠区域.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <pcl/features/moment_of_inertia_estimation.h>
#include <vector>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include<pcl/filters/crop_box.h>
#include <pcl/common/transforms.h>
#include<pcl/common/common.h>
//获取第二个点云相对第一个点云的重叠区域
void getOverlappedCloud(const pcl::PointCloud<pcl::PointXYZ> &cloud, const pcl::PointCloud<pcl::PointXYZ>&cloud2, pcl::PointCloud<pcl::PointXYZ>&overlapped_cloud2)
{
pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;
feature_extractor.setInputCloud(cloud.makeShared());
feature_extractor.compute();
pcl::PointXYZ min_point_AABB;
pcl::PointXYZ max_point_AABB;
pcl::PointXYZ min_point_OBB;
pcl::PointXYZ max_point_OBB;
pcl::PointXYZ position_OBB;
Eigen::Matrix3f rotational_matrix_OBB;
feature_extractor.getAABB(min_point_AABB, max_point_AABB);
feature_extractor.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
//将点云旋转,使得第一个点云的OBB=AABB,以此获得第一个点云OBB区域内的第二个点云
Eigen::Matrix4f tf = Eigen::Matrix4f::Identity();
tf << (rotational_matrix_OBB.inverse());
pcl::PointCloud<pcl::PointXYZ>cloud_tf, cloud2_tf;
pcl::transformPointCloud(cloud, cloud_tf, tf);
pcl::transformPointCloud(cloud2, cloud2_tf, tf);
Eigen::Vector4f pmin, pmax;
pcl::getMinMax3D(cloud_tf, pmin, pmax);
pcl::PointCloud<pcl::PointXYZ> tf_overlapped_cloud2;
std::vector<int> indices;
//第二个点云旋转之后的点云重叠区域
pcl::getPointsInBox(cloud2_tf, pmin, pmax, indices);
pcl::copyPointCloud(cloud2_tf, indices, tf_overlapped_cloud2);
//再变换到原始坐标系中
pcl::transformPointCloud(tf_overlapped_cloud2, overlapped_cloud2, tf.inverse());
std::cout << tf << std::endl;
std::cout << rotational_matrix_OBB << std::endl;
}
int main(int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>cloud, overlapped_cloud;
pcl::io::loadPCDFile("SHCSCloud.pcd", cloud);
pcl::PointCloud<pcl::PointXYZ>cloud2,overlapped_cloud2;
pcl::io::loadPCDFile("SHCSCloud副本.pcd", cloud2);
getOverlappedCloud(cloud, cloud2, overlapped_cloud2);
getOverlappedCloud(cloud2, cloud, overlapped_cloud);
pcl::io::savePCDFile<pcl::PointXYZ>("重叠点云.pcd", overlapped_cloud);
pcl::io::savePCDFile<pcl::PointXYZ>("重叠点云2.pcd", overlapped_cloud2);
}
方法1实验效果:
原始点云:
图中白色点云为原始红色点云的重叠区域:
绿色点云为蓝色原始点云的重叠区域:
耗时:
结论:从图上看,这种方法只能粗略点云重叠区域,提取的重叠区域与实际重叠区域存在一定偏差。
方法2(c++):
(1)假设有两组点云A,B,对A建立八叉树
(2)遍历B中所有点,查询其对应的A的体素是否存在点云(即同一体素中是否同时存在AB点云),若存在,则该点为B点云的重叠点云
(3)同理,得到A中重叠点云
show the codes
// 提取点云重叠区域2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<pcl/io/pcd_io.h>
#include<pcl/point_cloud.h>
#include<pcl/point_types.h>
#include<pcl/octree/octree.h>
#include<ctime>
using point = pcl::PointXYZ;
using cloud = pcl::PointCloud<point>;
void getOverlappedCloud(const pcl::PointCloud<pcl::PointXYZ>& cloud1, const pcl::PointCloud<pcl::PointXYZ>& cloud2, pcl::PointCloud<pcl::PointXYZ>& overlapped_cloud2)
{
double radius = 3.0;
pcl::octree::OctreePointCloudSearch<point> octree(radius);
octree.setInputCloud(cloud1.makeShared());
octree.addPointsFromInputCloud();
point min_pt, max_pt;
pcl::getMinMax3D(cloud1, min_pt, max_pt);
//pcl::PointCloud<point> overlapped_2;
for (size_t i = 0; i < cloud2.size(); ++i)
{
//新增判断,防止越界
if (cloud2.points[i].x>max_pt.x|| cloud2.points[i].y > max_pt.y || cloud2.points[i].z > max_pt.z ||
cloud2.points[i].x < min_pt.x || cloud2.points[i].y < min_pt.y || cloud2.points[i].z < min_pt.z)
{
continue;
}
std::vector<int> indices;
octree.voxelSearch(cloud2.points[i], indices);
pcl::PointCloud<point> cloud_out;
if (indices.size())
{
overlapped_cloud2.push_back(cloud2.points[i]);
}
}
}
int main()
{
auto start = std::clock();
pcl::PointCloud<point> cloud1, cloud2;
pcl::io::loadPCDFile<point>("D:/1重叠.pcd", cloud1);
pcl::io::loadPCDFile<point>("D:/2重叠.pcd", cloud2);
cloud overlapped1, overlapped2;
getOverlappedCloud(cloud1, cloud2, overlapped2);
getOverlappedCloud(cloud2, cloud1, overlapped1);
pcl::io::savePCDFile("D:/11重叠.pcd", overlapped1);
pcl::io::savePCDFile("D:/22重叠.pcd", overlapped2);
auto end = std::clock();
std::cerr << "耗时" << std::difftime(end, start) << "ms" << std::endl;
std::cout << "Hello World!\n";
}
方法2(python)
import open3d as o3d
def find_overlapped_cloud(cloud1, cloud2):
overlapped_cloud_indices = []
octree = o3d.geometry.Octree(max_depth=4)
octree.convert_from_point_cloud(cloud1, size_expand=0.01)
min_pt = octree.get_min_bound()
max_pt = octree.get_max_bound()
for point in cloud2.points:
if point[0] < min_pt[0] or point[1] < min_pt[1] or point[2] < min_pt[2] or \
point[0] > max_pt[0] or point[1] > max_pt[1] or point[2] > max_pt[2]:
continue
else:
leaf_node, leaf_info = octree.locate_leaf_node(point)
if leaf_info is not None:
indices = leaf_node.indices
for indice in indices:
overlapped_cloud_indices.append(indice)
return cloud1.select_by_index(overlapped_cloud_indices)
if __name__ == '__main__':
"read cloud"
cloud1 = o3d.io.read_point_cloud("D:/1重叠.pcd")
cloud2 = o3d.io.read_point_cloud("D:/2重叠.pcd")
"find overlapped cloud"
overlapped_cloud1 = find_overlapped_cloud(cloud2, cloud1)
overlapped_cloud2 = find_overlapped_cloud(cloud1, cloud2)
"save cloud"
o3d.io.write_point_cloud("D:/o3d_overlapped1.pcd", overlapped_cloud1)
o3d.io.write_point_cloud("D:/o3d_overlapped2.pcd", overlapped_cloud2)
方法2实验效果:
黄色为重叠区域
棕色为重叠区域
耗时: