使用 PCL(Point Cloud Library)检测车辆的 **3D 边界框(3D Bounding Box)** 是一个常见的任务,通常用于自动驾驶、目标检测等场景。以下是实现步骤和代码示例:
---
### 1. **实现步骤**
1. **点云预处理**:
- 去除地面点(如使用 RANSAC 平面分割)。
- 去除离群点(如统计滤波)。
2. **点云聚类**:
- 使用欧几里得聚类将点云分割成多个物体。
3. **计算 3D 边界框**:
- 对每个聚类,计算其最小外接矩形(OBB 或 AABB)。
- 使用 PCA 计算点云的主方向,确定 OBB 的方向。
4. **可视化**:
- 使用 PCL 可视化点云和 3D 边界框。
---
### 2. **代码实现**
以下是一个完整的代码示例,展示如何检测车辆的 3D 边界框并可视化:
---
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/common/pca.h>
#include <pcl/visualization/pcl_visualizer.h>
// 定义点云类型
typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;
// 检测车辆的 3D 边界框
void detectVehicleBoundingBoxes(const PointCloudT::Ptr& cloud) {
// 1. 去除地面点
pcl::SACSegmentation<PointT> seg;
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_PLANE);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setDistanceThreshold(0.2); // 地面分割的距离阈值
pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
seg.setInputCloud(cloud);
seg.segment(*inliers, *coefficients);
pcl::ExtractIndices<PointT> extract;
PointCloudT::Ptr ground_removed_cloud(new PointCloudT);
extract.setInputCloud(cloud);
extract.setIndices(inliers);
extract.setNegative(true); // 移除地面点
extract.filter(*ground_removed_cloud);
// 2. 去除离群点
pcl::StatisticalOutlierRemoval<PointT> sor;
sor.setInputCloud(ground_removed_cloud);
sor.setMeanK(50); // 邻居点数
sor.setStddevMulThresh(1.0); // 标准差倍数
PointCloudT::Ptr filtered_cloud(new PointCloudT);
sor.filter(*filtered_cloud);
// 3. 点云聚类
pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>);
tree->setInputCloud(filtered_cloud);
pcl::EuclideanClusterExtraction<PointT> ec;
ec.setClusterTolerance(0.5); // 聚类距离阈值
ec.setMinClusterSize(100); // 最小聚类点数
ec.setMaxClusterSize(25000); // 最大聚类点数
ec.setSearchMethod(tree);
ec.setInputCloud(filtered_cloud);
std::vector<pcl::PointIndices> cluster_indices;
ec.extract(cluster_indices);
// 4. 计算每个聚类的 3D 边界框
pcl::visualization::PCLVisualizer viewer("3D Bounding Boxes");
viewer.addPointCloud<PointT>(filtered_cloud, "cloud");
int cluster_id = 0;
for (const auto& indices : cluster_indices) {
// 提取当前聚类的点云
PointCloudT::Ptr cluster_cloud(new PointCloudT);
pcl::copyPointCloud(*filtered_cloud, indices, *cluster_cloud);
// 计算 PCA
pcl::PCA<PointT> pca;
pca.setInputCloud(cluster_cloud);
Eigen::Matrix3f eigen_vectors = pca.getEigenVectors(); // 特征向量
// 将点云转换到主成分坐标系
Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();
transform.block<3, 3>(0, 0) = eigen_vectors.transpose();
PointCloudT::Ptr transformed_cloud(new PointCloudT);
pcl::transformPointCloud(*cluster_cloud, *transformed_cloud, transform);
// 计算 OBB 的尺寸
PointT min_point, max_point;
pcl::getMinMax3D(*transformed_cloud, min_point, max_point);
Eigen::Vector3f obb_size = max_point.getVector3fMap() - min_point.getVector3fMap();
// 计算 OBB 的中心(在主成分坐标系中)
Eigen::Vector3f obb_center = (max_point.getVector3fMap() + min_point.getVector3fMap()) / 2.0f;
// 将 OBB 中心转换回原始坐标系
Eigen::Vector3f obb_center_original = eigen_vectors * obb_center;
// 添加 3D 边界框
std::string box_id = "box_" + std::to_string(cluster_id++);
viewer.addCube(obb_center_original, Eigen::Quaternionf(eigen_vectors),
obb_size.x(), obb_size.y(), obb_size.z(), box_id);
viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,
static_cast<float>(rand()) / RAND_MAX,
static_cast<float>(rand()) / RAND_MAX,
static_cast<float>(rand()) / RAND_MAX,
box_id);
viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME,
box_id);
}
// 显示
while (!viewer.wasStopped()) {
viewer.spinOnce(100);
}
}
int main() {
// 创建一个点云对象
PointCloudT::Ptr cloud(new PointCloudT);
// 填充点云数据(示例:模拟车辆点云)
for (float x = -1.0; x <= 1.0; x += 0.05) {
for (float y = -1.0; y <= 1.0; y += 0.05) {
for (float z = 0.0; z <= 2.0; z += 0.05) {
cloud->push_back(PointT(x, y, z));
}
}
}
// 检测车辆的 3D 边界框
detectVehicleBoundingBoxes(cloud);
return 0;
}
### 3. **代码说明**
1. **地面去除**:
- 使用 RANSAC 平面分割去除地面点。
2. **离群点去除**:
- 使用统计滤波去除噪声点。
3. **点云聚类**:
- 使用欧几里得聚类将点云分割成多个物体。
4. **3D 边界框计算**:
- 对每个聚类,使用 PCA 计算主方向,确定 OBB 的方向和尺寸。
5. **可视化**:
- 使用 `pcl::visualization::PCLVisualizer` 可视化点云和 3D 边界框。