コード例 #1
0
static void
computeBestFitOBB(TheaArray<Vector3> const & points, Box3 & result, bool has_up, Vector3 const & up)
{
  if (points.empty())
  {
    result = Box3();
    return;
  }
  else if (points.size() == 1)
  {
    result = Box3(AxisAlignedBox3(points[0]));
    return;
  }

  Vector3 centroid;
  CoordinateFrame3 cframe;

  if (has_up)
  {
    centroid = CentroidN<Vector3, 3>::compute(points.begin(), points.end());
    Vector3 u, v;
    up.createOrthonormalBasis(u, v);
    cframe = CoordinateFrame3::_fromAffine(AffineTransform3(basisMatrix(u, v, up), centroid));
  }
  else
  {
    Plane3 plane;
    LinearLeastSquares3<Vector3>::fitPlane(points.begin(), points.end(), plane, &centroid);
    planeToCFrame(plane, centroid, cframe);
  }

  OBB best_obb;
  computeOBB(points, cframe, best_obb, true);

  Matrix3 rot = Matrix3::rotationAxisAngle((has_up ? up : Vector3::unitY()), Math::degreesToRadians(10));
  for (int a = 10;  a < 180; a += 10)
  {
    cframe._setRotation(cframe.getRotation() * rot);
    computeOBB(points, cframe, best_obb, false);
  }

  result = Box3(AxisAlignedBox3(best_obb.lo, best_obb.hi), best_obb.cframe);
}
コード例 #2
0
ファイル: Math.cpp プロジェクト: daerduoCarey/Thea
RigidTransform3
closestRigidTransform(std::vector<CoordinateFrame3> const & src, std::vector<CoordinateFrame3> const & dst)
{
  alwaysAssertM(src.size() == dst.size(), "Source and destination sets must have same number of frames");

  if (src.empty())
    return RigidTransform3::identity();
  else if (src.size() == 1)
    return RigidTransform3(dst[0]) * RigidTransform3(src[0].inverse());
  else if (src.size() == 2)
  {
    // First map line segment to line segment...
    Vector3 src_mean = 0.5f * (src[0].getTranslation() + src[1].getTranslation());
    Vector3 dst_mean = 0.5f * (dst[0].getTranslation() + dst[1].getTranslation());

    static Real const MIN_SQLEN = 1.0e-8f;

    Vector3 src_axis = src[1].getTranslation() - src[0].getTranslation();
    Vector3 dst_axis = dst[1].getTranslation() - dst[0].getTranslation();

    if (src_axis.squaredLength() > MIN_SQLEN && dst_axis.squaredLength() > MIN_SQLEN)
    {
      Matrix3 rot = Matrix3::rotationArc(src_axis, dst_axis);

      // Now rotate around the mapped segment to align the z axes of the frames...
      Vector3 src_dir = rot * (src[0].getRotation().getColumn(2) + src[1].getRotation().getColumn(2));
      Vector3 dst_dir = dst[0].getRotation().getColumn(2) + dst[1].getRotation().getColumn(2);

      // Transform src_dir and dst_dir to be perpendicular to dst_axis
      dst_axis = dst_axis.fastUnit();
      src_dir = src_dir - src_dir.dot(dst_axis) * dst_axis;
      dst_dir = dst_dir - dst_dir.dot(dst_axis) * dst_axis;

      if (src_dir.squaredLength() > MIN_SQLEN && dst_dir.squaredLength() > MIN_SQLEN)
      {
        src_dir = src_dir.fastUnit();
        dst_dir = dst_dir.fastUnit();

        Vector3 src_perp = dst_axis.cross(src_dir);
        Vector3 dst_perp = dst_axis.cross(dst_dir);

        Matrix3 src_basis = basisMatrix(src_dir, src_perp, dst_axis);
        Matrix3 dst_basis = basisMatrix(dst_dir, dst_perp, dst_axis);
        Matrix3 extra_rot = dst_basis * src_basis.transpose();  // inverse == tranpose for rotation matrices

        rot = extra_rot * rot;
      }

      CoordinateFrame3 cf(RigidTransform3::_fromAffine(AffineTransform3(rot, dst_mean - rot * src_mean)));
      // qDebug().nospace() << "R = " << cf.getRotation().toString() << ", T = " << cf.getTranslation().toString();

      return RigidTransform3(cf);
    }
    else
      return RigidTransform3::translation(dst_mean - src_mean);
  }

  // ICP algorithm of Arun et al. 1987.

  size_t num_frames = src.size();
  Vector3 src_mean = Vector3::zero(), dst_mean = Vector3::zero();
  for (size_t i = 0; i < num_frames; ++i)
  {
    CoordinateFrame3 const & src_frame = src[i];
    CoordinateFrame3 const & dst_frame = dst[i];

    // qDebug().nospace() << "src[" << i << "] = R: " << src_frame.getRotation().toString() << ", T: "
    //                    << src_frame.getTranslation().toString();
    // qDebug().nospace() << "dst[" << i << "] = R: " << dst_frame.getRotation().toString() << ", T: "
    //                    << dst_frame.getTranslation().toString();

    src_mean += src_frame.getTranslation();
    dst_mean += dst_frame.getTranslation();
  }

  src_mean /= num_frames;
  dst_mean /= num_frames;

  Matrix3 corr = Matrix3::zero();
  Vector3 src_pt, dst_pt;
  for (size_t i = 0; i < num_frames; ++i)
  {
    CoordinateFrame3 const & src_frame = src[i];
    CoordinateFrame3 const & dst_frame = dst[i];

    src_pt = src_frame.getTranslation() - src_mean;
    dst_pt = dst_frame.getTranslation() - dst_mean;

    for (int r = 0; r < 3; ++r)
      for (int c = 0; c < 3; ++c)
        corr(r, c) += src_pt[r] * dst_pt[c];
  }

  Matrix3 U, V;
  TheaArray<Real> diag;
  Algorithms::SVD::compute(corr, U, diag, V);
  Matrix3 U_T = U.transpose();
  Matrix3 rotation = V * U_T;
  if (rotation.determinant() < 0)
  {
    // FIXME: One of the columns (which?) of V should be negated. See Eggert et al. '97, "Estimating 3D rigid transformations: a
    // comparison of four major algorithms".
    //
    // For now, we'll do the dumb but safe thing and test each option for the minimum error.

    THEA_WARNING << "Estimated rigid transform involves reflection, fixing by negating a column of V";

    Real min_sqerr = -1;
    for (int i = 0; i < 3; ++i)
    {
      // Swap i'th column of V
      Matrix3 V_i = V;
      V_i(0, i) = -V_i(0, i);
      V_i(1, i) = -V_i(1, i);
      V_i(2, i) = -V_i(2, i);

      Matrix3 candidate_rot = V_i * U_T;
      Real sqerr = 0;
      for (size_t j = 0; j < num_frames; ++j)
      {
        CoordinateFrame3 const & src_frame = src[j];
        CoordinateFrame3 const & dst_frame = dst[j];

        src_pt = src_frame.getTranslation() - src_mean;
        dst_pt = dst_frame.getTranslation() - dst_mean;
        sqerr += (candidate_rot * src_pt - dst_pt).squaredLength();
      }

      if (min_sqerr < 0 || sqerr < min_sqerr)
      {
        rotation = candidate_rot;
        min_sqerr = sqerr;
      }
    }
  }

  Vector3 translation = dst_mean - rotation * src_mean;

  CoordinateFrame3 cf(RigidTransform3::_fromAffine(AffineTransform3(rotation, translation)));
  // qDebug().nospace() << "R = " << rotation.toString() << ", T = " << translation.toString();

  return RigidTransform3(cf);
}