Commit 5b791b26 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Resolve "Adaptive downsampling for ICP"

parent eacd7eef
Loading
Loading
Loading
Loading
+48 −8
Original line number Diff line number Diff line
@@ -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;
@@ -290,6 +325,11 @@ public class IcpTransformer extends MeshVisitor {
            }
            
            currentIteration++;
            
            //if (currentIteration >= 3) {
            //    samplingStrategy.setRequiredSamples(numSamples);
            //    reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy);
            //}
        }
    }
    
+1 −1
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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);
+6 −3
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@ import javax.swing.SwingWorker;
 */
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;
    private final BatchPanel controlPanel;
@@ -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
+61 −31
Original line number Diff line number Diff line
@@ -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( 
                            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;
    }
}