Skip to content
Snippets Groups Projects
Commit 16c9fcaa authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch '180-adaptive-downsampling-for-icp' into 'master'

Resolve "Adaptive downsampling for ICP"

Closes #180

See merge request grp-fidentis/analyst2!190
parents eacd7eef 5b791b26
No related branches found
No related tags found
No related merge requests found
...@@ -9,6 +9,7 @@ import cz.fidentis.analyst.mesh.core.MeshPoint; ...@@ -9,6 +9,7 @@ import cz.fidentis.analyst.mesh.core.MeshPoint;
import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling;
import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -81,6 +82,29 @@ public class IcpTransformer extends MeshVisitor { ...@@ -81,6 +82,29 @@ public class IcpTransformer extends MeshVisitor {
private final PointSampling samplingStrategy; private final PointSampling samplingStrategy;
/**
* Constructor for random sampling, which is the best performing downsampling strategy.
*
* @param mainFacet Primary mesh facet. Must not be {@code null}.
* Inspected facets are transformed toward this primary mesh.
* @param maxIteration Maximal number of ICP iterations (it includes computing
* new transformation and applying it). A number bigger than zero.
* Reasonable number seems to be 10.
* @param scale If {@code true}, then the scale factor is also computed.
* @param error Acceptable error - a number bigger than or equal to zero.
* Mean distance of vertices is computed for each ICP iteration.
* If the difference between the previous and current mean distances is less than the error,
* then the ICP computation stops. Reasonable number seems to be 0.05.
* @param numSamples Number of samples for downsampling. Use 1000 for best performance. Zero = no downsampling
* @throws IllegalArgumentException if some parameter is wrong
*/
public IcpTransformer(MeshFacet mainFacet, int maxIteration, boolean scale, double error, int numSamples) {
this(new HashSet<>(Collections.singleton(mainFacet)), maxIteration, scale, error, (numSamples == 0) ? new NoSampling() : new RandomSampling(numSamples));
if (mainFacet == null) {
throw new IllegalArgumentException("mainFacet");
}
}
/** /**
* Constructor. * Constructor.
* *
...@@ -90,8 +114,10 @@ public class IcpTransformer extends MeshVisitor { ...@@ -90,8 +114,10 @@ public class IcpTransformer extends MeshVisitor {
* new transformation and applying it). A number bigger than zero. * new transformation and applying it). A number bigger than zero.
* Reasonable number seems to be 10. * Reasonable number seems to be 10.
* @param scale If {@code true}, then the scale factor is also computed. * @param scale If {@code true}, then the scale factor is also computed.
* @param error Acceptable error. A number bigger than or equal to zero. * @param error Acceptable error - a number bigger than or equal to zero.
* When reached, then the ICP stops. Reasonable number seems to be 0.05. * Mean distance of vertices is computed for each ICP iteration.
* If the difference between the previous and current mean distances is less than the error,
* then the ICP computation stops. Reasonable number seems to be 0.05.
* @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
* @throws IllegalArgumentException if some parameter is wrong * @throws IllegalArgumentException if some parameter is wrong
*/ */
...@@ -111,8 +137,10 @@ public class IcpTransformer extends MeshVisitor { ...@@ -111,8 +137,10 @@ public class IcpTransformer extends MeshVisitor {
* new transformation and applying it). A number bigger than zero. * new transformation and applying it). A number bigger than zero.
* Reasonable number seems to be 10. * Reasonable number seems to be 10.
* @param scale If {@code true}, then the scale factor is also computed. * @param scale If {@code true}, then the scale factor is also computed.
* @param error Acceptable error. A number bugger than or equal to zero. * @param error Acceptable error - a number bigger than or equal to zero.
* When reached, then the ICP stops. Reasonable number seems to be 0.05. * Mean distance of vertices is computed for each ICP iteration.
* If the difference between the previous and current mean distances is less than the error,
* then the ICP computation stops. Reasonable number seems to be 0.05.
* @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
* @throws IllegalArgumentException if some parameter is wrong * @throws IllegalArgumentException if some parameter is wrong
*/ */
...@@ -142,8 +170,10 @@ public class IcpTransformer extends MeshVisitor { ...@@ -142,8 +170,10 @@ public class IcpTransformer extends MeshVisitor {
* new transformation and applying it). A number bigger than zero. * new transformation and applying it). A number bigger than zero.
* Reasonable number seems to be 10. * Reasonable number seems to be 10.
* @param scale If {@code true}, then the scale factor is also computed. * @param scale If {@code true}, then the scale factor is also computed.
* @param error Acceptable error. A number bugger than or equal to zero. * @param error Acceptable error - a number bigger than or equal to zero.
* When reached, then the ICP stops. Reasonable number seems to be 0.05. * Mean distance of vertices is computed for each ICP iteration.
* If the difference between the previous and current mean distances is less than the error,
* then the ICP computation stops. Reasonable number seems to be 0.05.
* @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
* @throws IllegalArgumentException if some parameter is wrong * @throws IllegalArgumentException if some parameter is wrong
*/ */
...@@ -163,7 +193,10 @@ public class IcpTransformer extends MeshVisitor { ...@@ -163,7 +193,10 @@ public class IcpTransformer extends MeshVisitor {
* new transformation and applying it). A number bigger than zero. * new transformation and applying it). A number bigger than zero.
* Reasonable number seems to be 10. * Reasonable number seems to be 10.
* @param scale If {@code true}, then the scale factor is also computed. * @param scale If {@code true}, then the scale factor is also computed.
* @param error Acceptable error. A number bugger than or equal to zero. When reached, then the ICP stops. * @param error Acceptable error - a number bigger than or equal to zero.
* Mean distance of vertices is computed for each ICP iteration.
* If the difference between the previous and current mean distances is less than the error,
* then the ICP computation stops. Reasonable number seems to be 0.05.
* @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
* @throws IllegalArgumentException if some parameter is wrong * @throws IllegalArgumentException if some parameter is wrong
*/ */
...@@ -259,6 +292,8 @@ public class IcpTransformer extends MeshVisitor { ...@@ -259,6 +292,8 @@ public class IcpTransformer extends MeshVisitor {
false // auto crop false // auto crop
); );
//int numSamples = samplingStrategy.getNumDownsampledPoints(transformedFacet.getNumberOfVertices());
//samplingStrategy.setRequiredSamples(200);
MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy); MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy);
int currentIteration = 0; int currentIteration = 0;
...@@ -289,7 +324,12 @@ public class IcpTransformer extends MeshVisitor { ...@@ -289,7 +324,12 @@ public class IcpTransformer extends MeshVisitor {
applyTransformation(reducedFacet, transformation); applyTransformation(reducedFacet, transformation);
} }
currentIteration ++; currentIteration++;
//if (currentIteration >= 3) {
// samplingStrategy.setRequiredSamples(numSamples);
// reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy);
//}
} }
} }
......
...@@ -134,7 +134,7 @@ public abstract class PointSampling extends MeshVisitor { ...@@ -134,7 +134,7 @@ public abstract class PointSampling extends MeshVisitor {
* @param origPoints Original number of vertices * @param origPoints Original number of vertices
* @return number of points to be returned after downsampling. * @return number of points to be returned after downsampling.
*/ */
protected int getNumDownsampledPoints(int origPoints) { public int getNumDownsampledPoints(int origPoints) {
switch (this.samplingType) { switch (this.samplingType) {
case PERCENTAGE: case PERCENTAGE:
return (int) (origPoints * this.samplingLimit); return (int) (origPoints * this.samplingLimit);
......
...@@ -66,7 +66,7 @@ public class BatchPanel extends ControlPanel { ...@@ -66,7 +66,7 @@ public class BatchPanel extends ControlPanel {
jButton1.addActionListener(createListener(action, ACTION_COMMAND_COMPUTE_ICP)); jButton1.addActionListener(createListener(action, ACTION_COMMAND_COMPUTE_ICP));
spinSlider1.initInteger(2000, 0, 5000, 1); spinSlider1.initInteger(1000, 0, 5000, 1); // downsampling strength
jComboBox2.addItem(SIMILARITY_APPROX_HD); jComboBox2.addItem(SIMILARITY_APPROX_HD);
jComboBox2.addItem(SIMILARITY_COMPLETE_HD); jComboBox2.addItem(SIMILARITY_COMPLETE_HD);
......
...@@ -27,6 +27,9 @@ import javax.swing.SwingWorker; ...@@ -27,6 +27,9 @@ import javax.swing.SwingWorker;
* @author Radek Oslejsek * @author Radek Oslejsek
*/ */
public class IcpTask extends SwingWorker<MeshModel, HumanFace> { public class IcpTask extends SwingWorker<MeshModel, HumanFace> {
private static final double ICP_ERROR = 0.3; //0.05;
private static final int ICP_ITERS = 100;
private HumanFace avgFace; private HumanFace avgFace;
private final ProgressDialog progressDialog; private final ProgressDialog progressDialog;
...@@ -121,14 +124,14 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> { ...@@ -121,14 +124,14 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> {
HumanFaceUtils.alignMeshes( HumanFaceUtils.alignMeshes(
initFace, initFace,
face, // is transformed face, // is transformed
100, // max iterations ICP_ITERS, // max iterations
controlPanel.scaleIcp(), controlPanel.scaleIcp(),
0.3, // error ICP_ERROR, // error
sampling, sampling,
false // drop k-d tree, if exists false // drop k-d tree, if exists
); );
icpComputationTime.stop(); icpComputationTime.stop();
System.out.println(sampling); //System.out.println(sampling);
} }
if (computeAvgFace) { // AVG template face if (computeAvgFace) { // AVG template face
......
...@@ -3,10 +3,9 @@ package cz.fidentis.analyst.tests; ...@@ -3,10 +3,9 @@ package cz.fidentis.analyst.tests;
import cz.fidentis.analyst.batch.Stopwatch; import cz.fidentis.analyst.batch.Stopwatch;
import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.face.HumanFace;
import cz.fidentis.analyst.face.HumanFaceUtils; import cz.fidentis.analyst.face.HumanFaceUtils;
import cz.fidentis.analyst.icp.UndersampledMeshFacet;
import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.mesh.core.MeshModel; import cz.fidentis.analyst.mesh.core.MeshModel;
import cz.fidentis.analyst.symmetry.Plane;
import cz.fidentis.analyst.symmetry.SymmetryEstimator;
import cz.fidentis.analyst.symmetry.SymmetryEstimatorRobust;
import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling; import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling;
import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling;
...@@ -19,6 +18,7 @@ import java.io.IOException; ...@@ -19,6 +18,7 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
...@@ -48,13 +48,13 @@ public class IcpDownsampling { ...@@ -48,13 +48,13 @@ public class IcpDownsampling {
.limit(MAX_SAMPLES) .limit(MAX_SAMPLES)
.collect(Collectors.toList()); .collect(Collectors.toList());
SortedMap<Integer, Stopwatch> efficiency = new TreeMap<>(); SortedMap<Integer, Stopwatch> efficiency = new TreeMap<>(Collections.reverseOrder());
SortedMap<Integer, List<Double>> precision = new TreeMap<>(); SortedMap<Integer, List<Double>> precision = new TreeMap<>(Collections.reverseOrder());
//String alg = "random"; String alg = "random";
//String alg = "gaussian"; //String alg = "gaussian";
//String alg = "uniform space"; //String alg = "uniform space";
String alg = "uniform mesh"; //String alg = "uniform mesh";
int counter = 1; int counter = 1;
for (int i = 0; i < faces.size(); i += 5) { for (int i = 0; i < faces.size(); i += 5) {
...@@ -78,7 +78,7 @@ public class IcpDownsampling { ...@@ -78,7 +78,7 @@ public class IcpDownsampling {
}); });
System.out.println(); System.out.println();
System.out.println("Avg. Precision - " + sampling + " sampling:"); System.out.println("Avg. Precision - " + sampling + " sampling:");
System.out.println("# samples;average similarity;variance"); System.out.println("# samples;dissimilarity;variance");
precision.entrySet().forEach(e -> { precision.entrySet().forEach(e -> {
double avg = e.getValue().stream() double avg = e.getValue().stream()
.mapToDouble(val -> val) .mapToDouble(val -> val)
...@@ -100,64 +100,86 @@ public class IcpDownsampling { ...@@ -100,64 +100,86 @@ public class IcpDownsampling {
//double[] percs = new double[]{0.5,1,2,4,6,8,10,15,20,30,40,50,60,70,80,85,90,92,94,96,98,100}; //double[] percs = new double[]{0.5,1,2,4,6,8,10,15,20,30,40,50,60,70,80,85,90,92,94,96,98,100};
//double[] percs = new double[]{100,90,80,70,60,50,40,30,20,15,10,8,6,4,2,1,0.5}; //double[] percs = new double[]{100,90,80,70,60,50,40,30,20,15,10,8,6,4,2,1,0.5};
//int[] samples = new int[]{0,200,500,1000,2000,3000,5000,10000,20000,30000,40000}; //int[] samples = new int[]{0,200,500,1000,2000,3000,5000,10000,20000,30000,40000};
int[] samples = new int[]{0,200,500,1000,2000,3000,5000}; //int[] samples = new int[]{0,200,500,1000,2000,3000,5000};
//int[] samples = new int[]{50,100,150,300,400};
//int[] samples = new int[]{0,50,100,150,200,300,400,500,1000,2000,3000,5000,10000,20000,30000};
//int[] samples = new int[]{5,10,20,30,40,50};
int[] samples = new int[]{0,40,50,100,150,200,300,400,500,1000,2000,3000,5000,10000,20000,30000};
HumanFace priFace = new HumanFace(priFacePath.toFile()); HumanFace priFace = new HumanFace(priFacePath.toFile());
priFace.computeKdTree(false); priFace.computeKdTree(false);
SymmetryEstimator seVisitor = new SymmetryEstimatorRobust(new UniformSpaceSampling(), 100, 1000);
priFace.getMeshModel().compute(seVisitor);
priFace.setSymmetryPlane(seVisitor.getSymmetryPlane());
HumanFace secFaceFromFile = new HumanFace(secFacePath.toFile()); HumanFace secFaceFromFile = new HumanFace(secFacePath.toFile());
seVisitor = new SymmetryEstimatorRobust(new UniformSpaceSampling(), 100, 1000);
secFaceFromFile.getMeshModel().compute(seVisitor);
secFaceFromFile.setSymmetryPlane(seVisitor.getSymmetryPlane());
System.out.println(secFaceFromFile.getShortName()); System.out.println(secFaceFromFile.getShortName());
for (int i: samples) { for (int i: samples) {
HumanFace secFace = new HumanFace(new MeshModel(secFaceFromFile.getMeshModel()), secFaceFromFile.getId()); HumanFace secFace = new HumanFace(new MeshModel(secFaceFromFile.getMeshModel()), secFaceFromFile.getId());
secFace.setSymmetryPlane(new Plane(secFaceFromFile.getSymmetryPlane()));
PointSampling sampling; PointSampling secSampling;
PointSampling priSampling;
switch (samp) { switch (samp) {
case "random": case "random":
sampling = new RandomSampling(); secSampling = new RandomSampling();
priSampling = new RandomSampling();
break; break;
case "gaussian": case "gaussian":
sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN); secSampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN);
priSampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN);
break; break;
case "uniform space": case "uniform space":
sampling = new UniformSpaceSampling(); secSampling = new UniformSpaceSampling();
priSampling = new UniformSpaceSampling();
break; break;
case "uniform mesh": case "uniform mesh":
sampling = new UniformSurfaceSampling(); secSampling = new UniformSurfaceSampling();
priSampling = new UniformSurfaceSampling();
break; break;
default: default:
return; return;
} }
/*
if (samples[i] == 0) {
secSampling = new NoSampling();
priSampling = new NoSampling();
} else {
secSampling.setRequiredSamples(i);
priSampling.setRequiredSamples(i);
}
*/
if (i == 0) { if (i == 0) {
sampling = new NoSampling(); priSampling = new NoSampling();
} else { } else {
//sampling.setRequiredSamples(i/100.0); priSampling.setRequiredSamples(i);
sampling.setRequiredSamples(i);
} }
secSampling.setRequiredSamples(5000);
efficiency.computeIfAbsent(i, k-> new Stopwatch("")).start(); efficiency.computeIfAbsent(i, k-> new Stopwatch("")).start();
HumanFaceUtils.alignSymmetryPlanes(priFace, secFace, false, true); HumanFaceUtils.alignMeshes(
HumanFaceUtils.alignMeshes( downsamplePriFace(priFace, priSampling),
secFace, // is transformed
100, // max iterations
false,// scale
0.05, // error
secSampling,
false // drop k-d tree of the secFace, if exists
);
/*
HumanFaceUtils.alignMeshes( /// ORIG SETTING
priFace, priFace,
secFace, // is transformed secFace, // is transformed
100, // max iterations 100, // max iterations
false,// scale false,// scale
0.3, // error 0.05, // error
sampling, secSampling,
false // drop k-d tree, if exists false // drop k-d tree of the secFace, if exists
); );
*/
efficiency.get(i).stop(); efficiency.get(i).stop();
System.out.println("" + sampling); System.out.println(" Primary face: " + priSampling);
System.out.println("Secondary face: " + secSampling);
HausdorffDistance hd = new HausdorffDistance( HausdorffDistance hd = new HausdorffDistance(
priFace.getKdTree(), priFace.getKdTree(),
...@@ -171,4 +193,12 @@ public class IcpDownsampling { ...@@ -171,4 +193,12 @@ public class IcpDownsampling {
} }
System.out.println(); System.out.println();
} }
private static HumanFace downsamplePriFace(HumanFace priFace, PointSampling sampling) {
MeshFacet reducedFacet = new UndersampledMeshFacet(priFace.getMeshModel().getFacets().get(0), sampling);
MeshModel model = new MeshModel();
model.setFacets(List.of(reducedFacet));
HumanFace ret = new HumanFace(model, priFace.getId());
return ret;
}
} }
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