Commit 58d6f682 authored by Jakub Kolman's avatar Jakub Kolman
Browse files

[#123] test: adding tests and refactoring code

parent a2616ddb
Loading
Loading
Loading
Loading
+38 −30
Original line number Diff line number Diff line
@@ -3,12 +3,9 @@ package cz.fidentis.analyst.face;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.icp.IcpTransformer;
import cz.fidentis.analyst.icp.Quaternion;
import cz.fidentis.analyst.procrustes.ProcrustesAnalysisFaceModel;
import cz.fidentis.analyst.symmetry.Plane;
import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
import cz.fidentis.analyst.visitors.procrustes.ProcrustesVisitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -18,7 +15,6 @@ import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;
import javax.vecmath.Tuple3d;
import javax.vecmath.Vector3d;
import org.ejml.simple.SimpleMatrix;

/**
 * A utility class for operations (visitors) applied onto the whole human faces.
@@ -367,9 +363,20 @@ public class HumanFaceUtils {
        return new Plane(retPlane.getNormal(), dist);
    }

    /**
     * Moves feature points to calculated new position by computing procrustes analysis.
     * {@link cz.fidentis.analyst.procrustes.ProcrustesAnalysis}. If feature point was not 
     * used in the analysis, it's position will be kept as it had in the original list.
     * 
     * Move of the feature point is done to remove them from user vision first.
     * 
     * @param secondFace
     * @param movedFeaturePoints 
     */
    private static void moveFeaturePoints(HumanFace secondFace, HashMap<Integer, FeaturePoint> movedFeaturePoints) {
        secondFace.getFeaturePoints().forEach(fp -> {
            FeaturePoint movedFp = movedFeaturePoints.get(fp.getFeaturePointType().getType());
            if (movedFp != null) {
                if (fp.getFeaturePointType().getType()
                        != movedFp.getFeaturePointType().getType()) {
                    throw new RuntimeException("Types do not correspond");
@@ -377,6 +384,7 @@ public class HumanFaceUtils {
                fp.getPosition().x = movedFp.getX();
                fp.getPosition().y = movedFp.getY();
                fp.getPosition().z = movedFp.getZ();
            } 
        });
    }

+50 −52
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@ import cz.fidentis.analyst.Logger;
import cz.fidentis.analyst.face.HumanFace;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.api.IPosition;
import cz.fidentis.analyst.mesh.core.MeshPoint;
import org.ejml.simple.SimpleMatrix;
import org.ejml.simple.SimpleSVD;

@@ -19,7 +18,6 @@ import javax.swing.JOptionPane;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import cz.fidentis.analyst.procrustes.ProcrustesAnalysisFaceModel;
import java.util.Comparator;

/**
@@ -49,11 +47,12 @@ public class ProcrustesAnalysis {
        ProcrustesAnalysisFaceModel model1;
        ProcrustesAnalysisFaceModel model2;

        // check if feature points lists of both faces have the same size and same types
        // if not, than we will be working with subset or cancel the analysis
        if (humanFace1.getFeaturePoints().size() != humanFace2.getFeaturePoints().size()
                || !checkFeaturePointsType(
                        sortListByFeaturePointType(humanFace1.getFeaturePoints()),
                        sortListByFeaturePointType(humanFace1.getFeaturePoints()))) {

//            int n = 0;
            Object[] options = {"Yes", "No"};
            int n = JOptionPane.showOptionDialog(
@@ -70,7 +69,7 @@ public class ProcrustesAnalysis {
                model1 = new ProcrustesAnalysisFaceModel(humanFace1, viablePoints);
                model2 = new ProcrustesAnalysisFaceModel(humanFace2, viablePoints);
                Logger.print("Yes");
                Logger.print(Integer.valueOf(n).toString());
                Logger.print(Integer.toString(n));
            } else {
                JOptionPane.showMessageDialog(new JFrame("Procrustes cancelled"),
                        "Lists of feature points do not have the same size and work on subset was cancelled");
@@ -81,11 +80,12 @@ public class ProcrustesAnalysis {
            model2 = new ProcrustesAnalysisFaceModel(humanFace2);
        }

        this.modelCentroid1 = findCentroidOfFeaturePoints(model1.getFeaturePointValues());
        this.modelCentroid2 = findCentroidOfFeaturePoints(model2.getFeaturePointValues());
        
        this.faceModel1 = model1;
        this.faceModel2 = model2;

        this.modelCentroid1 = findCentroidOfFeaturePoints(model1.getFeaturePointValues());
        this.modelCentroid2 = findCentroidOfFeaturePoints(model2.getFeaturePointValues());
    }

    /**
@@ -137,15 +137,13 @@ public class ProcrustesAnalysis {
            // calculation of rotation matrix
            transformation.setRotationMatrix(this.rotate());

            // move faces back to so the centroid of vertices is in the origin point
            // move face vertices back, so the original place, 
            // but set feture points map to already superimposed position
            moveFaceModel(faceModel1, 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 {
@@ -154,12 +152,12 @@ 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));
    }
//    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));
//    }

    /**
     * Moves all vertices and feature points of a face by given vector value
@@ -168,12 +166,12 @@ public class ProcrustesAnalysis {
     * @param vector vector its values are used to move a face
     */
    private void moveFaceModel(ProcrustesAnalysisFaceModel faceModel, Vector3d vector) {
        for (FeaturePoint fp : faceModel.getFeaturePointsMap().values()) {
        faceModel.getFeaturePointsMap().values().forEach(fp -> {
            movePoint(fp, vector);
        }
        for (MeshPoint v : faceModel.getVertices()) {
        });
        faceModel.getVertices().forEach(v -> {
            movePoint(v, vector);
        }
        });
    }

    /**
@@ -187,16 +185,16 @@ public class ProcrustesAnalysis {
     * @param featurePointVector feature points adjustment
     */
    private void moveFaceModel(ProcrustesAnalysisFaceModel faceModel, Vector3d vertexVector, Vector3d featurePointVector) {
        for (FeaturePoint fp : faceModel.getFeaturePointsMap().values()) {
        faceModel.getFeaturePointsMap().values().forEach(fp -> {
            movePoint(fp, featurePointVector);
        }
        for (MeshPoint v : faceModel.getVertices()) {
        });
        faceModel.getVertices().forEach(v -> {
            movePoint(v, vertexVector);
        }
        });
    }

    /**
     * Moves point by given vector value
     * Moves point by given vector value.
     *
     * @param <T>
     * @param point
@@ -209,7 +207,7 @@ public class ProcrustesAnalysis {
    }

    /**
     * By rotation of matrices solves orthogonal procrustes problem
     * By rotation of matrices solves orthogonal procrustes problem.
     */
    private SimpleMatrix rotate() {
        SimpleMatrix primaryMatrix = createMatrixFromList(this.faceModel2.getFeaturePointValues());
@@ -276,10 +274,10 @@ public class ProcrustesAnalysis {
     * @param faceModel
     * @param scaleFactor
     */
    private void scaleFace(ProcrustesAnalysisFaceModel faceModel, double scaleFactor) {
        calculateScaledList(faceModel.getVertices(), scaleFactor);
        calculateScaledList(faceModel.getFeaturePointValues(), scaleFactor);
    }
//    private void scaleFace(ProcrustesAnalysisFaceModel faceModel, double scaleFactor) {
//        calculateScaledList(faceModel.getVertices(), scaleFactor);
//        calculateScaledList(faceModel.getFeaturePointValues(), scaleFactor);
//    }

    /**
     * Scales each given point from list by multiplying its position coordinates
@@ -306,13 +304,13 @@ public class ProcrustesAnalysis {
     */
    // if rotated vertices are drawn immediately it is better to set them after rotating them all
    // so it would be drawn just once
    private void rotateVertices(List<MeshPoint> vertices, SimpleMatrix matrix) {
        if (vertices != null) {
            for (int i = 0; i < vertices.size(); i++) {
                rotateVertex(vertices.get(i), matrix);
            }
        }
    }
//    private void rotateVertices(List<MeshPoint> vertices, SimpleMatrix matrix) {
//        if (vertices != null) {
//            for (int i = 0; i < vertices.size(); i++) {
//                rotateVertex(vertices.get(i), matrix);
//            }
//        }
//    }

    /**
     * Rotates vertex v by simulating matrix multiplication with given matrix
@@ -320,20 +318,20 @@ public class ProcrustesAnalysis {
     * @param v
     * @param matrix
     */
    private static void rotateVertex(MeshPoint v, SimpleMatrix matrix) {
        double x = ((v.getX() * matrix.get(0, 0))
                + (v.getY() * matrix.get(1, 0))
                + (v.getZ() * matrix.get(2, 0)));
        double y = ((v.getX() * matrix.get(0, 1))
                + (v.getY() * matrix.get(1, 1))
                + (v.getZ() * matrix.get(2, 1)));
        double z = ((v.getX() * matrix.get(0, 2))
                + (v.getY() * matrix.get(1, 2))
                + (v.getZ() * matrix.get(2, 2)));
        v.getPosition().x = x;
        v.getPosition().y = y;
        v.getPosition().z = z;
    }
//    private static void rotateVertex(MeshPoint v, SimpleMatrix matrix) {
//        double x = ((v.getX() * matrix.get(0, 0))
//                + (v.getY() * matrix.get(1, 0))
//                + (v.getZ() * matrix.get(2, 0)));
//        double y = ((v.getX() * matrix.get(0, 1))
//                + (v.getY() * matrix.get(1, 1))
//                + (v.getZ() * matrix.get(2, 1)));
//        double z = ((v.getX() * matrix.get(0, 2))
//                + (v.getY() * matrix.get(1, 2))
//                + (v.getZ() * matrix.get(2, 2)));
//        v.getPosition().x = x;
//        v.getPosition().y = y;
//        v.getPosition().z = z;
//    }

    /**
     * Scales position of given point by multiplying its coordinates with given
+95 −31
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ public class ProcrustesAnalysisFaceModel {
    private final HumanFace humanFace;
    private HashMap<Integer, FeaturePoint> featurePointsMap;        // sorted by feature point type
    private List<MeshPoint> vertices;
    private MeshModel meshModel;
    private final MeshModel meshModel;
    private final HashMap<Integer, Integer> featurePointTypeCorrespondence;

    /**
@@ -57,7 +57,7 @@ public class ProcrustesAnalysisFaceModel {
        List<FeaturePoint> modifiableFeaturePointList = getSelectionOfFeaturePoints(face.getFeaturePoints(), viablePoints);

        // Currently set feature points is not working as inteded because gui doesn't repaint them after changing values
        face.setFeaturePoints(modifiableFeaturePointList);
//        face.setFeaturePoints(modifiableFeaturePointList);

        // To be able to move vertices we have to make a copy of them because  getFacets() returns unmodifiable List
        List<MeshPoint> modifiableVertices = new ArrayList<>(face.getMeshModel().getFacets().get(0).getVertices());
@@ -70,15 +70,43 @@ public class ProcrustesAnalysisFaceModel {
    }

    /**
     * sets feature points map and also sets feature point on human face for
     * visualisation
     * Use this constructor instead of the
     * {@link #ProcrustesAnalysisFaceModel(cz.fidentis.analyst.face.HumanFace, java.util.List)}
     * until the
     * {@link cz.fidentis.analyst.face.HumanFace#setFeaturePoints(java.util.List)}
     * is fixed and fires event to render in GUI a new set of feature points.
     *
     * Feature points that are not used in subset are moved to position of
     * modelCentroid.
     *
     * @param humanFace1
     * @param viablePoints
     * @param modelCentroid1
     */
//    ProcrustesAnalysisFaceModel(HumanFace face, ArrayList<Integer> viablePoints, Point3d modelCentroid) {
//        this.humanFace = face;
//        // getFeaturePoints() returns unmodifiable List. To sort it we need to make copy first
//        List<FeaturePoint> modifiableFeaturePointList = getSelectionOfFeaturePoints(face.getFeaturePoints(), viablePoints);
//
//        // To be able to move vertices we have to make a copy of them because  getFacets() returns unmodifiable List
//        List<MeshPoint> modifiableVertices = new ArrayList<>(face.getMeshModel().getFacets().get(0).getVertices());
//
//        this.vertices = modifiableVertices;
//        this.meshModel = face.getMeshModel();
//        this.featurePointsMap = createFeaturePointMap(
//                sortListByFeaturePointType(modifiableFeaturePointList));
//        this.featurePointTypeCorrespondence = createFeaturePointTypeCorrespondence(face.getFeaturePoints());
//    }

    /**
     * Sets feature points map and also sets feature point on human face for
     * visualisation.
     *
     * @param featurePointsMap
     */
    public void setFeaturePointsMap(HashMap<Integer, FeaturePoint> featurePointsMap) {
        this.featurePointsMap = featurePointsMap;
        readjustFeaturePoints(featurePointsMap);

//        this.humanFace.setFeaturePoints(getFeaturePointValues());
    }

@@ -106,6 +134,15 @@ public class ProcrustesAnalysisFaceModel {
        return featurePointTypeCorrespondence;
    }

    /**
     * Creates a subset of selected feature points that can be used for
     * procrustes analysis. Each of the feature point types contained in the
     * subset is contained in both faces.
     *
     * @param featurePoints
     * @param viablePoints
     * @return
     */
    private List<FeaturePoint> getSelectionOfFeaturePoints(List<FeaturePoint> featurePoints, List<Integer> viablePoints) {
        ArrayList<FeaturePoint> selection = new ArrayList<>();
        featurePoints.forEach(fp -> {
@@ -116,6 +153,30 @@ public class ProcrustesAnalysisFaceModel {
        return selection;
    }

    /**
     * {@link #getSelectionOfFeaturePoints(java.util.List, java.util.List)}
     * Feature points that are not in subset are moved to position of
     * modelCentroid.
     *
     * @param featurePoints
     * @param viablePoints
     * @param modelCentroid
     * @return
     */
//    private List<FeaturePoint> getSelectionOfFeaturePoints(List<FeaturePoint> featurePoints, ArrayList<Integer> viablePoints, Point3d modelCentroid) {
//        ArrayList<FeaturePoint> selection = new ArrayList<>();
//        featurePoints.forEach(fp -> {
//            if (viablePoints.contains(fp.getFeaturePointType().getType())) {
//                selection.add(fp);
//            } else {
//                fp.getPosition().x = modelCentroid.x;
//                fp.getPosition().y = modelCentroid.y;
//                fp.getPosition().z = modelCentroid.z;
//            }
//        });
//        return selection;
//    }

    /**
     * Creates corresponding key value pair of matrix row index and a feature
     * point type value. It is used so we can get back type of feature point
@@ -163,28 +224,9 @@ public class ProcrustesAnalysisFaceModel {
    }

    private void readjustFeaturePoints(HashMap<Integer, FeaturePoint> featurePointsMap) {
        if (featurePointsMap.size() != this.humanFace.getFeaturePoints().size()) {
            readjustSelection(featurePointsMap);
        } else {
            this.humanFace.getFeaturePoints().forEach(fp -> {
                FeaturePoint movedFp = featurePointsMap.get(fp.getFeaturePointType().getType());
                if (fp.getFeaturePointType().getType()
                        != movedFp.getFeaturePointType().getType()) {
                    throw new RuntimeException("Types do not correspond");
                }
                fp.getPosition().x = movedFp.getX();
                fp.getPosition().y = movedFp.getY();
                fp.getPosition().z = movedFp.getZ();
            });
        }
    }

    /**
     * Moves subset of feature points to their new position. If point doesn't belong to subset it is moved to (0,0,0) instead.
     * 
     * @param featurePointsMap 
     */
    private void readjustSelection(HashMap<Integer, FeaturePoint> featurePointsMap) {
//        if (featurePointsMap.size() != this.humanFace.getFeaturePoints().size()) {
//            readjustSelection(featurePointsMap);
//        } else {
        this.humanFace.getFeaturePoints().forEach(fp -> {
            FeaturePoint movedFp = featurePointsMap.get(fp.getFeaturePointType().getType());
            if (movedFp != null) {
@@ -195,12 +237,34 @@ public class ProcrustesAnalysisFaceModel {
                fp.getPosition().x = movedFp.getX();
                fp.getPosition().y = movedFp.getY();
                fp.getPosition().z = movedFp.getZ();
            } else {
                fp.getPosition().x = 0;
                fp.getPosition().y = 0;
                fp.getPosition().z = 0;
            }
        });
//    }
    }

//    /**
//     * Moves subset of feature points to their new position. If point doesn't
//     * belong to subset it is moved to (0,0,0) instead.
//     *
//     * @param featurePointsMap
//     */
//    private void readjustSelection(HashMap<Integer, FeaturePoint> featurePointsMap) {
//        this.humanFace.getFeaturePoints().forEach(fp -> {
//            FeaturePoint movedFp = featurePointsMap.get(fp.getFeaturePointType().getType());
//            if (movedFp != null) {
//                if (fp.getFeaturePointType().getType()
//                        != movedFp.getFeaturePointType().getType()) {
//                    throw new RuntimeException("Types do not correspond");
//                }
//                fp.getPosition().x = movedFp.getX();
//                fp.getPosition().y = movedFp.getY();
//                fp.getPosition().z = movedFp.getZ();
//            } else {
//                fp.getPosition().x = 0;
//                fp.getPosition().y = 0;
//                fp.getPosition().z = 0;
//            }
//        });
//    }

}
+1 −6
Original line number Diff line number Diff line
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.fidentis.analyst.procrustes;

import cz.fidentis.analyst.icp.Quaternion;
@@ -11,7 +6,7 @@ import org.ejml.simple.SimpleMatrix;

/**
 * Class used to store information about transformation that needs to be applied
 * to a second face when computing procrustes analysis
 * to a second face when computing procrustes analysis.
 *
 * @author Jakub Kolman
 */
+0 −5
Original line number Diff line number Diff line
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.fidentis.analyst.visitors.procrustes;

import cz.fidentis.analyst.face.HumanFace;
Loading