【问题标题】:PCL: Scale two Point-Clouds to the same sizePCL:将两个点云缩放到相同的大小
【发布时间】:2019-12-18 15:26:59
【问题描述】:

我有两个点云并尝试将它们缩放到相同的大小。我的第一种方法是将平方根与特征值相除:

pcl::PCA<pcl::PointNormal> pca;
pca.setInputCloud(model_cloud_ptr);
Eigen::Vector3f ev_M = pca.getEigenValues();

pca.setInputCloud(segmented_cloud_ptr);
Eigen::Vector3f ev_S = pca.getEigenValues();

double s = sqrt(ev_M[0])/sqrt(ev_S[0]);

这有助于我扩展我的模型云,使其大小与我的分段云大致相同。但结果真的不是那么完美。这是一个简单的估计。我试着用TransformationEstimationSVDScaleSampleConsensusModelRegistration 来做这件事,就像this tutorial 一样。但是当我这样做时,我收到消息,源点/索引的数量与目标点/索引的数量不同。

当云中有不同数量的点时,将云缩放到相同大小的最佳方法是什么?

编辑我尝试按照@dspeyer 的建议进行操作,但这给了我几乎 1.0 的比例因子

pcl::PCA<pcl::PointNormal> pca;
pca.setInputCloud(model_cloud_ptr);
Eigen::Matrix3f ev_M = pca.getEigenVectors();
Eigen::Vector3f ev_M1 = ev_M.col(0);
Eigen::Vector3f ev_M2 = ev_M.col(1);

auto dist_M1 = ev_M1.maxCoeff()-ev_M1.minCoeff();
auto dist_M2 = ev_M2.maxCoeff()-ev_M2.minCoeff();  
auto distM_max = std::max(dist_M1, dist_M2);

pca.setInputCloud(segmented_cloud_ptr);
Eigen::Matrix3f ev_S = pca.getEigenVectors();
Eigen::Vector3f ev_S1 = ev_S.col(0);
Eigen::Vector3f ev_S2 = ev_S.col(1);

auto dist_S1 = ev_S1.maxCoeff()-ev_S1.minCoeff();
auto dist_S2 = ev_S2.maxCoeff()-ev_S2.minCoeff();
auto distS_max = std::max(dist_S1, dist_S2);

double s = distS_max / distM_max;

【问题讨论】:

  • 我猜你已经尝试简单地计算两个云的方差并均衡它们
  • 我猜如果云正确对齐并且形状有点相似,特征值方法将起作用。如何计算两个云的极值,然后在每个云中添加 8 个角点,如 (min,min,min) (max,min, min).. 等?
  • 你对“大小”有一个准确的定义吗?类似于“PCA 下降到 2d 后的相同边界框”或“同一半径球内 68% 的点”之类的东西?
  • 结果真的不是那么完美。一个数字将有助于理解它在什么意义上是不完美的
  • @dspeyer 你的第一个定义应该适合我的问题。

标签: c++ point-cloud-library image-registration


【解决方案1】:

我建议使用每个云的特征向量来识别每个主变化轴,然后根据该轴上每个云的变化对它们进行缩放。在我的示例中,我使用了一个定向边界框(本征空间中的最大最小值),但主轴(本征空间中的 x 轴)的平均值或标准偏差可能是更好的指标,具体取决于应用程序。

我在函数中留下了一些调试标志,以防它们对您有帮助,但给了它们我希望您会使用的默认值。我测试了样本和金色云的可变轴拉伸和可变旋转。这个函数应该能够处理这一切就好了。

此方法的一个警告是,如果翘曲是轴向可变的 AND 翘曲导致一个轴克服另一个轴作为主要变化轴,此函数可能会不正确地缩放云。我不确定这个边缘案例是否与您有关。只要您的云之间有统一的缩放比例,就永远不会发生这种情况。

debugFlags:debugOverlay 将使两个输入云都按比例缩放并保持各自的特征方向(允许更容易比较)。如果为 true,primaryAxisOnly 将仅使用变化的主轴来执行缩放,如果为 false,它将独立缩放所有 3 个变化轴。

功能:

