Skip to content
Snippets Groups Projects
Commit c5d994f1 authored by Jakub Kolman's avatar Jakub Kolman
Browse files

[123] feat: procrustes analysis with subset of feature points

parent 2c5017f6
No related branches found
No related tags found
No related merge requests found
package cz.fidentis.analyst.procrustes;
import cz.fidentis.analyst.Logger;
import cz.fidentis.analyst.face.HumanFace;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.api.IPosition;
......@@ -8,13 +9,19 @@ import org.ejml.simple.SimpleMatrix;
import org.ejml.simple.SimpleSVD;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.DataFormatException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import cz.fidentis.analyst.procrustes.ProcrustesAnalysisFaceModel;
import java.util.Comparator;
/**
* @author Jakub Kolman
......@@ -40,16 +47,39 @@ public class ProcrustesAnalysis {
HumanFace humanFace1,
HumanFace humanFace2) throws DataFormatException {
ProcrustesAnalysisFaceModel model1 = new ProcrustesAnalysisFaceModel(humanFace1);
ProcrustesAnalysisFaceModel model2 = new ProcrustesAnalysisFaceModel(humanFace2);
if (model1.getFeaturePointsMap().values().size() != model2.getFeaturePointsMap().values().size()) {
throw new DataFormatException("Lists of feature points do not have the same size");
}
if (!checkFeaturePointsType(
model1.getFeaturePointValues(), model2.getFeaturePointValues())) {
throw new DataFormatException("Lists of feature points do not have the same feature point types");
ProcrustesAnalysisFaceModel model1;
ProcrustesAnalysisFaceModel model2;
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(
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"),
"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");
}
} else {
model1 = new ProcrustesAnalysisFaceModel(humanFace1);
model2 = new ProcrustesAnalysisFaceModel(humanFace2);
}
this.faceModel1 = model1;
......@@ -426,4 +456,35 @@ public class ProcrustesAnalysis {
}
}
/**
* 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
*/
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) {
vaiablePoints.add(Integer.valueOf(fp.getFeaturePointType().getType()));
}
});
return vaiablePoints;
}
private List<FeaturePoint> sortListByFeaturePointType(List<FeaturePoint> featurePointList) {
Collections.sort(
featurePointList, Comparator.comparingInt(fp -> fp.getFeaturePointType().getType())
);
return featurePointList;
}
}
......@@ -44,6 +44,31 @@ public class ProcrustesAnalysisFaceModel {
this.featurePointTypeCorrespondence = createFeaturePointTypeCorrespondence(face.getFeaturePoints());
}
/**
* Constructor for face model if there is a need for a subset selection of
* feature points before applying procrustes analysis.
*
* @param face
* @param viablePoints
*/
public ProcrustesAnalysisFaceModel(HumanFace face, List<Integer> viablePoints) {
this.humanFace = face;
// getFeaturePoints() returns unmodifiable List. To sort it we need to make copy first
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);
// 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
......@@ -53,7 +78,7 @@ public class ProcrustesAnalysisFaceModel {
public void setFeaturePointsMap(HashMap<Integer, FeaturePoint> featurePointsMap) {
this.featurePointsMap = featurePointsMap;
readjustFeaturePoints(featurePointsMap);
// this.humanFace.setFeaturePoints(getFeaturePointValues());
}
......@@ -81,6 +106,16 @@ public class ProcrustesAnalysisFaceModel {
return featurePointTypeCorrespondence;
}
private List<FeaturePoint> getSelectionOfFeaturePoints(List<FeaturePoint> featurePoints, List<Integer> viablePoints) {
ArrayList<FeaturePoint> selection = new ArrayList<>();
featurePoints.forEach(fp -> {
if (viablePoints.contains(fp.getFeaturePointType().getType())) {
selection.add(fp);
}
});
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
......@@ -128,17 +163,44 @@ 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) {
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");
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;
}
fp.getPosition().x = movedFp.getX();
fp.getPosition().y = movedFp.getY();
fp.getPosition().z = movedFp.getZ();
});
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
......@@ -67,8 +67,7 @@ public class ProcrustesVisitor extends MeshVisitor {
public void visitMeshFacet(MeshFacet transformedFacet) {
try {
ProcrustesAnalysis procrustes = new ProcrustesAnalysis(this.primaryFace, this.secondaryFace, scale);
this.transformation = procrustes.analyze();
this.transformation = procrustes.analyze();
// applyTransformations(this.transformation, transformedFacet);
} catch (Exception e) {
System.err.println(e.getMessage());
......
......@@ -40,6 +40,8 @@ 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";
/**
* Main method
......@@ -52,7 +54,7 @@ public class ProcrustesVisitorTest {
FeaturePointImportService featurePointImportService = new FeaturePointImportService();
List<FeaturePoint> fpList1 = featurePointImportService.importFeaturePoints(
DATA_DIR.toString(), FACE_FP_1);
DATA_DIR.toString(), FACE_FP_ROTATION_1);
List<FeaturePoint> fpList2= featurePointImportService.importFeaturePoints(
DATA_DIR.toString(), FACE_FP_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,PAS_R x,PAS_R y,PAS_R z
0002_01,0,20,0,0,-20,0,0,0,-20,0,0,50,20,0,0
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment