问题描述
我在使用VTK和PCL进行点云可视化时,遇到一个问题:我加载点云数据后,VTK界面没有显示点云数据,我需要缩放一下,才会显示出点云,我搜索了一下,说是相机初始化位置和角度的问题,但是我觉得如果你只是缩放一下就能看到,就不是相机视角的原因吧 。然后我缩放后能看到后,我又去加载了一些点,这时候,如果不缩放,还是继续看不到,缩放后才会看到,这个其实就很奇怪,我第二次加载的点就是第一次视角下附近的点,但是为啥不缩放也看不到,然后我问了ai,然后给了我回答。
根本原因分析
经过分析,问题的根本原因主要有以下几点:
- 相机初始化问题:VTK默认的相机位置和视角可能不适合当前点云的尺度
- 边界框计算缺失:没有自动计算点云的边界框,导致相机不知道点云的范围
- 多数据源问题 :当同时加载多个点云时,
resetCamera()可能只考虑了部分点云
解决方案
针对以上问题,我们可以通过以下步骤解决:
1. 计算点云边界框
首先,我们需要计算点云的边界框,获取点云的最小和最大坐标值:
cpp
// 计算点云边界框
pcl::PointXYZ minPt, maxPt;
pcl::getMinMax3D(*cloud, minPt, maxPt);
2. 计算中心点和尺度
根据边界框计算点云的中心点和整体尺度:
cpp
// 计算中心点
double centerX = (minPt.x + maxPt.x) / 2.0;
double centerY = (minPt.y + maxPt.y) / 2.0;
double centerZ = (minPt.z + maxPt.z) / 2.0;
// 计算点云尺度(最大维度)
double size = std::max({maxPt.x - minPt.x, maxPt.y - minPt.y, maxPt.z - minPt.z});
3. 设置相机位置
根据计算得到的中心点和尺度,设置相机位置:
cpp
// 设置相机位置:在点云中心的正上方,距离为尺度的2倍
view->setCameraPosition(centerX, centerY, centerZ + size * 2,
centerX, centerY, centerZ, 0, 1, 0);
4. 重置相机
最后,调用resetCamera()确保所有点都在视野范围内:
cpp
view->resetCamera();
ui.openGLWidget->update();
完整代码实现
1. 加载PCD文件时的处理
cpp
void VTK930::onOpen() {
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZ>());
QString fileName = QFileDialog::getOpenFileName(this,
QStringLiteral("打开点云"), ".",
QStringLiteral("PCD文件(*.pcd)"));
if (fileName == "") return;
pcl::io::loadPCDFile(fileName.toStdString(), *cloud);
if (cloud->empty()) {
QMessageBox::warning(this, QStringLiteral("警告"),
QStringLiteral("点云文件为空!"));
return;
}
view->removePointCloud("cloud");
view->addPointCloud(cloud, "cloud");
// 计算点云边界框并设置相机
pcl::PointXYZ minPt, maxPt;
pcl::getMinMax3D(*cloud, minPt, maxPt);
double centerX = (minPt.x + maxPt.x) / 2.0;
double centerY = (minPt.y + maxPt.y) / 2.0;
double centerZ = (minPt.z + maxPt.z) / 2.0;
double size = std::max({maxPt.x - minPt.x, maxPt.y - minPt.y, maxPt.z - minPt.z});
view->setCameraPosition(centerX, centerY, centerZ + size * 2, centerX, centerY, centerZ, 0, 1, 0);
view->resetCamera();
ui.openGLWidget->update();
}
2. 添加点到可视化时的处理
cpp
void VTK930::addPointsToVisualizer(const std::vector<Point3D>& points, int r, int g, int b, const std::string& groupName) {
// ... 点云添加代码 ...
view->removePointCloud(groupName);
view->addPointCloud(cloud, groupName);
view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,
(double)r / 255.0, (double)g / 255.0, (double)b / 255.0, groupName);
// 计算所有点云的边界框并设置相机
double minX = std::numeric_limits<double>::max(), minY = std::numeric_limits<double>::max(), minZ = std::numeric_limits<double>::max();
double maxX = std::numeric_limits<double>::lowest(), maxY = std::numeric_limits<double>::lowest(), maxZ = std::numeric_limits<double>::lowest();
bool hasPoints = false;
for (const auto& pair : groupClouds) {
if (!pair.second->empty()) {
hasPoints = true;
for (const auto& pt : pair.second->points) {
minX = std::min(minX, (double)pt.x);
minY = std::min(minY, (double)pt.y);
minZ = std::min(minZ, (double)pt.z);
maxX = std::max(maxX, (double)pt.x);
maxY = std::max(maxY, (double)pt.y);
maxZ = std::max(maxZ, (double)pt.z);
}
}
}
if (hasPoints) {
double centerX = (minX + maxX) / 2.0;
double centerY = (minY + maxY) / 2.0;
double centerZ = (minZ + maxZ) / 2.0;
double size = std::max({maxX - minX, maxY - minY, maxZ - minZ});
view->setCameraPosition(centerX, centerY, centerZ + size * 2, centerX, centerY, centerZ, 0, 1, 0);
}
view->resetCamera();
ui.openGLWidget->update();
}
结果
修改后的代码,确实没有之前的情况了。
上面的完整代码是我项目的代码,你需要按需修改。