void rescaleClouds(pcl::PointCloud<pcl::PointXYZ>::Ptr& goldenCloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& sampleCloud, bool debugOverlay = false, bool primaryAxisOnly = true)
{
    //analyze golden cloud
    pcl::PCA<pcl::PointXYZ> pcaGolden;
    pcaGolden.setInputCloud(goldenCloud);
    Eigen::Matrix3f goldenEVs_Dir = pcaGolden.getEigenVectors();
    Eigen::Vector4f goldenMidPt = pcaGolden.getMean();
    Eigen::Matrix4f goldenTransform = Eigen::Matrix4f::Identity();
    goldenTransform.block<3, 3>(0, 0) = goldenEVs_Dir;
    goldenTransform.block<4, 1>(0, 3) = goldenMidPt;
    pcl::PointCloud<pcl::PointXYZ>::Ptr orientedGolden(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::transformPointCloud(*goldenCloud, *orientedGolden, goldenTransform.inverse());
    pcl::PointXYZ goldenMin, goldenMax;
    pcl::getMinMax3D(*orientedGolden, goldenMin, goldenMax);

    //analyze sample cloud
    pcl::PCA<pcl::PointXYZ> pcaSample;
    pcaSample.setInputCloud(sampleCloud);
    Eigen::Matrix3f sampleEVs_Dir = pcaSample.getEigenVectors();
    Eigen::Vector4f sampleMidPt = pcaSample.getMean();
    Eigen::Matrix4f sampleTransform = Eigen::Matrix4f::Identity();
    sampleTransform.block<3, 3>(0, 0) = sampleEVs_Dir;
    sampleTransform.block<4, 1>(0, 3) = sampleMidPt;
    pcl::PointCloud<pcl::PointXYZ>::Ptr orientedSample(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::transformPointCloud(*sampleCloud, *orientedSample, sampleTransform.inverse());
    pcl::PointXYZ sampleMin, sampleMax;
    pcl::getMinMax3D(*orientedSample, sampleMin, sampleMax);

    //apply scaling to oriented sample cloud 
    double xScale = (sampleMax.x - sampleMin.x) / (goldenMax.x - goldenMin.x);
    double yScale = (sampleMax.y - sampleMin.y) / (goldenMax.y - goldenMin.y);
    double zScale = (sampleMax.z - sampleMin.z) / (goldenMax.z - goldenMin.z);

    if (primaryAxisOnly) { std::cout << "scale: " << xScale << std::endl; }
    else { std::cout << "xScale: " << xScale << "yScale: " << yScale << "zScale: " << zScale << std::endl; }


    for (int i = 0; i < orientedSample->points.size(); i++)
    {
        if (primaryAxisOnly)
        {
            orientedSample->points[i].x = orientedSample->points[i].x / xScale;
            orientedSample->points[i].y = orientedSample->points[i].y / xScale;
            orientedSample->points[i].z = orientedSample->points[i].z / xScale;
        }
        else
        {
            orientedSample->points[i].x = orientedSample->points[i].x / xScale;
            orientedSample->points[i].y = orientedSample->points[i].y / yScale;
            orientedSample->points[i].z = orientedSample->points[i].z / zScale;
        }
    }
    //depending on your next step, it may be reasonable to leave this cloud at its eigen orientation, but this transformation will allow this function to scale in place.

    if (debugOverlay)
    {
        goldenCloud = orientedGolden;
        sampleCloud = orientedSample;
    }
    else
    {
        pcl::transformPointCloud(*orientedSample, *sampleCloud, sampleTransform);
    }
}

测试代码(您将需要自己的云和可视化工具):

pcl::PointCloud<pcl::PointXYZ>::Ptr golden(new pcl::PointCloud<pcl::PointXYZ>);
fileIO::loadFromPCD(golden, "CT_Scan_Nov_7_fullSpine.pcd");
CloudVis::simpleVis(golden);

double xStretch = 1.75;
double yStretch = 1.65;
double zStretch = 1.5;
pcl::PointCloud<pcl::PointXYZ>::Ptr stretched(new pcl::PointCloud<pcl::PointXYZ>);
for (int i = 0; i < golden->points.size(); i++)
{
    pcl::PointXYZ pt = golden->points[i];
    stretched->points.push_back(pcl::PointXYZ(pt.x * xStretch, pt.y * yStretch, pt.z * zStretch));
}
Eigen::Affine3f arbRotation = Eigen::Affine3f::Identity();
arbRotation.rotate(Eigen::AngleAxisf(M_PI / 4.0, Eigen::Vector3f::UnitY()));
pcl::transformPointCloud(*stretched, *stretched, arbRotation);

CloudVis::rgbClusterVis(golden, stretched);

rescaleClouds(golden, stretched,true,false);
CloudVis::rgbClusterVis(golden, stretched);

【讨论】:

  • 哇太棒了。非常感谢你的努力。你帮了我很大的忙。也为了提高我对整个主题的理解。
  • 我对此还有一个问题:您最终使用xScale 来扩展云。为什么会这样?我不应该采用最大形式 xScale、yScale、zScale 并用它来缩放它吗? (轴对齐)
  • 我关心的是具有 2 个或更多相似变化轴的对象或目标。在这种情况下,如果轴由于翘曲而未对齐,则个别轴翘曲可能会加剧问题(通过错误地将错误的两个轴相互缩放)。通过仅考虑主轴的变化,仍然可能关联错误的目标轴(如果是高度对称的对象),但永远不会扭曲对象。
  • 所以如果你有一个旧的家用电话作为你的目标。一个轴(长轴)显然是主要的,几乎肯定会匹配。就变化而言,其他两个轴可能非常接近(但假设 x 略大于 y)。现在,在您的捕获方法中,云略微扭曲,使得在您未缩放的目标上,y 的变化略大于 x。现在缩放函数可以将 x 与 y 对齐,将 y 与 x 对齐,然后将它们按原样缩放,这将是错误的并且会加剧翘曲。
  • 谢谢,那是有道理的。
【解决方案2】:

看来你应该能够:

  • 将所有内容投影到前两个特征向量上
  • 为每个取最小值和最大值
  • 为每个特征向量/数据集对减去 max-min
  • 取两个范围中的最大值(通常,但不总是第一个特征向量 - 如果不是,您会想要旋转最终显示)
  • 使用这些最大值的比率作为缩放常数

【讨论】:

  • “数据集对”到底是什么意思?是否可以为整个算法提供一个简单的示例,以便我更好地理解该过程?非常感谢您的帮助。
  • 有两个数据集(“model”和“segmented”),你可以从每个数据集中获取前两个特征向量。这给出了四组投影(modelE1、modelE2、segmentedE1、segmentedE2)。对于其中的每一个,计算 max-min。
  • 谢谢。和“取两个范围的最大值”?当我为每组减去 max-min 时,我会得到四个标量。 “两个范围”是什么意思?
  • 对于每个云,计算 max(range[that cloud][eigenvector1], range[that cloud][eigenvector2])
  • 执行此操作时,不知何故,两种云的值几乎相同,导致比例因子非常接近 1。请参阅我在问题中的编辑以获取我的代码。
猜你喜欢
  • 1970-01-01
  • 2013-12-07
  • 2021-02-26
  • 2023-03-10
  • 1970-01-01
  • 2014-11-07
  • 1970-01-01
  • 2019-02-22
  • 2019-04-14
相关资源
最近更新 更多