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

[#38] feat: centering models on feature points centroid

parent c0329df4
No related branches found
No related tags found
No related merge requests found
Showing
with 385 additions and 0 deletions
package cz.fidentis.analyst.procrustes;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.procrustes.exceptions.ProcrustesAnalysisException;
import cz.fidentis.analyst.procrustes.utils.ProcrustesAnalysisUtils;
import java.util.List;
import javax.vecmath.Vector3f;
/**
* @author Jakub Kolman
*/
public class ProcrustesAnalysis {
private List<FeaturePoint> orderedFpList1;
private List<FeaturePoint> orderedFpList2;
private ProcrustesAnalysisFaceModel featurePointModel1;
private ProcrustesAnalysisFaceModel featurePointModel2;
private Vector3f centroid;
/**
* @param fpList1
* @param vertices1
* @param fpList2
* @param vertices2
* @throws ProcrustesAnalysisException
*/
public ProcrustesAnalysis(
List<FeaturePoint> fpList1, List<Vector3f> vertices1,
List<FeaturePoint> fpList2, List<Vector3f> vertices2) throws ProcrustesAnalysisException {
if (fpList1.size() != fpList2.size()) {
throw new ProcrustesAnalysisException("Lists of feature points do not have the same size");
}
if (!featurePointTypesEquivalence(fpList1, fpList2)) {
throw new ProcrustesAnalysisException("Lists of feature points do not have the same feature point types");
}
featurePointModel1 = new ProcrustesAnalysisFaceModel(fpList1, vertices1);
featurePointModel2 = new ProcrustesAnalysisFaceModel(fpList2, vertices2);
centroid = ProcrustesAnalysisUtils.findCenteroidOfFeaturePoints(orderedFpList1);
superImpose(featurePointModel1, featurePointModel2);
}
/**
* Imposes two face models (lists of feature points and vertices) over each other
*
* @param featurePointModel1
* @param featurePointModel2
*/
private void superImpose(ProcrustesAnalysisFaceModel featurePointModel1,
ProcrustesAnalysisFaceModel featurePointModel2) {
// moves vertices of face models
featurePointModel1.setVertices(moveVertices(centroid, featurePointModel1.getVertices()));
featurePointModel1.setVertices(moveVertices(centroid, featurePointModel2.getVertices()));
}
/**
* Calculates new vertices adjusted to the centroid by method {@see findCenteroidOfFeaturePoints},
* so moved featurepoints would correspond with the same place on face as they did before.
*
* @param centroid
* @param vertices
*/
private List<Vector3f> moveVertices(Vector3f centroid, List<Vector3f> vertices) {
if (vertices != null && centroid != null) {
List<Vector3f> movedVertices = null;
for (Vector3f v : vertices) {
float x = v.x + centroid.x;
float y = v.y + centroid.y;
float z = v.z + centroid.z;
movedVertices.add(new Vector3f(x, y, z));
}
return movedVertices;
}
throw new ProcrustesAnalysisException("Could not compute vertices locations after moving the centroid from model to feature points");
}
/**
* Creates sorted feature point lists and compares them whether they have
* the same feature point types.
*
* @param fpList1
* @param fpList2
* @return
*/
private boolean featurePointTypesEquivalence(List<FeaturePoint> fpList1, List<FeaturePoint> fpList2) {
orderedFpList1 = ProcrustesAnalysisUtils.sortListByFeaturePointType(fpList1);
orderedFpList2 = ProcrustesAnalysisUtils.sortListByFeaturePointType(fpList2);
return ProcrustesAnalysisUtils.checkfeaturePointsType(orderedFpList1, orderedFpList2);
}
/**
* Calculate scaling ratio of how much the appropriate object corresponding
* to the second feature point list has to be scale up or shrunk.
* <p>
* If returned ratioValue is greater 1 then it means that the second object
* should be scaled up ratioValue times. If returned ratioValue is smaller 1
* than the second object should be shrunk.
*
* @return ratioValue
*/
private double calculateScalingValue() {
double[] distancesOfList1 = ProcrustesAnalysisUtils.calculateMeanDistancesFromOrigin(orderedFpList1);
double[] distancesOfList2 = ProcrustesAnalysisUtils.calculateMeanDistancesFromOrigin(orderedFpList2);
double[] ratioArray = new double[distancesOfList1.length];
double ratioValue = 0;
for (int i = 0; i < distancesOfList1.length; i++) {
ratioArray[i] += distancesOfList1[i] / distancesOfList2[i];
}
for (double ratio : ratioArray) {
ratioValue += ratio;
}
return ratioValue / distancesOfList1.length;
}
}
/*
* 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;
/**
*
* @author Jakub Kolman
*/
public class ProcrustesAnalysisComparison {
}
/*
* 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.feature.FeaturePoint;
import cz.fidentis.analyst.procrustes.utils.ProcrustesAnalysisUtils;
import javax.vecmath.Vector3f;
import java.util.HashMap;
import java.util.List;
/**
*
* @author Jakub Kolman
*/
public class ProcrustesAnalysisFaceModel {
private final HashMap<Integer, FeaturePoint> featurePointsMap;
private List<Vector3f> vertices;
public ProcrustesAnalysisFaceModel(List<FeaturePoint> fpList, List<Vector3f> vertices) {
this.featurePointsMap = ProcrustesAnalysisUtils.generateFeaturePointHashMap(fpList);
this.vertices = vertices;
}
public void setVertices(List<Vector3f> vertices) {
this.vertices = vertices;
}
public List<Vector3f> getVertices() {
return vertices;
}
}
/*
* 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.exceptions;
/**
*
* @author Jakub Kolman
*/
public class ProcrustesAnalysisException extends RuntimeException {
/**
* throws run time exception with message
* @param message
*/
public ProcrustesAnalysisException(String message) {
super(message);
}
}
/*
* 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.utils;
import cz.fidentis.analyst.feature.FeaturePoint;
import javax.vecmath.Vector3f;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
/**
*
* @author Jakub Kolman
*/
public class ProcrustesAnalysisUtils {
/**
* Sorts List by featurePoint type property
*
* @param featurePointList
* @return ordered list of feature points by type
*/
public static List<FeaturePoint> sortListByFeaturePointType(List<FeaturePoint> featurePointList) {
Collections.sort(
featurePointList, (FeaturePoint fp1, FeaturePoint fp2)
-> fp1.getFeaturePointType().getType() - fp2.getFeaturePointType().getType() // Ascending
);
return featurePointList;
}
public static HashMap<Integer, FeaturePoint> generateFeaturePointHashMap(List<FeaturePoint> featurePointList) {
HashMap<Integer, FeaturePoint> map = new HashMap<>();
for (FeaturePoint fp : featurePointList) {
if (fp.getFeaturePointType().getType() > 0) {
map.put(fp.getFeaturePointType().getType(), fp);
}
}
return map;
}
/**
* Checks if two feature point lists have the same types of feature points.
*
* To use this method you need to supply ordered lists by Feature Point type
* as parameters. Otherwise even if two lists contain the same featurepoints
* it will return false.
*
* Use sort function {@link #sortByFeaturePointType} on feature point lists
* two get correct results.
*
* @param featurePointList1
* @param featurePointList2
* @return true if two sorted lists by the feature point type contain the
* same feature point types
*/
public static boolean checkfeaturePointsType(List<FeaturePoint> featurePointList1, List<FeaturePoint> featurePointList2) {
int i = 0;
while (i < featurePointList1.size()) {
if (featurePointList1.get(i).getFeaturePointType().getType() != featurePointList2.get(i).getFeaturePointType().getType()) {
return false;
}
}
return true;
}
/**
* Creates array of distance values from feature point to origin (0,0,0).
*
* @param featurePointList
* @return array of distances
*/
public static double[] calculateMeanDistancesFromOrigin(List<FeaturePoint> featurePointList) {
double[] distances = new double[featurePointList.size()];
for (int i = 0; i < featurePointList.size(); i++) {
distances[i] = calculatePointDistanceFromOrigin(featurePointList.get(i));
}
return distances;
}
/**
* Finds centrioid from given feature point List
*
* @param featurePointList
* @return centroid of feature points (Vector3F)
*/
public static Vector3f findCenteroidOfFeaturePoints(List<FeaturePoint> featurePointList) {
float x = 0;
float y = 0;
float z = 0;
for (FeaturePoint fp : featurePointList) {
x += fp.getX();
y += fp.getY();
z += fp.getZ();
}
return new Vector3f(x / featurePointList.size(), y / featurePointList.size(), z / featurePointList.size());
}
// /**
// * Calculates distance of one feature point from another
//
// * @param fp1
// * @param fp2
// * @return
// */
// public static double calculateDistanceOfTwoPoints(FeaturePoint fp1, FeaturePoint fp2) {
// return Math.sqrt(
// (Math.pow(fp1.getX(), 2) - Math.pow(fp2.getX(), 2))
// + (Math.pow(fp1.getY(), 2) - Math.pow(fp2.getY(), 2))
// + (Math.pow(fp1.getZ(), 2) - Math.pow(fp2.getZ(), 2)));
// }
/**
* Calculates distance of single feature point from origin (0,0,0)
*
* @param fp
* @return
*/
public static double calculatePointDistanceFromOrigin(FeaturePoint fp) {
return Math.sqrt(
Math.pow(fp.getX(), 2)
+ Math.pow(fp.getY(), 2)
+ Math.pow(fp.getZ(), 2));
}
}
/*
* 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.comparison.procrustesAnalysis.utils;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.provider.FeaturePointTypeProvider;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import cz.fidentis.analyst.feature.services.FeaturePointCsvLoader;
import cz.fidentis.analyst.procrustes.utils.ProcrustesAnalysisUtils;
import java.io.IOException;
import java.util.Collections;
/**
*
* @author Jakub Kolman
*/
public class ProcurstesAnalysisUtilsTest {
private static final Path testFileDirectory = Paths.get("src", "test", "resources", "cz", "fidentis", "analyst", "comparison", "procrustesAnalysis");
// src\test\resources\cz\fidentis\analyst\comparison\procrustesAnalysis
private static final String SORTED_FILE = "landmarksSorted.csv";
private static final String NOT_SORTED_FILE = "landmarksNotSorted.csv";
@DisplayName("Test sorting utility sortByFeaturePointType")
@Test
void sortByTypeTest() throws IOException {
List<FeaturePoint> featurePointsSortedList = FeaturePointCsvLoader.loadFeaturePoints(testFileDirectory.toString(), SORTED_FILE);
List<FeaturePoint> featurePointsNotSortedList = FeaturePointCsvLoader.loadFeaturePoints(testFileDirectory.toString(), NOT_SORTED_FILE);
List<FeaturePoint> sortedListByType = ProcrustesAnalysisUtils.sortListByFeaturePointType(featurePointsNotSortedList);
assertTrue(featurePointsSortedList != null);
assertTrue(featurePointsNotSortedList != null);
assertTrue(featurePointsSortedList.size() > 0);
assertTrue(featurePointsNotSortedList.size() > 0);
assertTrue(featurePointsSortedList.equals(sortedListByType));
}
// No longer used since imported feature points are no longer optional
// private static <T> List<T> toList(Optional<List<T>> opt) {
// return opt.orElse( Collections.<T>emptyList() );
// }
}
Scan name,EX_R x,EX_R y,EX_R z,N x,N y,N z,G x,G y,G z,SN x,SN y,SN z
00007_01,1,2,3,4,5,6,7,8,9,10,11,12
Scan name,EX_R x,EX_R y,EX_R z,G x,G y,G z,SN x,SN y,SN z,N x,N y,N z
00007_01,1,2,3,7,8,9,10,11,12,4,5,6
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