Commit 4207346e authored by Jakub Kolman's avatar Jakub Kolman
Browse files

[#123] rebase: rebase on master branch

parent 58d6f682
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -111,4 +111,34 @@ public class Project {
        return null;
    }
    
    /**
     * Checks whether model saved in this project has in the same directory
     * file with feature points
     * @param name String name of face
     * @return true if feature points file was found, false otherwise
     */
    public boolean hasFPByName(String name) {
        
        Path path = this.getCfg().getPathToFaceByName(name);
        
        if (path != null) {
            
            String filePath = path.toString().split(".obj")[0] + "_landmarks.csv";
            if ((new File(filePath)).exists()) {
                return true;
            }

            filePath = path.toString().split("_ECA")[0] + "_landmarks.csv";
            if ((new File(filePath)).exists()) {
                return true;
            }

            filePath = path.toString().split("_CA")[0] + "_landmarks.csv";
            if ((new File(filePath)).exists()) {
                return true;
            }
        }
        return false;
    }
   
}
+94 −109
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@ import cz.fidentis.analyst.visitors.mesh.CurvatureCalculator;
import java.awt.image.BufferedImage;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
@@ -30,11 +29,10 @@ import java.util.Objects;
 * <p>
 * Changes in the human face and its data structures (e.g., mesh model, etc.)
 * can be monitored by listeners. Listeners have to implement the 
 * {@link cz.fidentis.analyst.face.events.HumanFaceListener} interface and they
 * have to be registered using the
 * {@link cz.fidentis.analyst.face.HumanFace#registerListener} method. Then they
 * are informed about changes in the human automatically via methods prescribed
 * by the interface.
 * {@link cz.fidentis.analyst.face.events.HumanFaceListener} interface and they have to be
 * registered using the {@link cz.fidentis.analyst.face.HumanFace#registerListener} method.
 * Then they are informed about changes in the human automatically via methods 
 * prescribed by the interface.
 * </p>
 * 
 * @author Radek Oslejsek
@@ -45,8 +43,8 @@ public class HumanFace implements Serializable {
    private MeshModel meshModel;
    
    /**
     * {@code KdTree} is marked as transient because the Kryo library is not
     * able to handle the reference of {@code KdNode} to {@code MeshModel}s.
     * {@code KdTree} is marked as transient because the Kryo library is not able
     * to handle the reference of {@code KdNode} to {@code MeshModel}s.
     */
    private transient KdTree kdTree; 

@@ -69,9 +67,10 @@ public class HumanFace implements Serializable {
     * Fast (de)serialization handler
     */
    //private static final FSTConfiguration FST_CONF = FSTConfiguration.createDefaultConfiguration();
    
    /**
     * Reads a 3D human face from the given OBJ file. Use
     * {@link restoreFromFile} to restore the human face from a dump file.
     * Reads a 3D human face from the given OBJ file.
     * Use {@link restoreFromFile} to restore the human face from a dump file.
     * 
     * @param file OBJ file
     * @param loadLandmarks If {@code true}, then the constructor aims to load 
@@ -80,7 +79,7 @@ public class HumanFace implements Serializable {
     * @throws IOException on I/O failure
     */
    public HumanFace(File file, boolean loadLandmarks) throws IOException {
        meshModel = MeshObjLoader.read(new FileInputStream(file));
        meshModel = MeshObjLoader.read(file);
        meshModel.simplifyModel();
        this.id = file.getCanonicalPath();
        
@@ -104,9 +103,9 @@ public class HumanFace implements Serializable {
    }
    
    /**
     * Reads a 3D human face from the given OBJ file. Also loads landmarks
     * (feature points) if appropriate file is found. Use
     * {@link restoreFromFile} to restore the human face from a dump file.
     * Reads a 3D human face from the given OBJ file. 
     * Also loads landmarks (feature points) if appropriate file is found.
     * Use {@link restoreFromFile} to restore the human face from a dump file.
     * 
     * @param file OBJ file
     * @throws IOException on I/O failure
@@ -116,8 +115,8 @@ public class HumanFace implements Serializable {
    }
    
    /**
     * Creates a human face from existing mesh model. The mesh model is stored
     * directly (not copied).
     * Creates a human face from existing mesh model. The mesh model is
     * stored directly (not copied).
     * 
     * @param model Mesh model
     * @param id Canonical path to the OBJ file
@@ -169,9 +168,8 @@ public class HumanFace implements Serializable {
    }

    /**
     * Registers listeners (objects concerned in the human face changes) to
     * receive events. If listener is {@code null}, no exception is thrown and
     * no action is taken.
     * Registers listeners (objects concerned in the human face changes) to receive events.
     * If listener is {@code null}, no exception is thrown and no action is taken.
     * 
     * @param listener Listener concerned in the human face changes.
     */
@@ -216,8 +214,7 @@ public class HumanFace implements Serializable {
    }

    /**
     * Sets the symmetry plane. If the input argument is {@code null}, then
     * removes the plane.
     * Sets the symmetry plane. If the input argument is {@code null}, then removes the plane.
     * 
     * @param plane The new symmetry plane; Must not be {@code null}
     */
@@ -235,7 +232,6 @@ public class HumanFace implements Serializable {
    
    /**
     * Returns {@code true} if the face has the symmetry plane computed.
     *
     * @return {@code true} if the face has the symmetry plane computed.
     */
    public boolean hasSymmetryPlane() {
@@ -244,7 +240,6 @@ public class HumanFace implements Serializable {
    
    /**
     * Returns bounding box of this face.
     *
     * @return bounding box of this face.
     */
    public BBox getBoundingBox() {
@@ -263,8 +258,7 @@ public class HumanFace implements Serializable {
    /**
     * Computes curvature
     * 
     * @param force If {@code} then the curvature is re-computed even if it
     * already exists.
     * @param force If {@code} then the curvature is re-computed even if it already exists.
     */
    public void computeCurvature(boolean force) {
        if (force || !hasCurvature) {
@@ -279,7 +273,7 @@ public class HumanFace implements Serializable {
     */
    @Deprecated
    public void setFeaturePoints(List<FeaturePoint> points) {
        this.featurePoints = points;
        featurePoints = points;
    }
    
    /**
@@ -307,7 +301,6 @@ public class HumanFace implements Serializable {
    
    /**
     * Checks if HumanFace has feature points
     *
     * @return true if yes and false if not
     */
    public boolean hasFeaturePoints() {
@@ -325,7 +318,6 @@ public class HumanFace implements Serializable {
    
    /**
     * Returns canonical path to the face file
     *
     * @return canonical path to the face file
     */
    public String getPath() {
@@ -333,9 +325,7 @@ public class HumanFace implements Serializable {
    }
    
    /**
     * Returns short name of the face without its path in the name. May not be
     * unique.
     *
     * Returns short name of the face without its path in the name. May not be unique.
     * @return short name of the face without its path in the name
     */
    public String getShortName() {
@@ -346,7 +336,6 @@ public class HumanFace implements Serializable {
    
    /**
     * Returns already computed k-d tree of the triangular mesh or {@code null}.
     *
     * @return Already computed k-d tree of the triangular mesh or {@code null}
     */
    public KdTree getKdTree() {
@@ -355,7 +344,6 @@ public class HumanFace implements Serializable {
    
    /**
     * Checks if HumanFace has KdTree calculated
     *
     * @return true if yes and false if not
     */
    public boolean hasKdTree() {
@@ -365,9 +353,8 @@ public class HumanFace implements Serializable {
    /**
     * Computes new k-d tree or returns the existing one.
     * 
     * @param recompute If {@code true} and an old k-d tree exists, then it is
     * recomputed again. Otherwise, the tree is computed only if does not exist
     * yet.
     * @param recompute If {@code true} and an old k-d tree exists, then it is recomputed again. 
     * Otherwise, the tree is computed only if does not exist yet.
     * @return K-d tree with the triangular mesh.
     */
    public KdTree computeKdTree(boolean recompute) {
@@ -379,7 +366,6 @@ public class HumanFace implements Serializable {
    
    /**
     * Removes k-d tree from the Human face. 
     *
     * @return removed k-d tree or {@code null}
     */
    public KdTree removeKdTree() {
@@ -389,9 +375,7 @@ public class HumanFace implements Serializable {
    }
    
    /**
     * Tries to find a file with landmarks definition based on the name of the
     * face's OBJ file.
     *
     * Tries to find a file with landmarks definition based on the name of the face's OBJ file.
     * @return The file with landmarks or {@code null}
     */
    public final File findLandmarks() {
@@ -446,4 +430,5 @@ public class HumanFace implements Serializable {
        return true;
    }
    
    
}
+3 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ import cz.fidentis.analyst.mesh.core.CornerTableRow;
import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
import cz.fidentis.analyst.mesh.core.MeshModel;
import cz.fidentis.analyst.mesh.core.MeshPointImpl;
import cz.fidentis.analyst.mesh.material.Material;
import cz.fidentis.analyst.symmetry.Plane;
import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox;
import java.io.File;
@@ -118,11 +119,13 @@ public class HumanFaceFactory {
                HumanFaceFactory.kryo.register(CornerTable.class);
                HumanFaceFactory.kryo.register(CornerTableRow.class);
                HumanFaceFactory.kryo.register(BBox.class);
                HumanFaceFactory.kryo.register(Material.class);
                HumanFaceFactory.kryo.register(Plane.class);
                HumanFaceFactory.kryo.register(Point3d.class);
                HumanFaceFactory.kryo.register(Vector3d.class);
                HumanFaceFactory.kryo.register(ArrayList.class);
                HumanFaceFactory.kryo.register(HashMap.class);
                HumanFaceFactory.kryo.register(String.class);
            }
        }
    }
+10 −7
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ public class HumanFaceUtils {
            boolean recomputeKdTree) {

        // transform mesh:
        System.out.println("_AAA " + staticFace.getShortName() + ":" + transformedFace.getShortName());
        IcpTransformer icp = new IcpTransformer(
                staticFace.getMeshModel(),
                maxIterations,
@@ -364,9 +365,10 @@ public class HumanFaceUtils {
    }

    /**
     * Moves feature points to calculated new position by computing procrustes analysis.
     * {@link cz.fidentis.analyst.procrustes.ProcrustesAnalysis}. If feature point was not 
     * used in the analysis, it's position will be kept as it had in the original list.
     * Moves feature points to calculated new position by computing procrustes
     * analysis. {@link cz.fidentis.analyst.procrustes.ProcrustesAnalysis}. If
     * feature point was not used in the analysis, it's position will be kept as
     * it had in the original list.
     *
     * Move of the feature point is done to remove them from user vision first.
     *
@@ -400,4 +402,5 @@ public class HumanFaceUtils {
        );
        return featurePointList;
    }

}
+51 −9
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;
@@ -284,10 +319,17 @@ public class IcpTransformer extends MeshVisitor {
            transformation = computeIcpTransformation(nearestPoints, distances, reducedFacet.getVertices());
            transformations.get(transformedFacet).add(transformation);
            applyTransformation(transformedFacet, transformation);
            if (!samplingStrategy.isBackedByOrigMesh()) { // transform samples as well
            
            if (!samplingStrategy.isBackedByOrigMesh()) { // samples have to be transfomed as well
                applyTransformation(reducedFacet, transformation);
            }
            
            currentIteration++;
            
            //if (currentIteration >= 3) {
            //    samplingStrategy.setRequiredSamples(numSamples);
            //    reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy);
            //}
        }
    }
    
Loading