Commit 24d0e424 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch '314-reuse-visitor-in-symmetry' into 'master'

Reuse visitor in Symmetry (robust-point-cloud) algorithm

See merge request grp-fidentis/analyst2!339
parents 136979d6 7aed8186
Loading
Loading
Loading
Loading
+16 −7
Original line number Diff line number Diff line
@@ -35,28 +35,37 @@ __attribute__ ((reqd_work_group_size(WARP_GROUP_SIZE, 1, 1)))
kernel void calculateClosestIntersections(const int rayCount, const int maxLeafSize,
      const int smoothing, const int filter,
      global Point3D* origins, global Vector3D* directions, // Ray direction
      global MeshTriangle* triangles, // Mesh Triangles - triangle == 6 float3 values stored continuously
      global MeshTriangle* triangles, // Mesh Triangles ==> triangle == 6 float3 values stored continuously
      global Point3D* octreeBBox,
      global int* indices, global unsigned int* triangleIndices, // Index mappings - nodes to octants and leaves to triangles
      global float8* closestIntersections){ // Results --> Everything is casted to floats in float8* type to avoid waste of space during alignment. There is a room for better representation

      //Allocate Memory
      local struct DirectionInfo info[1];
      int threadID = get_global_id(0);

      if(get_local_id(0)==0.0f){
          info[0] = getDirection(octreeBBox[0], octreeBBox[1], directions[0]);
      }

      OctNode stackMemory[STACK_SIZE];
      OctNodeStack stack = {STACK_SIZE,-1, -1, stackMemory};
      Point3D originPoint = origins[threadID];

      barrier(CLK_LOCAL_MEM_FENCE); // Waits for info to be precomputed

      if(threadID>=rayCount){ // Terminates useless threads - Created due to work-group alignment to warp size
          return;
      }

      RayIntersection intersection = traverseOctree(rayCount, maxLeafSize,
                                    smoothing, // Smoothing enum
                                    filter, // Applied filter
                                    origins[get_global_id(0)], info, //Ray
                                    (OctNodeStack) {STACK_SIZE,-1, -1, stackMemory}, //Octree traversal stack
                                    originPoint, info, // Ray
                                    stack, // Octree traversal stack
                                    indices, // Octree
                                    triangleIndices, triangles); // Mesh Triangles

      closestIntersections[get_global_id(0)] = (float8) {intersection.point.x,intersection.point.y,intersection.point.z,intersection.triangle,
      closestIntersections[threadID] = (float8) {intersection.point.x,intersection.point.y,intersection.point.z,intersection.triangle,
                                          intersection.normal.x,intersection.normal.y,intersection.normal.z,intersection.distance};
}
 No newline at end of file
+0 −5
Original line number Diff line number Diff line
@@ -107,11 +107,6 @@ inline RayIntersection traverseOctree(const int rayCount, const int maxLeafSize,
    Point3D tm;
    RayIntersection i;

    barrier(CLK_LOCAL_MEM_FENCE); // Waits for info to be precomputed
    if(get_global_id(0)>=rayCount){
        return intersection;
    }

    Point3D origin = getOrigin(dirInfo[0].originAdd, rayOrigin + dirInfo[0].scaledDirection);
    Point3D t0 = half_divide((dirInfo[0].rootLowerBound - origin),dirInfo[0].normalizedDirection);
    Point3D t1 = half_divide((dirInfo[0].rootUpperBound - origin),dirInfo[0].normalizedDirection);
+63 −0
Original line number Diff line number Diff line
package cz.fidentis.analyst.gui.app.tools;

import cz.fidentis.analyst.data.face.HumanFace;
import cz.fidentis.analyst.data.face.HumanFaceFactory;
import cz.fidentis.analyst.data.mesh.MeshFacet;
import cz.fidentis.analyst.data.mesh.MeshFactory;
import cz.fidentis.analyst.data.mesh.MeshModel;
import cz.fidentis.analyst.engines.distance.MeshDistanceConfig;
import cz.fidentis.analyst.engines.distance.MeshDistanceServices;
import cz.fidentis.analyst.engines.face.FaceStateServices;
import cz.fidentis.analyst.engines.sampling.PointSamplingConfig;
import cz.fidentis.analyst.engines.symmetry.SymmetryConfig;
import cz.fidentis.analyst.engines.symmetry.SymmetryServices;
import cz.fidentis.analyst.gui.task.batch.Stopwatch;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Throwaway benchmark, delete as soon as possible
 *
 * @author Marek Horský
 */
public class PoissonSymmetryBenchmark {
    private static final String DATA_DIR = "C:\\Users\\marek\\Desktop\\FidentisFaces\\multiple-scans";
    private static final int MAX_SAMPLES = 500;
    
    /**
     * Main method 
     * @param args Input arguments 
     * @throws IOException on IO error
     */
    public static void main(String[] args) throws IOException, ClassNotFoundException, Exception {
        List<Path> faces = Files.list(new File(DATA_DIR).toPath())
                .filter(f -> f.toString().endsWith(".obj"))
                .sorted()
                .limit(MAX_SAMPLES)
                .collect(Collectors.toList());

        long time = 0;
        
        for (int i = 0; i < faces.size(); i++) {
            System.out.println(i + ": " + faces.get(i));

            HumanFace face = HumanFaceFactory.create(faces.get(i).toFile());
            FaceStateServices.updateCurvature(face, FaceStateServices.Mode.COMPUTE_IF_ABSENT);
            PointSamplingConfig samplingStrategy0 = new PointSamplingConfig(PointSamplingConfig.Method.POISSON_OPENCL, 200);
            SymmetryConfig estimator0 = new SymmetryConfig(SymmetryConfig.Method.ROBUST_POINT_CLOUD, samplingStrategy0);

            long t = Instant.now().toEpochMilli();
            face.setSymmetryPlane(SymmetryServices.estimate(face.getMeshModel(), estimator0));
            time += (Instant.now().toEpochMilli() - t);
        }
        
        System.out.println("Time: " + time);
    }
}
+23 −1
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import cz.fidentis.analyst.engines.face.FaceStateServices;
import cz.fidentis.analyst.engines.face.events.SymmetryPlaneChangedEvent;
import cz.fidentis.analyst.engines.sampling.PointSamplingConfig;
import cz.fidentis.analyst.engines.sampling.PointSamplingServices;
import cz.fidentis.analyst.engines.sampling.PointSamplingVisitor;
import cz.fidentis.analyst.engines.symmetry.SymmetryConfig;
import cz.fidentis.analyst.gui.elements.SpinSlider;
import cz.fidentis.analyst.gui.task.ControlPanelAction;
@@ -31,6 +32,15 @@ public class SymmetryAction extends ControlPanelAction<SymmetryPanel> implements

    private int primCloudSlot = -1;

    /**
     * Working with the sub-sampling density slider triggers frequent re-computation of samples.
     * In this case, calling the sampling algorithm via {@code PointSamplingServices.sample()}
     * is inefficient because new and new object is created and often new and new internal
     * structures are initiated. Therefore, the stateful invocation is used instead, when
     * the visitor is obtained and reused (with different settings) until the strategy is changed.
     */
    private PointSamplingVisitor samplingVisitor;

    /**
     * Constructor. 
     * A new {@code SymmetryPanel} is instantiated and added to the {@code topControlPane}
@@ -98,6 +108,16 @@ public class SymmetryAction extends ControlPanelAction<SymmetryPanel> implements
                showHidePrimaryPointSamples(getCanvas().getScene().getPrimaryFaceSlot(), numSamples);
                break;
            case SymmetryPanel.ACTION_COMMAND_POINT_SAMPLING_STRATEGY:
                if(samplingVisitor!=null){
                    samplingVisitor.dispose();
                }

                var strategy = getSamplingStrategy(getControlPanel().getPointSamplingStrength1());
                samplingVisitor = strategy == null ? null : strategy.getVisitor();
                if (samplingVisitor != null) {
                    getPrimaryFace().getMeshModel().getFacets().forEach(facet -> samplingVisitor.visitMeshFacet(facet));
                }

                showHidePrimaryPointSamples(getCanvas().getScene().getPrimaryFaceSlot(), getControlPanel().getPointSamplingStrength1());
                break;
            case SymmetryPanel.ACTION_COMMAND_SHOW_SAMPLES:
@@ -208,9 +228,11 @@ public class SymmetryAction extends ControlPanelAction<SymmetryPanel> implements
        if (this.primCloudSlot == -1) {
            this.primCloudSlot = getCanvas().getScene().getFreeSlotForFace();
        }

        samplingVisitor.setRequiredSamples(numSamples);
        getScene().setOtherDrawable(
                this.primCloudSlot,
                new DrawablePointCloud(PointSamplingServices.sample(face.getMeshModel(), sampling))
                new DrawablePointCloud(samplingVisitor.getSamples())
        );
    }
    
+11 −10
Original line number Diff line number Diff line
@@ -68,8 +68,6 @@ public class PoissonDiskSubSamplingGPU extends PoissonDiskSubSampling {
        if (clContext == null) {
            initialize();
        }
        this.rayIntersectionOpenCLServices = new RayIntersectionOpenCLServices(clContext,
                new RayIntersectionOpenCLConfig(smoothing, false));
        long time = Instant.now().toEpochMilli();
        this.octree.build(facets);
        System.out.println(("Octree build: " + (Instant.now().toEpochMilli() - time)));
@@ -136,7 +134,6 @@ public class PoissonDiskSubSamplingGPU extends PoissonDiskSubSampling {
        } while (iterationCount <= MIN_PROJECTION_ITERATIONS || !validSamples.isEmpty());
        System.out.println("Ray casting time: " + time);
        setRealSamples(samples.size());
        rayIntersectionOpenCLServices.release();
        return new ArrayList<>(samples);
    }

@@ -148,18 +145,22 @@ public class PoissonDiskSubSamplingGPU extends PoissonDiskSubSampling {
    @Override
    public void dispose() {
        System.out.println("Poisson released");
        if(clContext!=null && !clContext.isReleased()){
            meshCentroidBuffer.release();
            projectorBuffer.release();
            directionBuffer.release();
            octree.release();
        //rayIntersectionOpenCLServices.release();
            rayIntersectionOpenCLServices.release();
            queue.release();
            OpenCLServices.release(clContext);
        }
    }

    private void initialize() {
        this.clContext = OpenCLServices.createContext();
        this.queue = clContext.getMaxFlopsDevice().createCommandQueue();
        this.rayIntersectionOpenCLServices = new RayIntersectionOpenCLServices(clContext,
                new RayIntersectionOpenCLConfig(smoothing, false));
        this.octree = OctreeOpenCL.create(clContext);
        this.meshCentroidBuffer = BufferFactory.getVoxelPointBuffer(clContext);
        this.projectorBuffer = BufferFactory.getVoxelPointBuffer(clContext);
Loading