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; package cz.fidentis.analyst.procrustes;
import cz.fidentis.analyst.Logger;
import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.face.HumanFace;
import cz.fidentis.analyst.feature.FeaturePoint; import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.api.IPosition; import cz.fidentis.analyst.feature.api.IPosition;
...@@ -8,13 +9,19 @@ import org.ejml.simple.SimpleMatrix; ...@@ -8,13 +9,19 @@ import org.ejml.simple.SimpleMatrix;
import org.ejml.simple.SimpleSVD; import org.ejml.simple.SimpleSVD;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.vecmath.Point3d; import javax.vecmath.Point3d;
import javax.vecmath.Vector3d; import javax.vecmath.Vector3d;
import cz.fidentis.analyst.procrustes.ProcrustesAnalysisFaceModel;
import java.util.Comparator;
/** /**
* @author Jakub Kolman * @author Jakub Kolman
...@@ -40,16 +47,39 @@ public class ProcrustesAnalysis { ...@@ -40,16 +47,39 @@ public class ProcrustesAnalysis {
HumanFace humanFace1, HumanFace humanFace1,
HumanFace humanFace2) throws DataFormatException { HumanFace humanFace2) throws DataFormatException {
ProcrustesAnalysisFaceModel model1 = new ProcrustesAnalysisFaceModel(humanFace1); ProcrustesAnalysisFaceModel model1;
ProcrustesAnalysisFaceModel model2 = new ProcrustesAnalysisFaceModel(humanFace2); ProcrustesAnalysisFaceModel model2;
if (model1.getFeaturePointsMap().values().size() != model2.getFeaturePointsMap().values().size()) { if (humanFace1.getFeaturePoints().size() != humanFace2.getFeaturePoints().size()
throw new DataFormatException("Lists of feature points do not have the same size"); || !checkFeaturePointsType(
} sortListByFeaturePointType(humanFace1.getFeaturePoints()),
sortListByFeaturePointType(humanFace1.getFeaturePoints()))) {
if (!checkFeaturePointsType(
model1.getFeaturePointValues(), model2.getFeaturePointValues())) { // int n = 0;
throw new DataFormatException("Lists of feature points do not have the same feature point types"); 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; this.faceModel1 = model1;
...@@ -426,4 +456,35 @@ public class ProcrustesAnalysis { ...@@ -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 { ...@@ -44,6 +44,31 @@ public class ProcrustesAnalysisFaceModel {
this.featurePointTypeCorrespondence = createFeaturePointTypeCorrespondence(face.getFeaturePoints()); 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 * sets feature points map and also sets feature point on human face for
* visualisation * visualisation
...@@ -53,7 +78,7 @@ public class ProcrustesAnalysisFaceModel { ...@@ -53,7 +78,7 @@ public class ProcrustesAnalysisFaceModel {
public void setFeaturePointsMap(HashMap<Integer, FeaturePoint> featurePointsMap) { public void setFeaturePointsMap(HashMap<Integer, FeaturePoint> featurePointsMap) {
this.featurePointsMap = featurePointsMap; this.featurePointsMap = featurePointsMap;
readjustFeaturePoints(featurePointsMap); readjustFeaturePoints(featurePointsMap);
// this.humanFace.setFeaturePoints(getFeaturePointValues()); // this.humanFace.setFeaturePoints(getFeaturePointValues());
} }
...@@ -81,6 +106,16 @@ public class ProcrustesAnalysisFaceModel { ...@@ -81,6 +106,16 @@ public class ProcrustesAnalysisFaceModel {
return featurePointTypeCorrespondence; 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 * 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 * point type value. It is used so we can get back type of feature point
...@@ -128,17 +163,44 @@ public class ProcrustesAnalysisFaceModel { ...@@ -128,17 +163,44 @@ public class ProcrustesAnalysisFaceModel {
} }
private void readjustFeaturePoints(HashMap<Integer, FeaturePoint> featurePointsMap) { 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 -> { this.humanFace.getFeaturePoints().forEach(fp -> {
FeaturePoint movedFp = featurePointsMap.get(fp.getFeaturePointType().getType()); FeaturePoint movedFp = featurePointsMap.get(fp.getFeaturePointType().getType());
if (fp.getFeaturePointType().getType() if (movedFp != null) {
!= movedFp.getFeaturePointType().getType()) { if (fp.getFeaturePointType().getType()
throw new RuntimeException("Types do not correspond"); != 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 { ...@@ -67,8 +67,7 @@ public class ProcrustesVisitor extends MeshVisitor {
public void visitMeshFacet(MeshFacet transformedFacet) { public void visitMeshFacet(MeshFacet transformedFacet) {
try { try {
ProcrustesAnalysis procrustes = new ProcrustesAnalysis(this.primaryFace, this.secondaryFace, scale); ProcrustesAnalysis procrustes = new ProcrustesAnalysis(this.primaryFace, this.secondaryFace, scale);
this.transformation = procrustes.analyze(); this.transformation = procrustes.analyze();
// applyTransformations(this.transformation, transformedFacet); // applyTransformations(this.transformation, transformedFacet);
} catch (Exception e) { } catch (Exception e) {
System.err.println(e.getMessage()); System.err.println(e.getMessage());
......
...@@ -40,6 +40,8 @@ public class ProcrustesVisitorTest { ...@@ -40,6 +40,8 @@ public class ProcrustesVisitorTest {
private static final String FACE_2 = "0002_02_ECA.obj"; 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_1 = "0002_01_landmarks.csv";
private static final String FACE_FP_2 = "0002_02_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 * Main method
...@@ -52,7 +54,7 @@ public class ProcrustesVisitorTest { ...@@ -52,7 +54,7 @@ public class ProcrustesVisitorTest {
FeaturePointImportService featurePointImportService = new FeaturePointImportService(); FeaturePointImportService featurePointImportService = new FeaturePointImportService();
List<FeaturePoint> fpList1 = featurePointImportService.importFeaturePoints( List<FeaturePoint> fpList1 = featurePointImportService.importFeaturePoints(
DATA_DIR.toString(), FACE_FP_1); DATA_DIR.toString(), FACE_FP_ROTATION_1);
List<FeaturePoint> fpList2= featurePointImportService.importFeaturePoints( List<FeaturePoint> fpList2= featurePointImportService.importFeaturePoints(
DATA_DIR.toString(), FACE_FP_2); 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