Skip to content

Commit 1a099b0

Browse files
authored
Better Cylinder Model Sampling (#6342)
* Add cylinder sampling test with intersecting lines * Compute axis direction using cross product * Add symmetry cylinder sampling test * Estimate cylinder radius using both points
1 parent a09c4c8 commit 1a099b0

File tree

2 files changed

+66
-7
lines changed

2 files changed

+66
-7
lines changed

sample_consensus/include/pcl/sample_consensus/impl/sac_model_cylinder.hpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,29 +94,26 @@ pcl::SampleConsensusModelCylinder<PointT, PointNT>::computeModelCoefficients (
9494
Eigen::Vector4f n1 ((*normals_)[samples[0]].normal[0], (*normals_)[samples[0]].normal[1], (*normals_)[samples[0]].normal[2], 0.0f);
9595
Eigen::Vector4f n2 ((*normals_)[samples[1]].normal[0], (*normals_)[samples[1]].normal[1], (*normals_)[samples[1]].normal[2], 0.0f);
9696
Eigen::Vector4f w = n1 + p1 - p2;
97+
Eigen::Vector4f line_dir = n1.cross3 (n2);
9798

98-
float a = n1.dot (n1);
9999
float b = n1.dot (n2);
100100
float c = n2.dot (n2);
101101
float d = n1.dot (w);
102102
float e = n2.dot (w);
103-
float denominator = a*c - b*b;
104-
float sc, tc;
103+
float denominator = line_dir.squaredNorm ();
104+
float sc;
105105
// Compute the line parameters of the two closest points
106106
if (denominator < 1e-8) // The lines are almost parallel
107107
{
108108
sc = 0.0f;
109-
tc = (b > c ? d / b : e / c); // Use the largest denominator
110109
}
111110
else
112111
{
113112
sc = (b*e - c*d) / denominator;
114-
tc = (a*e - b*d) / denominator;
115113
}
116114

117115
// point_on_axis, axis_direction
118116
Eigen::Vector4f line_pt = p1 + n1 + sc * n1;
119-
Eigen::Vector4f line_dir = p2 + tc * n2 - line_pt;
120117
line_dir.normalize ();
121118

122119
model_coefficients.resize (model_size_);
@@ -129,7 +126,9 @@ pcl::SampleConsensusModelCylinder<PointT, PointNT>::computeModelCoefficients (
129126
model_coefficients[4] = line_dir[1];
130127
model_coefficients[5] = line_dir[2];
131128
// cylinder radius
132-
model_coefficients[6] = static_cast<float> (sqrt (pcl::sqrPointToLineDistance (p1, line_pt, line_dir)));
129+
model_coefficients[6] = static_cast<float> (
130+
0.5 * (sqrt (pcl::sqrPointToLineDistance (p1, line_pt, line_dir)) +
131+
sqrt (pcl::sqrPointToLineDistance (p2, line_pt, line_dir))));
133132

134133
if (model_coefficients[6] > radius_max_ || model_coefficients[6] < radius_min_)
135134
return (false);

test/sample_consensus/test_sample_consensus_quadric_models.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,66 @@ TEST(SampleConsensusModelTorusSelfIntersectSpindle, RANSAC)
16601660
EXPECT_NEAR(coeff[7], 0.25000000000000017, 1e-2);
16611661
}
16621662

1663+
TEST(SampleConsensusModelCylinder, SampleIntersectingLines)
1664+
{
1665+
PointCloud<PointXYZ> cloud;
1666+
PointCloud<Normal> normals;
1667+
cloud.resize(2);
1668+
normals.resize(2);
1669+
cloud[0].getVector3fMap() << 0.0f, 1.0f, 0.0f;
1670+
cloud[1].getVector3fMap() << 1.0f, 0.0f, 0.0f;
1671+
normals[0].getNormalVector3fMap() << 0.0f, 1.0f, 0.0f;
1672+
normals[1].getNormalVector3fMap() << 1.0f, 0.0f, 0.0f;
1673+
1674+
SampleConsensusModelCylinder<PointXYZ, Normal> model(cloud.makeShared());
1675+
model.setInputNormals(normals.makeShared());
1676+
1677+
Eigen::VectorXf model_coefficients;
1678+
model.computeModelCoefficients({0, 1}, model_coefficients);
1679+
1680+
ASSERT_EQ(7, model_coefficients.size());
1681+
1682+
EXPECT_NEAR(0.0f, model_coefficients[0], 1e-5f);
1683+
EXPECT_NEAR(0.0f, model_coefficients[1], 1e-5f);
1684+
EXPECT_NEAR(0.0f, model_coefficients[3], 1e-5f);
1685+
EXPECT_NEAR(0.0f, model_coefficients[4], 1e-5f);
1686+
EXPECT_LT(0.0f, std::abs(model_coefficients[5]));
1687+
EXPECT_NEAR(1.0f, model_coefficients[6], 1e-5f);
1688+
}
1689+
1690+
TEST(SampleConsensusModelCylinder, SampleModelsSymmetric)
1691+
{
1692+
PointCloud<PointXYZ> cloud;
1693+
PointCloud<Normal> normals;
1694+
cloud.resize(2);
1695+
normals.resize(2);
1696+
cloud[0].getVector3fMap() << 0.1f, 1.3f, 0.4f;
1697+
cloud[1].getVector3fMap() << 1.2f, -0.2f, -0.1f;
1698+
normals[0].getNormalVector3fMap() << 0.0f, 0.9f, -0.1f;
1699+
normals[1].getNormalVector3fMap() << 1.1f, -0.2f, 0.0f;
1700+
1701+
SampleConsensusModelCylinder<PointXYZ, Normal> model(cloud.makeShared());
1702+
model.setInputNormals(normals.makeShared());
1703+
1704+
Eigen::VectorXf model_coefficients, model_coefficients_swapped;
1705+
model.computeModelCoefficients({0, 1}, model_coefficients);
1706+
model.computeModelCoefficients({1, 0}, model_coefficients_swapped);
1707+
1708+
ASSERT_EQ(7, model_coefficients.size());
1709+
ASSERT_EQ(7, model_coefficients_swapped.size());
1710+
1711+
Eigen::Vector4f pt1(model_coefficients[0], model_coefficients[1], model_coefficients[2], 0.f);
1712+
Eigen::Vector4f dir1(model_coefficients[3], model_coefficients[4], model_coefficients[5], 0.f);
1713+
Eigen::Vector4f pt2(model_coefficients_swapped[0], model_coefficients_swapped[1], model_coefficients_swapped[2], 0.f);
1714+
Eigen::Vector4f dir2(model_coefficients_swapped[3], model_coefficients_swapped[4], model_coefficients_swapped[5], 0.f);
1715+
1716+
// Check if both cylinder axes are the same
1717+
EXPECT_GT(1e-5, sqrPointToLineDistance(pt2, pt1, dir1));
1718+
EXPECT_GT(1e-5f, (dir1 * (dir2.x() / dir1.x()) - dir2).squaredNorm());
1719+
1720+
EXPECT_NEAR(model_coefficients[6], model_coefficients_swapped[6], 1e-5f);
1721+
}
1722+
16631723
int
16641724
main(int argc, char** argv)
16651725
{

0 commit comments

Comments
 (0)