diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java index 022861ed796af59bdf5d128b0b3d823f846c6178..da76e5a2cfba20e2c22a7dd38df46888db0cf865 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java @@ -9,6 +9,7 @@ import cz.fidentis.analyst.mesh.core.MeshPoint; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -81,6 +82,29 @@ public class IcpTransformer extends MeshVisitor { 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. * @@ -90,8 +114,10 @@ public class IcpTransformer extends MeshVisitor { * 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. - * When reached, then the ICP stops. Reasonable number seems to be 0.05. + * @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. * @throws IllegalArgumentException if some parameter is wrong */ @@ -111,8 +137,10 @@ public class IcpTransformer extends MeshVisitor { * 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 bugger than or equal to zero. - * When reached, then the ICP stops. Reasonable number seems to be 0.05. + * @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. * @throws IllegalArgumentException if some parameter is wrong */ @@ -142,8 +170,10 @@ public class IcpTransformer extends MeshVisitor { * 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 bugger than or equal to zero. - * When reached, then the ICP stops. Reasonable number seems to be 0.05. + * @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. * @throws IllegalArgumentException if some parameter is wrong */ @@ -163,7 +193,10 @@ public class IcpTransformer extends MeshVisitor { * 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 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. * @throws IllegalArgumentException if some parameter is wrong */ @@ -259,6 +292,8 @@ public class IcpTransformer extends MeshVisitor { false // auto crop ); + //int numSamples = samplingStrategy.getNumDownsampledPoints(transformedFacet.getNumberOfVertices()); + //samplingStrategy.setRequiredSamples(200); MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy); int currentIteration = 0; @@ -289,7 +324,12 @@ public class IcpTransformer extends MeshVisitor { applyTransformation(reducedFacet, transformation); } - currentIteration ++; + currentIteration++; + + //if (currentIteration >= 3) { + // samplingStrategy.setRequiredSamples(numSamples); + // reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy); + //} } } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java index 12202b757e2b2eef05e13e772aa2eaa94a28a89d..5890d1fd81af6a3dcaa5320580a62c1648f38eea 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java @@ -134,7 +134,7 @@ public abstract class PointSampling extends MeshVisitor { * @param origPoints Original number of vertices * @return number of points to be returned after downsampling. */ - protected int getNumDownsampledPoints(int origPoints) { + public int getNumDownsampledPoints(int origPoints) { switch (this.samplingType) { case PERCENTAGE: return (int) (origPoints * this.samplingLimit); diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java index be2eaeb35119355b61a7672e6378c8a53a642fd5..8b6ea4c9fe008329b5c0dbdc99c0a9f6ad88fc45 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java @@ -66,7 +66,7 @@ public class BatchPanel extends ControlPanel { 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_COMPLETE_HD); diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java b/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java index 67d6cf51a44497be34a84d5e271cee51043c6fb9..886e86471b9989c3c03565a833233f4f825cce9d 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java @@ -27,6 +27,9 @@ import javax.swing.SwingWorker; * @author Radek Oslejsek */ 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 final ProgressDialog progressDialog; @@ -121,14 +124,14 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> { HumanFaceUtils.alignMeshes( initFace, face, // is transformed - 100, // max iterations + ICP_ITERS, // max iterations controlPanel.scaleIcp(), - 0.3, // error + ICP_ERROR, // error sampling, false // drop k-d tree, if exists ); icpComputationTime.stop(); - System.out.println(sampling); + //System.out.println(sampling); } if (computeAvgFace) { // AVG template face diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java b/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java index 216a461cc2b1fd2914156fc8a882f2c631bcb1f8..2df712c7c4643955b4197f60c31b55d97d5d21bf 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java +++ b/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java @@ -3,10 +3,9 @@ package cz.fidentis.analyst.tests; import cz.fidentis.analyst.batch.Stopwatch; import cz.fidentis.analyst.face.HumanFace; 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.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.sampling.CurvatureSampling; import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; @@ -19,6 +18,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.SortedMap; @@ -48,13 +48,13 @@ public class IcpDownsampling { .limit(MAX_SAMPLES) .collect(Collectors.toList()); - SortedMap<Integer, Stopwatch> efficiency = new TreeMap<>(); - SortedMap<Integer, List<Double>> precision = new TreeMap<>(); + SortedMap<Integer, Stopwatch> efficiency = new TreeMap<>(Collections.reverseOrder()); + SortedMap<Integer, List<Double>> precision = new TreeMap<>(Collections.reverseOrder()); - //String alg = "random"; + String alg = "random"; //String alg = "gaussian"; //String alg = "uniform space"; - String alg = "uniform mesh"; + //String alg = "uniform mesh"; int counter = 1; for (int i = 0; i < faces.size(); i += 5) { @@ -78,7 +78,7 @@ public class IcpDownsampling { }); System.out.println(); System.out.println("Avg. Precision - " + sampling + " sampling:"); - System.out.println("# samples;average similarity;variance"); + System.out.println("# samples;dissimilarity;variance"); precision.entrySet().forEach(e -> { double avg = e.getValue().stream() .mapToDouble(val -> val) @@ -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[]{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}; + //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()); 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()); - seVisitor = new SymmetryEstimatorRobust(new UniformSpaceSampling(), 100, 1000); - secFaceFromFile.getMeshModel().compute(seVisitor); - secFaceFromFile.setSymmetryPlane(seVisitor.getSymmetryPlane()); - System.out.println(secFaceFromFile.getShortName()); for (int i: samples) { HumanFace secFace = new HumanFace(new MeshModel(secFaceFromFile.getMeshModel()), secFaceFromFile.getId()); - secFace.setSymmetryPlane(new Plane(secFaceFromFile.getSymmetryPlane())); - PointSampling sampling; + PointSampling secSampling; + PointSampling priSampling; switch (samp) { case "random": - sampling = new RandomSampling(); + secSampling = new RandomSampling(); + priSampling = new RandomSampling(); break; case "gaussian": - sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN); + secSampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN); + priSampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN); break; case "uniform space": - sampling = new UniformSpaceSampling(); + secSampling = new UniformSpaceSampling(); + priSampling = new UniformSpaceSampling(); break; case "uniform mesh": - sampling = new UniformSurfaceSampling(); + secSampling = new UniformSurfaceSampling(); + priSampling = new UniformSurfaceSampling(); break; default: return; } + /* + if (samples[i] == 0) { + secSampling = new NoSampling(); + priSampling = new NoSampling(); + } else { + secSampling.setRequiredSamples(i); + priSampling.setRequiredSamples(i); + } + */ + if (i == 0) { - sampling = new NoSampling(); + priSampling = new NoSampling(); } else { - //sampling.setRequiredSamples(i/100.0); - sampling.setRequiredSamples(i); + priSampling.setRequiredSamples(i); } + secSampling.setRequiredSamples(5000); 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, secFace, // is transformed 100, // max iterations false,// scale - 0.3, // error - sampling, - false // drop k-d tree, if exists + 0.05, // error + secSampling, + false // drop k-d tree of the secFace, if exists ); + */ efficiency.get(i).stop(); - System.out.println("" + sampling); + System.out.println(" Primary face: " + priSampling); + System.out.println("Secondary face: " + secSampling); HausdorffDistance hd = new HausdorffDistance( priFace.getKdTree(), @@ -171,4 +193,12 @@ public class IcpDownsampling { } 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; + } }