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

Final refactoring of the SymmetryEstimator

parent 0304aa3a
No related branches found
No related tags found
No related merge requests found
**/target
# From directories, ignore...
# From files and directories, ignore...
preferences.fip
### Idea:
*.iml
......
......@@ -43,8 +43,10 @@ public class EfficiencyTests {
System.out.println(measureCurvature(face1, new GaussianCurvatureVisitor(false), false) + "\tmsec:\tGaussian curvature");
System.out.println(measureCurvature(face1, new MaxCurvatureVisitor(false), false) + "\tmsec:\tMax curvature");
System.out.println(measureSymmetryPlane(face1, true) + "\tmsec:\tSymmetry plane");
System.out.println(measureSymmetryPlane(face1, true, false) + "\tmsec:\tSymmetry plane with Gaussian curvature");
System.out.println(measureSymmetryPlane(face1, true, true) + "\tmsec:\tSymmetry plane with MAX curvature");
System.out.println();
System.out.println(measureKdTreeCreation(face1) + "\tmsec:\tKd-tree creation of first face");
System.out.println(measureKdTreeCreation(face2) + "\tmsec:\tKd-tree creation of second face");
......@@ -91,14 +93,15 @@ public class EfficiencyTests {
return System.currentTimeMillis() - startTime;
}
private static long measureSymmetryPlane(HumanFace face, boolean printDetails) {
private static long measureSymmetryPlane(HumanFace face, boolean printDetails, boolean maxCurvatureAlg) {
long startTime = System.currentTimeMillis();
SymmetryEstimator est = new SymmetryEstimator(face.getMeshModel().getFacets().get(0), Config.getDefault());
Plane plane = est.getApproxSymmetryPlane(null);
SymmetryEstimator est = new SymmetryEstimator(face.getMeshModel().getFacets().get(0), Config.getDefault(), maxCurvatureAlg);
est.calculateSymmetryPlane();
Plane plane = est.getSymmetryPlane();
long retTime = System.currentTimeMillis() - startTime;
if (printDetails) {
System.out.println(plane.getNormal() + ", " + plane.getDistance());
System.out.println("Symmetry plane: " + plane.getNormal() + ", " + plane.getDistance());
}
return retTime;
......
package cz.fidentis.analyst.symmetry;
/**
*
* @author Natália Bebjaková
*
* Representation of configuration for symmetry estimate.
* Default numbers are given due to the best results on tested data.
* On many different 3D models, it exists other values of config that will have
* better impact on results in estimate of symmetry.
*
* @author Natalia Bebjakova
*/
public class Config {
private static final double DEFAULT_MIN_CURV_RATIO = 0.5;
......
package cz.fidentis.analyst.symmetry;
import cz.fidentis.analyst.mesh.core.CornerTableRow;
import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.visitors.mesh.BoundingBox;
import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
import cz.fidentis.analyst.mesh.core.MeshPointImpl;
import cz.fidentis.analyst.mesh.core.MeshTriangle;
import cz.fidentis.analyst.visitors.mesh.BoundingBox;
import cz.fidentis.analyst.visitors.mesh.BoundingBoxVisitor;
import cz.fidentis.analyst.visitors.mesh.CurvatureVisitor;
import cz.fidentis.analyst.visitors.mesh.GaussianCurvatureVisitor;
import cz.fidentis.analyst.visitors.mesh.MaxCurvatureVisitor;
import cz.fidentis.analyst.visitors.mesh.TriangleListVisitor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
......@@ -20,50 +18,52 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;
import javax.vecmath.Vector3d;
/**
* Main class for computing approximate plane of symmetry of the 3D model.
* For computing the symmetry, for every
* Default values of the configuration are given due to the best results on tested objects.
* On many different 3D models, it exists other values of config that will have better impact on result.
* <p>
* Note: this algorithm can be implemented as a mesh visitor because it requires
* complete mesh to compute the symmetry plane. Sequential application on
* multiple meshes does not work.
* </p>
*
* @author Natalia Bebjakova
* @author Radek Oslejsek
*/
public class SymmetryEstimator {
private final MeshFacet facet; // Facet of the model on which symmetry is computed
private List<Vector3d> areas; // Representation for areas of Voronoi region of triangles
private BoundingBox boundingBox; // Represent min-max box. It is automatically maintained by given point array.
private final MeshFacet facet;
private final Config config;
private final boolean maxCurvatureAlg;
// results:
private List<Double> curvatures;
private BoundingBox boundingBox;
private Plane symmetryPlane;
/**
* Constructor.
*
* @param f facet on which the symmetry will be computed
* @param config configuration of optional parameters of the algorithm
* @param facet Mesh facet for which the symmetry plane is calculated
* @param config Algorighm options
* @param maxCurvatureAlg If {@code true}, then more precise but slower {@link MaxCurvatureVisitor}
* algorithm is used. Otherwise, the faster {@link GaussianCurvatureVisitor} is used.
* See {@link CurvatureVisitor} for more details.
* @throws IllegalArgumentException if some input paramter is missing
*/
public SymmetryEstimator(MeshFacet f, Config config) {
this.facet = f;
this.config = config;
this.areas = new ArrayList<>(f.getNumTriangles());
int i = 0;
for (MeshTriangle tri: f) {
areas.add(tri.getVoronoiPoint());
public SymmetryEstimator(MeshFacet facet, Config config, boolean maxCurvatureAlg) {
if (facet == null) {
throw new IllegalArgumentException("facet");
}
TriangleListVisitor vis = new TriangleListVisitor(false);
f.accept(vis);
BoundingBoxVisitor visitor = new BoundingBoxVisitor(false);
facet.accept(visitor);
boundingBox = visitor.getBoundingBox();
if (config == null) {
throw new IllegalArgumentException("config");
}
this.facet = facet;
this.config = config;
this.maxCurvatureAlg = maxCurvatureAlg;
}
/**
......@@ -73,39 +73,22 @@ public class SymmetryEstimator {
* @param b B
* @param scale Scale
*/
/*
protected SymmetryEstimator(Vector3d centroid, Vector3d a, Vector3d b, double scale) {
this(createMeshFacet(centroid, a, b, scale), Config.getDefault());
}
*/
/**
*
* @return configuration of optional parameters of the algorithm
*/
public Config getConfig() {
return config;
}
/**
*
* @return Facet of the model on which symmetry is computed
*/
public MeshFacet getFacet() {
return facet;
}
/**
* Computes the approximate plane of symmetry.
* TO DO: ZRUSIT VSTUPNI PARAMETR!!!
*
* @param panel parrent window for the progress window (can be null)
* @return approximate plane of symmtetry
* Calculates the symmetry plane.
*/
public Plane getApproxSymmetryPlane(JPanel panel) {
public void calculateSymmetryPlane() {
if (!facet.hasVertexNormals()) {
facet.calculateVertexNormals();
}
List<Double> curvatures = calculateCurvatures(facet);
curvatures = calculateCurvatures(facet, maxCurvatureAlg);
final SignificantPoints sigPoints = new SignificantPoints(curvatures, config.getSignificantPointCount());
// Initiate structure for concurrent computation:
......@@ -113,7 +96,7 @@ public class SymmetryEstimator {
final List<Future<ApproxSymmetryPlane>> results = new ArrayList<>(sigPoints.size()*sigPoints.size());
// Compute candidate planes concurrently:
final double maxDistance = boundingBox.getMaxDiag() * config.getMaxRelDistance();
final double maxDistance = calculateMaxRelativeDistance(facet, config);
for (int i = 0; i < sigPoints.size(); i++) {
for (int j = 0; j < sigPoints.size(); j++) {
int finalI = i;
......@@ -160,48 +143,38 @@ public class SymmetryEstimator {
Logger.getLogger(SymmetryEstimator.class.getName()).log(Level.SEVERE, null, ex);
}
// Compute the average plane, if required...
if (config.isAveraging() && !planes.isEmpty()) {
return new Plane(planes);
symmetryPlane = new Plane(planes);
} else {
symmetryPlane = planes.isEmpty() ? null : planes.get(0);
}
// ... or return the random best-fitting plane:
return planes.isEmpty() ? null : planes.get(0);
}
private List<Double> calculateCurvatures(MeshFacet facet) {
CurvatureVisitor vis;
if (facet.getNumberOfVertices() < 2500) {
// searching for Maximum curvature in vertex using Gaussian curvature
// and Mean curvature leads to longer calculation but better results for real face models
//curvatures.add(getMaxCurvature(i));
vis = new MaxCurvatureVisitor(false);
} else {
//curvatures.add(getGaussianCurvature(i));
vis = new GaussianCurvatureVisitor(false);
}
facet.accept(vis);
List<Double> curvatures = new ArrayList<>(vis.getCurvatures().get(facet));
for (int i = 0; i < curvatures.size(); i++) {
if (Double.isNaN(curvatures.get(i))) {
curvatures.set(i, Double.MIN_VALUE); // !!!!
}
}
return curvatures;
/**
* Returns the symmetry plane computed by the {@link SymmetryEstimator#calculateSymmetryPlane()} method.
* @return the symmetry plane or {@code null}
*/
public Plane getSymmetryPlane() {
return symmetryPlane;
}
/**
* COmputes and returns a triangular mesh for the symmetry plane (a rectangle).
* Its size is restricted by the bounding box of the original mesh facet.
*
* @param plane Plane computed as symmetry plane
* @return mesh that represents facet with computed plane of approximate symmetry
* @return mesh facet of the symmetry plane or {@code null}
*/
public SymmetryEstimator mergeWithPlane(Plane plane) {
Vector3d normal = plane.getNormal();
public MeshFacet getSymmetryPlaneMesh() {
if (symmetryPlane == null) {
return null;
}
Vector3d normal = symmetryPlane.getNormal();
Vector3d midPoint = boundingBox.getMidPoint().getPosition();
double alpha = -((normal.x * midPoint.x) +
(normal.y * midPoint.y) + (normal.z * midPoint.z) +
plane.getDistance()) / (normal.dot(normal));
symmetryPlane.getDistance()) / (normal.dot(normal));
Vector3d midPointOnPlane = new Vector3d(midPoint);
Vector3d nn = new Vector3d(normal);
......@@ -210,7 +183,7 @@ public class SymmetryEstimator {
double val = normal.x * midPointOnPlane.x + normal.y *
midPointOnPlane.y + normal.z *
midPointOnPlane.z + plane.getDistance();
midPointOnPlane.z + symmetryPlane.getDistance();
Vector3d a = new Vector3d();
if (Math.abs(normal.dot(new Vector3d(0.0, 1.0, 0.0))) > Math.abs(normal.dot(new Vector3d (1.0, 0.0, 0.0)))) {
......@@ -224,39 +197,47 @@ public class SymmetryEstimator {
b.cross(normal,a);
b.normalize();
SymmetryEstimator planeMesh = new SymmetryEstimator(midPointOnPlane, a, b,
(boundingBox.getMaxPoint().subtractPosition(boundingBox.getMinPoint())).getPosition().x);
return mergeMeshWith(planeMesh);
double scale = (boundingBox.getMaxPoint().subtractPosition(boundingBox.getMinPoint())).getPosition().x;
return createMeshFacet(midPointOnPlane, a, b, symmetryPlane.getNormal(), scale);
}
/**
*
* @param s mesh that will be merged
* @return mesh with merged vertices from both meshes
* Returns the bounding box computed during the {@link SymmetryEstimator#calculateSymmetryPlane()}.
* @return the bounding box or {@code null}
*/
public SymmetryEstimator mergeMeshWith(SymmetryEstimator s) {
CornerTableRow row1 = new CornerTableRow(facet.getNumberOfVertices(), -1);
CornerTableRow row2 = new CornerTableRow(facet.getNumberOfVertices() + 1, facet.getNumberOfVertices() + 3);
CornerTableRow row3 = new CornerTableRow(facet.getNumberOfVertices() + 2, -1);
CornerTableRow row4 = new CornerTableRow(facet.getNumberOfVertices() + 2, -1);
CornerTableRow row5 = new CornerTableRow(facet.getNumberOfVertices() + 3, facet.getNumberOfVertices() + 1);
CornerTableRow row6 = new CornerTableRow(facet.getNumberOfVertices(), -1);
facet.getCornerTable().addRow(row1);
facet.getCornerTable().addRow(row2);
facet.getCornerTable().addRow(row3);
facet.getCornerTable().addRow(row4);
facet.getCornerTable().addRow(row5);
facet.getCornerTable().addRow(row6);
public BoundingBox getBoundingBox() {
return boundingBox;
}
/**
* Returns curvatures computed during the {@link SymmetryEstimator#calculateSymmetryPlane()}.
* @return the curvatures or {@code null}
*/
public List<Double> getCurvature() {
return Collections.unmodifiableList(curvatures);
}
for(int n = 0; n < 4; n++) {
facet.addVertex(s.getFacet().getVertices().get(n));
}
return this;
protected double calculateMaxRelativeDistance(MeshFacet facet, Config config) {
BoundingBoxVisitor visitor = new BoundingBoxVisitor(false);
facet.accept(visitor);
boundingBox = visitor.getBoundingBox();
return boundingBox.getMaxDiag() * config.getMaxRelDistance();
}
private static MeshFacet createMeshFacet(Vector3d centroid, Vector3d a, Vector3d b, double scale) {
protected static List<Double> calculateCurvatures(MeshFacet facet, boolean maxCurvature) {
CurvatureVisitor vis = (maxCurvature) ? new MaxCurvatureVisitor(false) : new GaussianCurvatureVisitor(false);
facet.accept(vis);
List<Double> curvatures = new ArrayList<>(vis.getCurvatures().get(facet));
for (int i = 0; i < curvatures.size(); i++) {
if (Double.isNaN(curvatures.get(i))) {
curvatures.set(i, 0.0);
}
}
return curvatures;
}
protected static MeshFacet createMeshFacet(Vector3d centroid, Vector3d a, Vector3d b, Vector3d normal, double scale) {
Vector3d[] points = new Vector3d[4];
Vector3d aScaled = new Vector3d(a);
......@@ -282,11 +263,38 @@ public class SymmetryEstimator {
MeshFacet facet = new MeshFacetImpl();
for (Vector3d point : points) {
facet.addVertex(new MeshPointImpl(point, null, null));
facet.addVertex(new MeshPointImpl(point, normal, null));
}
facet.calculateVertexNormals();
//facet.calculateVertexNormals();
return facet;
}
/**
*
* @param s mesh that will be merged
* @return mesh with merged vertices from both meshes
*/
/*
protected static SymmetryEstimator mergeMeshWith(SymmetryEstimator s) {
CornerTableRow row1 = new CornerTableRow(facet.getNumberOfVertices(), -1);
CornerTableRow row2 = new CornerTableRow(facet.getNumberOfVertices() + 1, facet.getNumberOfVertices() + 3);
CornerTableRow row3 = new CornerTableRow(facet.getNumberOfVertices() + 2, -1);
CornerTableRow row4 = new CornerTableRow(facet.getNumberOfVertices() + 2, -1);
CornerTableRow row5 = new CornerTableRow(facet.getNumberOfVertices() + 3, facet.getNumberOfVertices() + 1);
CornerTableRow row6 = new CornerTableRow(facet.getNumberOfVertices(), -1);
facet.getCornerTable().addRow(row1);
facet.getCornerTable().addRow(row2);
facet.getCornerTable().addRow(row3);
facet.getCornerTable().addRow(row4);
facet.getCornerTable().addRow(row5);
facet.getCornerTable().addRow(row6);
for(int n = 0; n < 4; n++) {
facet.addVertex(s.getFacet().getVertices().get(n));
}
return this;
}
*/
}
......@@ -7,8 +7,10 @@ import java.util.List;
import javax.vecmath.Vector3d;
/**
* Calculates curvatures of mesh vertices more precisely using Laplacian operator.
* This algorithm is roughly 5-times slower than the pure Gaussian curvature algorithm.
* Calculates max curvatures of mesh vertices using the combination
* of Gaussian curvature and mean curvature (based on Laplacian function).
* This algorithm is roughly 4-times slower than the pure Gaussian curvature algorithm,
* but is more precise.
*
* @author Natalia Bebjakova
* @author Radek Oslejsek
......
package cz.fidentis.analyst.gui;
import static cz.fidentis.analyst.gui.UserInterface.frameMain;
import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.mesh.core.MeshModel;
import cz.fidentis.analyst.symmetry.Config;
import cz.fidentis.analyst.symmetry.Plane;
......@@ -22,22 +23,21 @@ import javax.swing.event.ChangeEvent;
* Panel for estimating approximate symmetry of the model
*/
public final class SymmetryPanel extends javax.swing.JPanel {
/**
* Configuration with optional parameters of the algorithm
*/
private Config config;
/**
* GL Canvas on which model is displayed
*/
private Canvas canvas;
/**
* Class that is responsible for computing the symmetry
*/
private SymmetryEstimator symCounter;
/**
* Computed approximate plane of the symmetry
*/
private Plane finalPlane;
private Plane symmetryPlane;
/**
*
......@@ -156,12 +156,14 @@ public final class SymmetryPanel extends javax.swing.JPanel {
private void countSymmetry() throws InterruptedException {
MeshModel model = new MeshModel();
canvas.changeModel(canvas.getLoadedModel());
symCounter = new SymmetryEstimator(canvas.getModel().getFacets().get(0), config);
finalPlane = symCounter.getApproxSymmetryPlane(this);
SymmetryEstimator counted = symCounter.mergeWithPlane(finalPlane);
model.addFacet(counted.getFacet());
this.canvas.changeModel(model);
SymmetryEstimator est = new SymmetryEstimator(canvas.getModel().getFacets().get(0), config, true); // MISTO TRUE MUSI BYT VYBER STRATEGIE VYPOCTU ZAKRIVENI!!!
est.calculateSymmetryPlane();
symmetryPlane = est.getSymmetryPlane();
MeshFacet facet = est.getSymmetryPlaneMesh();
if (facet != null) {
model.addFacet(facet);
this.canvas.changeModel(model);
}
}
......@@ -526,8 +528,8 @@ public final class SymmetryPanel extends javax.swing.JPanel {
* @param evt Final computed plane is shown to user
*/
private void showPlaneLabelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_showPlaneLabelMouseClicked
JOptionPane.showMessageDialog(frameMain, "Approximate plane of symmetry: \n" + finalPlane.getNormal().x + "\n" + finalPlane.getNormal().y + "\n" + finalPlane.getNormal().z + "\n" +
finalPlane.getDistance() + "\n", "Final plane.", 0, new ImageIcon(getClass().getResource("/showPlanePane.png")));
JOptionPane.showMessageDialog(frameMain, "Approximate plane of symmetry: \n" + symmetryPlane.getNormal().x + "\n" + symmetryPlane.getNormal().y + "\n" + symmetryPlane.getNormal().z + "\n" +
symmetryPlane.getDistance() + "\n", "Final plane.", 0, new ImageIcon(getClass().getResource("/showPlanePane.png")));
}//GEN-LAST:event_showPlaneLabelMouseClicked
/**
......
......@@ -11,7 +11,7 @@ import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* MashFacet
* Mash facet is a compact triangular mesh without duplicit vertices.
*
* @author Matej Lukes
*/
......
/home/oslejsek/GIT/HCI/analyst-data/multi-scan-models-anonymized/average-girl-17-20
\ No newline at end of file
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