3Dオブジェクトの表面にランダムにサンプリングされたポイントのセットがあります。2つの異なるオブジェクト間の類似性を計算できるようにしたいと思います。これを機能させるには、まず、比較する両方のオブジェクトのサンプルポイントが同じ回転とスケールであることを確認する必要があります。これは、主成分軸をx / y / z軸に沿って配置し、最長の主成分が単位長になるようにスケーリングすることで実現できると思いました。
最初にポイントセットの図心を計算し、原点が新しい図心になるようにすべての点を変換します。
CGAL linear_least_squares_fitting_3関数を使用して主成分分析を行います。これにより、ポイントを通る最適な平面が得られます。両方の基本ベクトルの外積をとることにより、この平面の法線を計算します。
Plane plane;
linear_least_squares_fitting_3(points.begin(), points.end(),
plane, CGAL::Dimension_tag<0>());
auto dir1 = dir2vec(plane.base1().direction());
auto dir2 = dir2vec(plane.base2().direction());
auto normal = dir1 ^ dir2; // cross product
normal.normalize(); dir1.normalize(); dir2.normalize();
この関数は、オブジェクトを同等dir2vec
のオブジェクトに変換します(私はOpenSceneGraphグラフィックエンジンを使用しています)。最後に、次のコードを使用して、すべてを単位軸に回転させます。CGAL::Direction_3
osg::Vec3d
Matrixd r1, r2, r3;
r1.makeRotate(normal, Vec3d(1,0,0));
r2.makeRotate(dir1 * r1, Vec3d(0,1,0));
r3.makeRotate(dir2 * r1 * r2, Vec3d(0,0,1));
auto rotate = [&](Vec3d const &p) {
return p * r1 * r2 * r3;
};
transform(osgPoints.begin(), osgPoints.end(), osgPoints.begin(), rotate);
ここに、osgPoints
がありvector<osg::Vec3d>
ます。テストの目的で、回転したポイントの図心を元の位置に変換して、両方のポイントクラウドが重ならないようにします。
Vec3d center = point2vec(centroid);
auto tocentroid = [&](Vec3d const &v) {
return v + center;
};
transform(osgPoints.begin(), osgPoints.end(), osgPoints.begin(), tocentroid);
それをテストするために、同じポイントセットの2つのコピーを使用しますが、1つは変換(回転および変換)されます。上記のコードは回転を元に戻す必要がありますが、結果は私が期待したものではありません。この画像を参照してください。赤い線は、最適な平面のベースベクトルとその法線を示しています。linear_least_squares_fitting_3
平面の一方が他方に対して少し回転しているため、への両方の呼び出しの結果はわずかに異なる答えを与えるように見えます。
これは、両方のオブジェクトが重心を原点として配置されている別の画像です。これで、法線と基本ベクトルが一緒になっていることがはっきりとわかりますが、点はそうではありません。
なぜこれが起こるのか、そしてどうすればそれを防ぐことができるのか誰かが知っていますか?