diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/EigenvalueDecomposition.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/EigenvalueDecomposition.java index 0383fec09c31e2478bfd2c34e1bf44b769261cb8..bab622abab955255da51dcf73dcd42bd60fd9a57 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/EigenvalueDecomposition.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/EigenvalueDecomposition.java @@ -934,21 +934,7 @@ public class EigenvalueDecomposition implements java.io.Serializable { return res; } - /** Return the real parts of the eigenvalues - @return real(diag(D)) - */ - - public double[] getRealEigenvalues () { - return d; - } - /** Return the imaginary parts of the eigenvalues - @return imag(diag(D)) - */ - - public double[] getImagEigenvalues () { - return e; - } /** Return the block diagonal eigenvalue matrix @return D @@ -980,5 +966,4 @@ public class EigenvalueDecomposition implements java.io.Serializable { D[2][0],D[2][1],D[2][2],D[2][3], D[3][0],D[3][1],D[3][2],D[3][3]); } - private static final long serialVersionUID = 1; } \ No newline at end of file diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/Icp.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/Icp.java index 5657115f145f4301ccf278826572459af9efc17a..6568bb9a9dff2f8344cd00415a643309b5220564 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/Icp.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/Icp.java @@ -6,7 +6,6 @@ import cz.fidentis.analyst.mesh.core.MeshPoint; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; import javax.vecmath.Matrix4d; -import javax.vecmath.Quat4d; import javax.vecmath.Vector3d; import java.util.ArrayList; import java.util.Collections; @@ -66,7 +65,7 @@ public class Icp { double prevMeanD = Double.POSITIVE_INFINITY; - while ((currentIteration < 1) && + while ((currentIteration < maxIteration) && (Double.isInfinite(prevMeanD) || Math.abs(prevMeanD - Objects.requireNonNull(transformation).getMeanD() ) > error )){ hausdorffDist.visitMeshFacet(transformedFacet); @@ -77,9 +76,9 @@ public class Icp { prevMeanD = transformation.getMeanD(); } - transformation = computeIcpTransformation1(nearestPoints, distances); - transformations.add(transformation); - applyTransformation1(transformation); + transformation = computeIcpTransformation(nearestPoints, distances); + //transformations.add(transformation); + applyTransformation(transformation); currentIteration ++; } @@ -100,161 +99,6 @@ public class Icp { * PRIVATE METHODS ***********************************************************/ - private Matrix4d computeIcpRotation(List<Vector3d> nearestPoints, Vector3d nearCenter, Vector3d compCenter) { - List<MeshPoint> comparedPoints = transformedFacet.getVertices(); - double CX = 0; - double CY = 0; - double CZ = 0; - - double NX = 0; - double NY = 0; - double NZ = 0; - - - for (int i = 0; i < comparedPoints.size(); i++) { - if (nearestPoints.get(i) == null) { - continue; - } - CX += relativeCoordinate(comparedPoints.get(i).getPosition(), compCenter).x; - CY += relativeCoordinate(comparedPoints.get(i).getPosition(), compCenter).y; - CZ += relativeCoordinate(comparedPoints.get(i).getPosition(), compCenter).z; - - NX += relativeCoordinate(nearestPoints.get(i), nearCenter).x; - NY += relativeCoordinate(nearestPoints.get(i), nearCenter).y; - NZ += relativeCoordinate(nearestPoints.get(i), nearCenter).z; - } - - double Sxx = CX * NX; - double Sxy = CX * NY; - double Sxz = CX * NZ; - - double Syx = CY * NX; - double Syy = CY * NY; - double Syz = CY * NZ; - - double Szx = CZ * NX; - double Szy = CZ * NY; - double Szz = CZ * NZ; - - return new Matrix4d(Sxx + Syy + Szz, Syz -Szy, -Sxz + Szx, Sxy + Syz, - -Szy + Syz, Sxx - Szz - Syy, Sxy + Syx, Sxz -Szx, - Szx -Sxz, Syx +Sxy, Syy-Szz-Sxx, Syz + Szy, - -Syx + Sxy, Szx + Sxz, Szy +Syz, Szz - Syy -Sxx ); - } - - private Matrix4d computeQuatMatrix(Quat4d q){ - double qq = q.w* q.w+ q.x+ q.x+ q.y* q.y+ q.z* q.z; - return new Matrix4d(qq, 0, 0, 0, - 0, (q.w*q.w + q.x *q.x - q.y * q.y - q.z * q.z), 2*(q.x * q.y - q.w *q.z), 2*(q.x * q.z + q.w *q.y), - 0, 2*(q.y * q.x + q.w *q.z),(q.w*q.w - q.x *q.x + q.y * q.y - q.z * q.z), 2*(q.y * q.z - q.w *q.x), - 0, 2*(q.z * q.x - q.w *q.y), 2*(q.z * q.y + q.w *q.x), (q.w*q.w - q.x *q.x - q.y * q.y + q.z * q.z)); - } - - /** - * Compute transformation parameters( translation, rotation and scale factor). - * Based on http://www.sci.utah.edu/~shireen/pdfs/tutorials/Elhabian_ICP09.pdf - * (p. 90 .. 92) - * - * @param nearestPoints List of nearest points computed by Hausdorff distance. - * @param distances list of distances computed by Hausdorff distance - * @return Return Icp transformation which is represented by computed parameters. - - */ - private IcpTransformation computeIcpTransformation2(List<Vector3d> nearestPoints, List<Double> distances) { - List<MeshPoint>comparedPoints = transformedFacet.getVertices(); - double meanD = 0; - double countOfNotNullPoints = 0; - - List<Vector3d>centers = computeCenterBothFacets(nearestPoints, comparedPoints); - Vector3d mainCenter = centers.get(0); - Vector3d comparedCenter = centers.get(1); - - //computing rotation - Matrix4d rotMatrix = computeIcpRotation(nearestPoints,centers.get(0), centers.get(1)); - - EigenvalueDecomposition matrix = new EigenvalueDecomposition(rotMatrix); - Matrix4d eigD = matrix.getD(); - Matrix4d eigM = matrix.getV(); - int max = 0; - //Finding quaternion q representing the rotation - // (it is the eigenvector corresponding to the largest eigenvalue of rotMatrix) - for (int i = 0; i < 4; i++) { - if (eigD.getElement(max, max) <= eigD.getElement(i, i)) { - max = i; - } - } - Quat4d q = new Quat4d(eigM.getElement(1, max), eigM.getElement(2, max), - eigM.getElement(3, max),eigM.getElement(0, max)); - q.normalize(); - - Matrix4d relativeNMatrix, relativeCMatrix; - Matrix4d numerator = new Matrix4d(); - Matrix4d denominator = new Matrix4d(); - - Matrix4d sumNumerator = new Matrix4d(); - Matrix4d sumDenominator = new Matrix4d(); - - Matrix4d quatMatrix = computeQuatMatrix(q); - Matrix4d quatMatrixMulRelativeC = new Matrix4d(); - - /* - * computing scale - * s = Sum(rmi*((Q(.transpose)*Q)*rci)) / Sum(rci * rci) - * Sum - i = 0 .. comparedPoints.size() - * rmi - relative coordinate of nearest neighbour (i = index) - * rci - relative coordinate of compared point (i = index) - * Q(.transpose)*Q - quatMatrix - * */ - - for(int i = 0; i < comparedPoints.size(); i++) { - if (nearestPoints.get(i) == null) { - continue; - } - - meanD += distances.get(i); - countOfNotNullPoints ++; - - Vector3d relativeC = relativeCoordinate(comparedPoints.get(i).getPosition(),comparedCenter); - Vector3d relativeN = relativeCoordinate(nearestPoints.get(i),mainCenter); - - relativeNMatrix = pointToMatrix(relativeN); - relativeCMatrix = pointToMatrix(relativeC); - - quatMatrixMulRelativeC.mul(quatMatrix, relativeCMatrix); - numerator.mul(relativeNMatrix, quatMatrixMulRelativeC); - - denominator.mul(relativeCMatrix, relativeCMatrix); - - sumNumerator.add(numerator); - sumDenominator.add(denominator); - } - // x, y, z should be same because it is uniform scale - double scaleFactor = sumNumerator.getElement(0,0)/ sumDenominator.getElement(0,0); - - /* computing translation - * t = mc - s*(Q(.transpose)*Q)*cc - * mc - mainCenter - * cc - comparedCenter - * s - scaleFactor - * Q(.transpose)*Q - quatMatrix - * */ - Matrix4d translationMatrix = new Matrix4d(); - Matrix4d mainCenterMatrix = pointToMatrix(mainCenter); - Matrix4d comparedCenterMatrix = pointToMatrix(comparedCenter); - - Matrix4d quatMatrixMulComparedCenterAndScale = new Matrix4d(); - quatMatrixMulComparedCenterAndScale.mul(quatMatrix, comparedCenterMatrix); - quatMatrixMulComparedCenterAndScale.mul(scaleFactor); - translationMatrix.sub(mainCenterMatrix, quatMatrixMulComparedCenterAndScale); - Vector3d translationOffset = new Vector3d(translationMatrix.getElement(0,0), - translationMatrix.getElement(0,1), translationMatrix.getElement(0,2)); - - meanD /= countOfNotNullPoints; - - return new IcpTransformation(translationOffset, q, scaleFactor, meanD); - } - - /** * Compute transformation parameters( translation, rotation and scale factor). * Based on old Fidentis implementation @@ -264,12 +108,8 @@ public class Icp { * @return Return Icp transformation which is represented by computed parameters. */ - private IcpTransformation computeIcpTransformation1(List<Vector3d> nearestPoints, List<Double> distances) { + private IcpTransformation computeIcpTransformation(List<Vector3d> nearestPoints, List<Double> distances) { List<MeshPoint>comparedPoints = transformedFacet.getVertices(); - double x, y, z; - double meanX = 0; - double meanY = 0; - double meanZ = 0; double meanD = 0; double countOfNotNullPoints = 0; @@ -285,16 +125,8 @@ public class Icp { continue; } // START computing Translation coordinates - x = nearestPoints.get(i).x - comparedPoints.get(i).getPosition().x; - y = nearestPoints.get(i).y - comparedPoints.get(i).getPosition().y; - z = nearestPoints.get(i).z - comparedPoints.get(i).getPosition().z; - - meanX += x; - meanY += y; - meanZ += z; meanD += distances.get(i); - Vector3d relativeC = relativeCoordinate(comparedPoints.get(i).getPosition(),comparedCenter); Vector3d relativeN = relativeCoordinate(nearestPoints.get(i),mainCenter); @@ -306,13 +138,9 @@ public class Icp { } meanD /= countOfNotNullPoints; - meanX /= countOfNotNullPoints; - meanY /= countOfNotNullPoints; - meanZ /= countOfNotNullPoints; - Vector3d translation = new Vector3d(meanX, meanY, meanZ); - // Vector3d translation = new Vector3d(mainCenter.x - comparedCenter.x, - // mainCenter.y - comparedCenter.y, mainCenter.z -comparedCenter.z); + Vector3d translation = new Vector3d(mainCenter.x - comparedCenter.x, + mainCenter.y - comparedCenter.y, mainCenter.z -comparedCenter.z); // END computing translation parameter @@ -328,10 +156,10 @@ public class Icp { max = i; } } - Quat4d q = new Quat4d(eigM.getElement(1, max), eigM.getElement(2, max), - eigM.getElement(3, max),eigM.getElement(0, max)); + Quaternion q = new Quaternion(eigM.getElement(0, max), eigM.getElement(1, max), eigM.getElement(2, max), + eigM.getElement(3, max)); - q.normalize(); + q = q.normalize(); // END computing rotation parameter //computing SCALE parameter @@ -340,8 +168,7 @@ public class Icp { double sxDown = 0; if (scale) { - Matrix4d rotationMatrix = new Matrix4d(); - rotationMatrix.set(q); + Matrix4d rotationMatrix = q.toMatrix(); Matrix4d matrixPoint, matrixPointCompare; @@ -369,7 +196,6 @@ public class Icp { } // end computing scale parameter - return new IcpTransformation(translation, q, scaleFactor, meanD); } @@ -378,65 +204,34 @@ public class Icp { * * @param transformation Computed transformation. */ - private void applyTransformation1(IcpTransformation transformation) { + private void applyTransformation(IcpTransformation transformation) { Vector3d meshPointPosition; - Quat4d rotationCopy = new Quat4d(); - Quat4d conjugateRotation = new Quat4d(); + Quaternion rotationCopy; - Quat4d rotation = new Quat4d(); + Quaternion rotation; for (MeshPoint comparedPoint : transformedFacet.getVertices()) { meshPointPosition = comparedPoint.getPosition(); - Quat4d point = new Quat4d(meshPointPosition.x, meshPointPosition.y, meshPointPosition.z, 1); + Quaternion point = new Quaternion(1, meshPointPosition.x, meshPointPosition.y, meshPointPosition.z); - if (transformedFacet.getVertices().size() > 1) { - conjugateRotation.conjugate(transformation.getRotation()); - rotationCopy.mul(point, conjugateRotation); - rotation.mul(transformation.getRotation(), rotationCopy); + if (transformedFacet.getVertices().size() > 1) { + rotationCopy = Quaternion.multiply(point, transformation.getRotation().getConjugate()); + rotation = Quaternion.multiply(transformation.getRotation(), rotationCopy); } else { rotation = point; } if(scale && !Double.isNaN(transformation.getScaleFactor())) { - meshPointPosition.set(rotation.x * transformation.getScaleFactor() + transformation.getTranslation().x, - rotation.y * transformation.getScaleFactor() + transformation.getTranslation().y, - rotation.z * transformation.getScaleFactor() + transformation.getTranslation().z); + meshPointPosition.set(rotation.getX() * transformation.getScaleFactor() + transformation.getTranslation().x, + rotation.getY() * transformation.getScaleFactor() + transformation.getTranslation().y, + rotation.getZ() * transformation.getScaleFactor() + transformation.getTranslation().z); } else { - meshPointPosition.set(rotation.x + transformation.getTranslation().x, - rotation.y + transformation.getTranslation().y , - rotation.z + transformation.getTranslation().z); + meshPointPosition.set(rotation.getX() + transformation.getTranslation().x, + rotation.getY() + transformation.getTranslation().y , + rotation.getZ() + transformation.getTranslation().z); } } } - /** - * Apply computed transformation to compared facet. - * - * @param transformation Computed transformation. - */ - private void applyTransformation2(IcpTransformation transformation) { - /* - * Mi = s*R*Ci + t - * Mi - model/main facet (i - index) - * s - scale - * R - rotation - * Ci - compared facet (i - index) - * t - translation - */ - Vector3d meshPointPosition; - Quat4d rotatedPoint = new Quat4d(); - - for (MeshPoint comparedPoint : transformedFacet.getVertices()) { - meshPointPosition = comparedPoint.getPosition(); - - Quat4d point = new Quat4d(meshPointPosition.x, meshPointPosition.y, meshPointPosition.z, 1); - rotatedPoint.mul(transformation.getRotation(), point); - - meshPointPosition.set(rotatedPoint.x * transformation.getScaleFactor() + transformation.getTranslation().x, - rotatedPoint.y * transformation.getScaleFactor() + transformation.getTranslation().y , - rotatedPoint.z * transformation.getScaleFactor() + transformation.getTranslation().z); - - } - } /** @@ -505,8 +300,6 @@ public class Icp { p.y, -p.z, 0, p.x, p.z, p.y, -p.x,0 ); } - - /** * Compute sum matrix of given point. Given point is point from main facet. * diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformation.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformation.java index 103958d7ffbd16432d7f338cef7f28933ffa0e44..e98ebc23c81735516722474c712758c329ca67fa 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformation.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformation.java @@ -1,6 +1,5 @@ package cz.fidentis.analyst.icp; -import javax.vecmath.Quat4d; import javax.vecmath.Vector3d; /** @@ -10,7 +9,7 @@ import javax.vecmath.Vector3d; */ public class IcpTransformation { - private final Quat4d rotation; + private final Quaternion rotation; private final double scaleFactor; private final Vector3d translation; private final double meanD; @@ -25,7 +24,7 @@ public class IcpTransformation { * In case there is no scale the value is 0. * @param meanD MeanD represents mean distance between objects. */ - public IcpTransformation(Vector3d translation, Quat4d rotation, double scaleFactor,double meanD) { + public IcpTransformation(Vector3d translation, Quaternion rotation, double scaleFactor, double meanD) { this.translation = translation; this.scaleFactor = scaleFactor; this.rotation = rotation; @@ -36,7 +35,7 @@ public class IcpTransformation { * Getter for rotation. * @return Return rotation parameter. */ - public Quat4d getRotation() { + public Quaternion getRotation() { return rotation; } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/Quaternion.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/Quaternion.java new file mode 100644 index 0000000000000000000000000000000000000000..47b7c0eec4c58bbf64280fb24bc1adc333147fa0 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/Quaternion.java @@ -0,0 +1,147 @@ +package cz.fidentis.analyst.icp; + +import javax.vecmath.Matrix4d; + +/** + * This class represents quaternions. + * + * @author Maria Kocurekova + */ +public final class Quaternion { + public final double w; + private final double x; + private final double y; + private final double z; + + /** + * Builds a quaternion from its components. + * + * @param w Scalar component. + * @param x First vector component. + * @param y Second vector component. + * @param z Third vector component. + */ + public Quaternion( final double w, + final double x, + final double y, + final double z) { + this.w = w; + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Gets the second component of the quaternion (first component + * of the vector part). + * + * @return the first component of the vector part. + */ + public double getX(){ + return x; + } + + /** + * Gets the third component of the quaternion (second component + * of the vector part). + * + * @return the second component of the vector part. + */ + public double getY(){ + return y; + } + + /** + * Gets the fourth component of the quaternion (third component + * of the vector part). + * + * @return the third component of the vector part. + */ + public double getZ(){ + return z; + } + + /** + * Gets the first component of the quaternion (scalar part). + * + * @return the scalar part. + */ + public double getW(){ + return w; + } + + /** + * Returns the conjugate quaternion of the instance. + * + * @return the conjugate quaternion + */ + public Quaternion getConjugate() { + return new Quaternion(w, -x, -y, -z); + } + + /** + * Returns the Hamilton product of two quaternions. + * + * @param q1 First quaternion. + * @param q2 Second quaternion. + * @return the Hamilton product of two quaternions + */ + public static Quaternion multiply(final Quaternion q1, final Quaternion q2) { + // Components of the first quaternion. + final double q1w = q1.getW(); + final double q1x = q1.getX(); + final double q1y = q1.getY(); + final double q1z = q1.getZ(); + + // Components of the second quaternion. + final double q2w = q2.getW(); + final double q2x = q2.getX(); + final double q2y = q2.getY(); + final double q2z = q2.getZ(); + + // Components of the product. + final double w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; + final double x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y; + final double y = q1w * q2y - q1x * q2z + q1y * q2w + q1z * q2x; + final double z = q1w * q2z + q1x * q2y - q1y * q2x + q1z * q2w; + + return new Quaternion(w, x, y, z); + } + + /** + * Computes the normalized quaternion + * + * @return a normalized quaternion + */ + public Quaternion normalize() { + double norm = Math.sqrt(w * w + + x * x + y * y + z * z); + return new Quaternion(w / norm, + x / norm, + y / norm, + z / norm); + } + + /** + * Convert a Quaternion to a rotation matrix + * + * @return a rotation matrix (4x4) + */ + public Matrix4d toMatrix(){ + double xx = x * x; + double xy = x * y; + double xz = x * z; + double xw = x * w; + double yy = y * y; + double yz = y * z; + double yw = y * w; + double zz = z * z; + double zw = z * w; + + return new Matrix4d(1 - 2 * ( yy + zz ),2 * ( xy + zw), 2 * ( xz - yw),0, + 2 * ( xy - zw ), 1 - 2 * ( xx + zz ), 2 * ( yz + xw ), 0, + 2 * ( xz + yw ), 2 * ( yz - xw ), 1 - 2 * ( xx + yy ), 0, + 0, 0,0, 1); + } + +}