diff --git a/Comparison/src/main/java/cz/fidentis/analyst/procrustes/ProcrustesAnalysis.java b/Comparison/src/main/java/cz/fidentis/analyst/procrustes/ProcrustesAnalysis.java index a200183c33f6788b4de061965ded2c5670eec86d..779955c63370245595d66b2eb12960fe4ef160d7 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/procrustes/ProcrustesAnalysis.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/procrustes/ProcrustesAnalysis.java @@ -22,7 +22,6 @@ import javax.vecmath.Vector3d; import cz.fidentis.analyst.procrustes.ProcrustesAnalysisFaceModel; import java.util.Comparator; - /** * @author Jakub Kolman */ @@ -49,31 +48,31 @@ public class ProcrustesAnalysis { ProcrustesAnalysisFaceModel model1; ProcrustesAnalysisFaceModel model2; - + if (humanFace1.getFeaturePoints().size() != humanFace2.getFeaturePoints().size() || !checkFeaturePointsType( - sortListByFeaturePointType(humanFace1.getFeaturePoints()), + sortListByFeaturePointType(humanFace1.getFeaturePoints()), sortListByFeaturePointType(humanFace1.getFeaturePoints()))) { // int n = 0; Object[] options = {"Yes", "No"}; int n = JOptionPane.showOptionDialog( - new JFrame("Warning"), - "The sets of feature points do not correspond with each other. Do you want to continue with a subset of feature points?", - "Warning", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - options[1]); + new JFrame("Warning"), + "The sets of feature points do not correspond with each other. Do you want to continue with a subset of feature points?", + "Warning", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[1]); if (n == 0) { ArrayList<Integer> viablePoints = getSubsetOfFeaturePoints(humanFace1.getFeaturePoints(), humanFace2.getFeaturePoints()); model1 = new ProcrustesAnalysisFaceModel(humanFace1, viablePoints); model2 = new ProcrustesAnalysisFaceModel(humanFace2, viablePoints); Logger.print("Yes"); Logger.print(Integer.valueOf(n).toString()); - } else { - JOptionPane.showMessageDialog(new JFrame("Procrustes cancelled"), + } else { + JOptionPane.showMessageDialog(new JFrame("Procrustes cancelled"), "Lists of feature points do not have the same size and work on subset was cancelled"); throw new DataFormatException("Lists of feature points do not have the same size and work on subset was cancelled"); } @@ -90,8 +89,8 @@ public class ProcrustesAnalysis { } /** - * Constructor with variable for scaling option. - * Default scale option is set to false. + * Constructor with variable for scaling option. Default scale option is set + * to false. * * @param humanFace1 * @param humanFace2 @@ -107,45 +106,46 @@ public class ProcrustesAnalysis { } /** - * Method called for analysis after creating initial data in constructor. This method causes superimposition - * and rotation of the faces. - * - * Using generalized orthogonal procrustes analysis need to be first moved over each other, then scale and then rotate. + * Method called for analysis after creating initial data in constructor. + * This method causes superimposition and rotation of the faces. * - * @throws DataFormatException if faces have less than 3 feature points in which case analysis doesn't make sense + * Using generalized orthogonal procrustes analysis need to be first moved + * over each other, then scale and then rotate. + * + * @throws DataFormatException if faces have less than 3 feature points in + * which case analysis doesn't make sense */ public ProcrustesTransformation analyze() throws DataFormatException { ProcrustesTransformation transformation = new ProcrustesTransformation(); - if (this.faceModel1.getFeaturePointsMap().size() >= 3) { + if (this.faceModel1.getFeaturePointsMap().size() >= 3) { // adjustment is used for superimposition and return to the original place transformation.setCentroidAdjustment(new Vector3d(-this.modelCentroid2.x, -this.modelCentroid2.y, -this.modelCentroid2.z)); transformation.setSuperImpositionAdjustment(new Vector3d(this.modelCentroid1.x, this.modelCentroid1.y, this.modelCentroid1.z)); // sets both faces to the origin of plains - superImpose(); - + superImpose(); + // calculation of scaling vector if (scale) { double scaleFactorValue = this.calculateScalingValue(); if (scaleFactorValue != 1) { - scaleFace(faceModel2, scaleFactorValue); +// scaleFace(faceModel2, scaleFactorValue); + calculateScaledList(this.faceModel2.getFeaturePointValues(), scaleFactorValue); transformation.setScale(scaleFactorValue); } - } + } // calculation of rotation matrix - transformation.setRotationMatrix(this.rotate()); + transformation.setRotationMatrix(this.rotate()); // move faces back to so the centroid of vertices is in the origin point moveFaceModel(faceModel1, new Vector3d(this.modelCentroid1.x, this.modelCentroid1.y, this.modelCentroid1.z)); - moveFaceModel(faceModel2, new Vector3d(this.modelCentroid1.x, this.modelCentroid1.y, this.modelCentroid1.z)); - -// readjustFace(faceModel2, -// new Vector3d(this.modelCentroid2.x, this.modelCentroid2.y, this.modelCentroid2.z), -// new Vector3d(this.modelCentroid1.x, this.modelCentroid1.y, this.modelCentroid1.z)); +// moveFaceModel(faceModel2, new Vector3d(this.modelCentroid2.x, this.modelCentroid2.y, this.modelCentroid2.z)); + moveFaceModel(faceModel2, + new Vector3d(this.modelCentroid2.x, this.modelCentroid2.y, this.modelCentroid2.z), + new Vector3d(this.modelCentroid1.x, this.modelCentroid1.y, this.modelCentroid1.z)); // move featurepoints stored in map to superimposed true position // moveFeaturePoints(faceModel2, new Vector3d(this.modelCentroid1.x, this.modelCentroid1.y, this.modelCentroid1.z)); - transformation.setFaceModel(faceModel2); } else { @@ -153,17 +153,17 @@ public class ProcrustesAnalysis { } return transformation; } - + private Vector3d getPointDistance(Point3d point1, Point3d point2) { if (point1 == null || point2 == null) { return null; } - return new Vector3d((point2.x - point1.x), (point2.y - point1.y), (point2.z - point1.z)); + return new Vector3d((point2.x - point1.x), (point2.y - point1.y), (point2.z - point1.z)); } /** * Moves all vertices and feature points of a face by given vector value - * + * * @param faceModel face that is moved * @param vector vector its values are used to move a face */ @@ -175,18 +175,37 @@ public class ProcrustesAnalysis { movePoint(v, vector); } } - + /** - * Moves point by given vector value + * Moves face model by given vector values. + * Different vector value is used for vertices and feature points. + * This method is used to fit feature points to already superimposed position over given face1, but move vertices + * of face back to original position of face so all the vertex movement is done by procrustes visitor. * + * @param faceModel on which adjustment will be applied + * @param vertexVector vertex adjustment + * @param featurePointVector feature points adjustment + */ + private void moveFaceModel(ProcrustesAnalysisFaceModel faceModel, Vector3d vertexVector, Vector3d featurePointVector) { + for (FeaturePoint fp : faceModel.getFeaturePointsMap().values()) { + movePoint(fp, featurePointVector); + } + for (MeshPoint v : faceModel.getVertices()) { + movePoint(v, vertexVector); + } + } + + /** + * Moves point by given vector value + * * @param <T> * @param point - * @param vector + * @param vector */ private <T extends IPosition> void movePoint(T point, Vector3d vector) { - point.getPosition().x = point.getX() + vector.x; - point.getPosition().y = point.getY() + vector.y; - point.getPosition().z = point.getZ() + vector.z; + point.getPosition().x = point.getX() + vector.x; + point.getPosition().y = point.getY() + vector.y; + point.getPosition().z = point.getZ() + vector.z; } /** @@ -206,13 +225,11 @@ public class ProcrustesAnalysis { this.faceModel2.setFeaturePointsMap( createFeaturePointMapFromMatrix( primaryMatrix, this.faceModel2)); - - rotateVertices(this.faceModel2.getVertices(), rotationMatrix); +// rotateVertices(this.faceModel2.getVertices(), rotationMatrix); return rotationMatrix; } - /** * Calculate scaling ratio of how much the appropriate object corresponding * to the second feature point list has to be scale up or shrunk. @@ -226,7 +243,7 @@ public class ProcrustesAnalysis { private double calculateScalingValue() { List<FeaturePoint> featurePointList1 = this.faceModel1.getFeaturePointValues(); List<FeaturePoint> featurePointList2 = this.faceModel2.getFeaturePointValues(); - + double[] distancesOfList1 = new double[featurePointList1.size()]; double[] distancesOfList2 = new double[featurePointList2.size()]; @@ -253,7 +270,8 @@ public class ProcrustesAnalysis { } /** - * Initiates scaling of feature points and vertices of given face model by scaleFactor. + * Initiates scaling of feature points and vertices of given face model by + * scaleFactor. * * @param faceModel * @param scaleFactor @@ -264,7 +282,8 @@ public class ProcrustesAnalysis { } /** - * Scales each given point from list by multiplying its position coordinates with scaleFactor. + * Scales each given point from list by multiplying its position coordinates + * with scaleFactor. * * @param list * @param scaleFactor @@ -276,8 +295,7 @@ public class ProcrustesAnalysis { scaledList.add(scalePointDistance(point, scaleFactor)); } } - - + /** * Rotates all vertices. * <p> @@ -318,7 +336,8 @@ public class ProcrustesAnalysis { } /** - * Scales position of given point by multiplying its coordinates with given scaleFactor. + * Scales position of given point by multiplying its coordinates with given + * scaleFactor. * * @param point * @param scaleFactor @@ -336,11 +355,11 @@ public class ProcrustesAnalysis { * Checks if two feature point lists have the same types of feature points. * <p> * To use this method you need to supply ordered lists by Feature Point type - * as parameters. Otherwise even if two lists contain the same feature points - * it will return false. + * as parameters. Otherwise even if two lists contain the same feature + * points it will return false. * <p> - * Use sort function sortListByFeaturePointType on feature point lists - * to get correct results. + * Use sort function sortListByFeaturePointType on feature point lists to + * get correct results. * * @param featurePointList1 * @param featurePointList2 @@ -377,7 +396,8 @@ public class ProcrustesAnalysis { } /** - * Creates feature point map HashMap with key FeaturePoint.type and value FeaturePoint back from matrix. + * Creates feature point map HashMap with key FeaturePoint.type and value + * FeaturePoint back from matrix. * * @param matrix * @param model @@ -406,7 +426,6 @@ public class ProcrustesAnalysis { * @param list * @return matrix */ - private <T extends IPosition> SimpleMatrix createMatrixFromList(List<T> list) { SimpleMatrix matrix = new SimpleMatrix(list.size(), 3); for (int i = 0; i < list.size(); i++) { @@ -416,6 +435,7 @@ public class ProcrustesAnalysis { } return matrix; } + /** * Calculates distance of one feature point from another * @@ -426,12 +446,13 @@ public class ProcrustesAnalysis { private double calculateDistanceFromPoint(FeaturePoint fp1, Point3d point) { return Math.sqrt( (Math.pow(fp1.getX() - point.x, 2)) - + (Math.pow(fp1.getY()- point.y, 2)) - + (Math.pow(fp1.getZ()- point.z, 2))); + + (Math.pow(fp1.getY() - point.y, 2)) + + (Math.pow(fp1.getZ() - point.z, 2))); } /** - * moves faces so the feature points centroids of both faces will be at the origin (0,0,0) + * moves faces so the feature points centroids of both faces will be at the + * origin (0,0,0) */ protected void superImpose() { moveFaceModel(this.faceModel2, new Vector3d(-this.modelCentroid2.x, -this.modelCentroid2.y, -this.modelCentroid2.z)); @@ -440,7 +461,7 @@ public class ProcrustesAnalysis { private HashMap<Integer, FeaturePoint> moveFeaturePoints(ProcrustesAnalysisFaceModel faceModel2, Vector3d pointDistance) { HashMap<Integer, FeaturePoint> map = new HashMap<>(); - for (Map.Entry<Integer,FeaturePoint> entry: faceModel2.getFeaturePointsMap().entrySet()) { + for (Map.Entry<Integer, FeaturePoint> entry : faceModel2.getFeaturePointsMap().entrySet()) { entry.getValue().getPosition().x += pointDistance.x; entry.getValue().getPosition().y += pointDistance.y; entry.getValue().getPosition().z += pointDistance.z; @@ -449,28 +470,22 @@ public class ProcrustesAnalysis { return map; } - private void readjustFace(ProcrustesAnalysisFaceModel faceModel, Vector3d vertexAdjustment, Vector3d fpAdjustment) { - faceModel2.setFeaturePointsMap(moveFeaturePoints(faceModel, fpAdjustment)); - for (MeshPoint v : faceModel.getVertices()) { - movePoint(v, vertexAdjustment); - } - } - /** - * Finds corresponding subset of feature points from both faces so the procrustes analysis can be - * applied on the subset. - * + * Finds corresponding subset of feature points from both faces so the + * procrustes analysis can be applied on the subset. + * * @param featurePoints - * @param featurePoints0 - * - * @return List of feature point types that are in both sets of feature points + * @param featurePoints0 + * + * @return List of feature point types that are in both sets of feature + * points */ private ArrayList<Integer> getSubsetOfFeaturePoints(List<FeaturePoint> featurePoints1, List<FeaturePoint> featurePoints2) { HashMap<Integer, FeaturePoint> fpMap = new HashMap<>(); for (FeaturePoint fp : featurePoints1) { fpMap.put(fp.getFeaturePointType().getType(), fp); } - + ArrayList<Integer> vaiablePoints = new ArrayList<>(); featurePoints2.forEach(fp -> { if (fpMap.get(fp.getFeaturePointType().getType()) != null) { @@ -486,5 +501,5 @@ public class ProcrustesAnalysis { ); return featurePointList; } - + } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/procrustes/ProcrustesVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/procrustes/ProcrustesVisitor.java index 063e75332b262c755b74e98dfe0b006d5cd213f9..edf67f47a98f56489865deb0ee77bf3a95491673 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/procrustes/ProcrustesVisitor.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/procrustes/ProcrustesVisitor.java @@ -68,17 +68,17 @@ public class ProcrustesVisitor extends MeshVisitor { try { ProcrustesAnalysis procrustes = new ProcrustesAnalysis(this.primaryFace, this.secondaryFace, scale); this.transformation = procrustes.analyze(); -// applyTransformations(this.transformation, transformedFacet); + applyTransformations(this.transformation, transformedFacet); } catch (Exception e) { System.err.println(e.getMessage()); } } private void applyTransformations(ProcrustesTransformation transformation, MeshFacet transformedFacet){ - moveFace(transformedFacet, transformation.getCentroidAdjustment()); - scaleFace(transformedFacet, transformation.getScale()); - rotateFace(transformedFacet, transformation.getRotationMatrix()); - moveFace(transformedFacet, transformation.getSuperImpositionAdjustment()); + moveFace(this.secondaryFace.getMeshModel().getFacets().get(0), transformation.getCentroidAdjustment()); + scaleFace(this.secondaryFace.getMeshModel().getFacets().get(0), transformation.getScale()); + rotateFace(this.secondaryFace.getMeshModel().getFacets().get(0), transformation.getRotationMatrix()); + moveFace(this.secondaryFace.getMeshModel().getFacets().get(0), transformation.getSuperImpositionAdjustment()); } private void moveFace(MeshFacet transformedFacet, Vector3d vector) { diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/ProcrustesVisitorTest.java b/GUI/src/main/java/cz/fidentis/analyst/tests/ProcrustesVisitorTest.java index 25cacbe3448c2d87705ba2fcdd838edbdc94e863..2c4777b736600e784802a74466319e99306d5d3c 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/tests/ProcrustesVisitorTest.java +++ b/GUI/src/main/java/cz/fidentis/analyst/tests/ProcrustesVisitorTest.java @@ -40,8 +40,10 @@ public class ProcrustesVisitorTest { private static final String FACE_2 = "0002_02_ECA.obj"; private static final String FACE_FP_1 = "0002_01_landmarks.csv"; private static final String FACE_FP_2 = "0002_02_landmarks.csv"; - private static final String FACE_FP_ROTATION_1 = "rotation_180_moved_2_landmarks.csv"; - private static final String FACE_FP_ROTATION_2 = "rotation_landmarks.csv"; + private static final String FACE_FP_ROTATION_1 = "rotation_landmarks.csv"; + private static final String FACE_FP_ROTATION_2 = "rotation_180_moved_2_landmarks.csv"; + private static final String FACE_FP_TWOS = "twos_landmarks.csv"; + private static final String FACE_FP_FOURS = "fours_landmarks.csv"; /** * Main method @@ -56,7 +58,7 @@ public class ProcrustesVisitorTest { List<FeaturePoint> fpList1 = featurePointImportService.importFeaturePoints( DATA_DIR.toString(), FACE_FP_ROTATION_1); List<FeaturePoint> fpList2= featurePointImportService.importFeaturePoints( - DATA_DIR.toString(), FACE_FP_2); + DATA_DIR.toString(), FACE_FP_ROTATION_2); HumanFace face1 = factory.getFace(factory.loadFace( diff --git a/GUI/src/test/resources/cz/fidentis/analyst/fours_landmarks.csv b/GUI/src/test/resources/cz/fidentis/analyst/fours_landmarks.csv new file mode 100644 index 0000000000000000000000000000000000000000..39e6aa05982cc93e332aa445ef091291af5c7f0f --- /dev/null +++ b/GUI/src/test/resources/cz/fidentis/analyst/fours_landmarks.csv @@ -0,0 +1,2 @@ +Scan name,EX_R x,EX_R y,EX_R z,EX_L x,EX_L y,EX_L z,EN_R x,EN_R y,EN_R z,EN_L x,EN_L y,EN_L z +0002_01,40,40,40,40,-40,40,-40,-40,-40,-40,40,-40 diff --git a/GUI/src/test/resources/cz/fidentis/analyst/twos_landmarks.csv b/GUI/src/test/resources/cz/fidentis/analyst/twos_landmarks.csv new file mode 100644 index 0000000000000000000000000000000000000000..989ac9bb6a11f1afdd176ee36b97e53ddee537dc --- /dev/null +++ b/GUI/src/test/resources/cz/fidentis/analyst/twos_landmarks.csv @@ -0,0 +1,2 @@ +Scan name,EX_R x,EX_R y,EX_R z,EX_L x,EX_L y,EX_L z,EN_R x,EN_R y,EN_R z,EN_L x,EN_L y,EN_L z +0002_01,20,20,20,20,-20,20,-20,-20,-20,-20,20,-20