PCL:将两个点云缩放到相同大小
PCL: Scale two Point-Clouds to the same size
我有两个点云并尝试将它们缩放到相同的大小。我的第一种方法是将平方根除以特征值:
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]);
这有助于我将模型云缩放到与分段云大致相同的大小。但是结果真的不是那么完美。这是一个简单的估计。我试着用 TransformationEstimationSVDScale and also with SampleConsensusModelRegistration
like in this tutorial 来做。但是当这样做时我得到消息,源的数量 points/indices 与目标的数量 points/indices 不同。
当云中有不同数量的点时,将云缩放到相同大小的最佳方法是什么?
编辑 我尝试按照@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;
看来您应该能够:
- 将所有内容投影到前两个特征向量
- 取每个的最小值和最大值
- 为每个 eigenvector/dataset 对减去最大-最小值
- 取两个范围的最大值(通常,但不总是第一个特征向量——如果不是,你会想要旋转最终显示)
- 使用这些最大值的比率作为缩放常数
我建议使用每个云的特征向量来识别每个变化的主轴,然后根据该轴上的每个云变化对它们进行缩放。在我的示例中,我使用了定向边界框(本征空间中的最大最小值),但主轴(本征空间中的 x 轴)的平均值或标准差可能是更好的指标,具体取决于应用程序。
我在函数中保留了一些调试标志以防它们对您有帮助,但为它们提供了我希望您会使用的默认值。我测试了样本和金色云的可变轴拉伸和可变旋转。这个函数应该能够很好地处理这一切。
此方法的一个警告是,如果扭曲是轴向可变的 AND 扭曲会导致一个轴克服另一个轴作为变化的主轴,此函数可能无法正确缩放云。我不确定这个边缘案例是否与您相关。只要你的云之间有统一的缩放比例,这种情况就永远不会发生。
debugFlags:debugOverlay 将使两个输入云保持缩放并处于各自的特征方向(允许更容易比较)。 primaryAxisOnly 如果为 true 将仅使用变化的主轴执行缩放,如果为 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);
我有两个点云并尝试将它们缩放到相同的大小。我的第一种方法是将平方根除以特征值:
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]);
这有助于我将模型云缩放到与分段云大致相同的大小。但是结果真的不是那么完美。这是一个简单的估计。我试着用 TransformationEstimationSVDScale and also with SampleConsensusModelRegistration
like in this tutorial 来做。但是当这样做时我得到消息,源的数量 points/indices 与目标的数量 points/indices 不同。
当云中有不同数量的点时,将云缩放到相同大小的最佳方法是什么?
编辑 我尝试按照@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;
看来您应该能够:
- 将所有内容投影到前两个特征向量
- 取每个的最小值和最大值
- 为每个 eigenvector/dataset 对减去最大-最小值
- 取两个范围的最大值(通常,但不总是第一个特征向量——如果不是,你会想要旋转最终显示)
- 使用这些最大值的比率作为缩放常数
我建议使用每个云的特征向量来识别每个变化的主轴,然后根据该轴上的每个云变化对它们进行缩放。在我的示例中,我使用了定向边界框(本征空间中的最大最小值),但主轴(本征空间中的 x 轴)的平均值或标准差可能是更好的指标,具体取决于应用程序。
我在函数中保留了一些调试标志以防它们对您有帮助,但为它们提供了我希望您会使用的默认值。我测试了样本和金色云的可变轴拉伸和可变旋转。这个函数应该能够很好地处理这一切。
此方法的一个警告是,如果扭曲是轴向可变的 AND 扭曲会导致一个轴克服另一个轴作为变化的主轴,此函数可能无法正确缩放云。我不确定这个边缘案例是否与您相关。只要你的云之间有统一的缩放比例,这种情况就永远不会发生。
debugFlags:debugOverlay 将使两个输入云保持缩放并处于各自的特征方向(允许更容易比较)。 primaryAxisOnly 如果为 true 将仅使用变化的主轴执行缩放,如果为 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);