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

Resolve "Remove dependency of GUI on JOGL"

parent df507270
Loading
Loading
Loading
Loading
+0 −89
Original line number Diff line number Diff line
@@ -167,95 +167,6 @@
            <artifactId>imgscalr-lib</artifactId>
            <version>4.2</version>
        </dependency>
        <dependency>
            <groupId>org.jocl</groupId>
            <artifactId>jocl</artifactId>
            <version>2.0.4</version>
        </dependency>
        
        <!-- JOGL -->
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all-natives-linux-aarch64</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all-natives-linux-amd64</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all-natives-linux-armv6hf</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all-natives-linux-i586</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all-natives-macosx-universal</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all-natives-windows-amd64</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.jogl</groupId>
            <artifactId>jogl-all-natives-windows-i586 </artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>

        <!-- GLUEGEN -->
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt-natives-macosx-universal</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt-natives-linux-aarch64</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt-natives-linux-amd64</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt-natives-linux-armv6hf</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt-natives-linux-i586</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt-natives-windows-amd64</artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
        <dependency>
            <groupId>org.jogamp.gluegen</groupId>
            <artifactId>gluegen-rt-natives-windows-i586 </artifactId>
            <version>${version.org.jogamp}</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+0 −352
Original line number Diff line number Diff line
package cz.fidentis.analyst.batch;

import cz.fidentis.analyst.Logger;
import cz.fidentis.analyst.core.ProgressDialog;
import cz.fidentis.analyst.face.HumanFace;
import cz.fidentis.analyst.face.HumanFaceFactory;
import cz.fidentis.analyst.distance.HausdorffDistance;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.jocl.CL;
import org.jocl.Pointer;
import org.jocl.Sizeof;
import org.jocl.cl_command_queue;
import org.jocl.cl_context;
import org.jocl.cl_context_properties;
import org.jocl.cl_device_id;
import org.jocl.cl_kernel;
import org.jocl.cl_mem;
import org.jocl.cl_platform_id;
import org.jocl.cl_program;
import org.jocl.cl_queue_properties;

/**
 * A GPU accelerated variant of {@link ApproxHausdorffDistTask}.
 * The computation is accelerated by using both the CPU and GPU parallelism.
 * The OpenCL code is based on the
 * <a href="https://github.com/gpu/JOCLSamples/blob/master/src/main/java/org/jocl/samples/JOCLSample.java">JOCLSample</a>.
 * However, the computation is slower than using CPU-based multitasking only
 * (For 500 faces, 40 seconds of the HD finalization phase on GPU compared to 18 second on 8-core CPU)
 * This class is marked as deprecated for this reason. 
 * Use {@link ApproxHausdorffDistTask} instead.
 * <p>
 * The exact computation parameters are taken from the {@code BatchPanel}.
 * </p> 
 * 
 * @author Radek Oslejsek
 */
@Deprecated
public class ApproxHausdorffDistTaskGPU extends SimilarityTask {
    
    /**
     * OpenCL device
     */
    private cl_device_id device;
    
    /**
     * OpenCL context
     */
    private cl_context context;
    
    /**
     * OpenCL command queue
     */
    private cl_command_queue commandQueue;
    
    /**
     * Built OpenCL program
     */
    private cl_program program;
    
    /**
     * GPU memory where the distances of faces to the template face are stored
     */
    private cl_mem[] srcArrays;
    
    private final Stopwatch totalTime = new Stopwatch("Total computation time:\t\t");
    private final Stopwatch hdComputationTime = new Stopwatch("Hausdorff distance preparation time:\t");
    private final Stopwatch loadTime = new Stopwatch("Disk access time:\t\t");
    private final Stopwatch kdTreeConstructionTime = new Stopwatch("KD trees construction time:\t\t");
    private final Stopwatch finalHdComputationTime = new Stopwatch("Hausdorff distance finalization time:\t");
    
    private final int templateFaceIndex;
    
    /**
     * The source code of the OpenCL program to execute
     */
    private static final String PROGRAM_SOURCE =
        "__kernel void "+
        "hdKernel(__global const float *a,"+
        "         __global const float *b,"+
        "         __global float *c)"+
        "{"+
        "    int gid = get_global_id(0);"+
        "    c[gid] = fabs(a[gid] - b[gid]);"+
        "}";

    /**
     * Constructor.
     * 
     * @param progressDialog A window that show the progress of the computation. Must not be {@code null}
     * @param controlPanel A control panel with computation parameters. Must not be {@code null}
     * @param templateFaceIndex Index of the template face
     */
    public ApproxHausdorffDistTaskGPU(ProgressDialog progressDialog, BatchPanel controlPanel, int templateFaceIndex) {
        super(progressDialog, controlPanel);
        this.templateFaceIndex = templateFaceIndex;
    }
    
    @Override
    protected Void doInBackground() throws Exception {
        initOpenCL();

        HumanFaceFactory factory = getControlPanel().getHumanFaceFactory();
        List<Path> faces = getControlPanel().getFacePaths();
        
        totalTime.start();
        
        factory.setReuseDumpFile(true); // it's safe because no changes are made to models 
        factory.setStrategy(HumanFaceFactory.Strategy.MRU); // keep first X faces in the memory
        
        // We don't need to reaload the initFace periodically for two reasons:
        //   - It is never dumped from memory to disk because we use MRU
        //   - Even if dumped, the face keeps in the mempry until we hold the pointer to it
        loadTime.start();
        String templateFaceId = factory.loadFace(faces.get(templateFaceIndex).toFile());
        HumanFace templateFace = factory.getFace(templateFaceId);
        loadTime.stop();
        
        // Cache of distances of individual vertices in the GPU memory
        // srcArrays[i] stores distance of vertices of i-th face to the template face
        srcArrays = new cl_mem[faces.size()];
        int numVertices = (int) templateFace.getMeshModel().getNumVertices();
        
        for (int i = 0; i < faces.size(); i++) {
            
            if (isCancelled()) { // the user canceled the process
                return null;
            }
            
            if (i != templateFaceIndex) {
                
                loadTime.start();
                String faceId = factory.loadFace(faces.get(i).toFile());
                HumanFace face = factory.getFace(faceId);
                loadTime.stop();
                
                kdTreeConstructionTime.start();
                face.computeKdTree(false);
                kdTreeConstructionTime.stop();

                hdComputationTime.start();
                HausdorffDistance hd = new HausdorffDistance(
                        face.getKdTree(),
                        HausdorffDistance.Strategy.POINT_TO_POINT,
                        true, // relative distance
                        true, // parallel
                        true  // crop
                );
                templateFace.getMeshModel().compute(hd);
                
                // Copy commputed distances into the GPU memory:
                float[] auxDist = new float[numVertices];
                int k = 0;
                for (float val: hd.getDistances().values().stream().flatMap(List::stream)
                        .map(x -> x.floatValue()).collect(Collectors.toList())) {
                    auxDist[k++] = val;
                }
                Pointer arrayP = Pointer.to(auxDist);
                srcArrays[i] = CL.clCreateBuffer(context,
                        CL.CL_MEM_READ_ONLY | CL.CL_MEM_COPY_HOST_PTR,
                        Sizeof.cl_float * numVertices, arrayP, null);

                hdComputationTime.stop();

                face.removeKdTree();
            } else { // distance to template face itself is zero
                float[] auxDist = new float[numVertices]; // filled by zeros by default
                Pointer arrayP = Pointer.to(auxDist);
                srcArrays[i] = CL.clCreateBuffer(context,
                        CL.CL_MEM_READ_ONLY | CL.CL_MEM_COPY_HOST_PTR,
                        Sizeof.cl_float * numVertices, arrayP, null);
            }
            
            // update progress bar
            int progress = (int) Math.round(100.0 * (i+1) / faces.size());
            getProgressDialog().setValue(progress);
            
            //Logger.print(factory.toString());
        }
        
        finalHdComputationTime.start();
        finalizeHdConcurrently(faces.size(), numVertices);
        finalHdComputationTime.stop();
        
        totalTime.stop();

        printTimeStats();
        
        return null;
    }
    
    @Override
    protected void done() {
        super.done();
        CL.clReleaseCommandQueue(commandQueue);
        CL.clReleaseContext(context);
    }
    
    protected void printTimeStats() {
        Logger.print(hdComputationTime.toString());
        Logger.print(loadTime.toString());
        Logger.print(finalHdComputationTime.toString());
        Logger.print(kdTreeConstructionTime.toString());
        Logger.print(totalTime.toString());
    }
    
    private void initOpenCL() {
        // The platform, device type and device number that will be used
        final int platformIndex = 0;
        final long deviceType = CL.CL_DEVICE_TYPE_ALL;
        final int deviceIndex = 0;

        // Enable exceptions and subsequently omit error checks in this sample
        CL.setExceptionsEnabled(true);

        // Obtain the number of platforms
        int[] numPlatformsArray = new int[1];
        CL.clGetPlatformIDs(0, null, numPlatformsArray);
        int numPlatforms = numPlatformsArray[0];

        // Obtain a platform ID
        cl_platform_id[] platforms = new cl_platform_id[numPlatforms];
        CL.clGetPlatformIDs(platforms.length, platforms, null);
        cl_platform_id platform = platforms[platformIndex];

        // Initialize the context properties
        cl_context_properties contextProperties = new cl_context_properties();
        contextProperties.addProperty(CL.CL_CONTEXT_PLATFORM, platform);
        
        // Obtain the number of devices for the platform
        int[] numDevicesArray= new int[1];
        CL.clGetDeviceIDs(platform, deviceType, 0, null, numDevicesArray);
        int numDevices = numDevicesArray[0];
        
        // Obtain a device ID 
        cl_device_id[] devices= new cl_device_id[numDevices];
        CL.clGetDeviceIDs(platform, deviceType, numDevices, devices, null);
        this.device = devices[deviceIndex];

        // Create a context for the selected device
        this.context = CL.clCreateContext(
            contextProperties, 1, new cl_device_id[]{device}, 
            null, null, null);        
        
        // Create a command-queue for the selected device
        cl_queue_properties properties = new cl_queue_properties();
        commandQueue = CL.clCreateCommandQueueWithProperties(
            context, device, properties, null);

        // Create the program from the source code
        program = CL.clCreateProgramWithSource(context,
            1, new String[]{ PROGRAM_SOURCE }, null, null);
        
        // Build the program
        CL.clBuildProgram(program, 0, null, null, null, null);
    }

    protected void finalizeHdConcurrently(int numFaces, int numVertices) {
        // Set the work-item dimensions
        long[] globalWorkSize = new long[]{numVertices};
        
        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        final List<Future<Void>> results = new ArrayList<>();
        
        for (int i = 0; i < numFaces; i++) {
            for (int j = i; j < numFaces; j++) {

                if (i == j) {
                    continue;
                }

                final int finalI = i;
                final int finalJ = j;

                results.add(executor.submit(new Callable<Void>() {
                    @Override
                    public Void call() throws Exception {
                        float[] dstArray = new float[numVertices];

                        cl_mem dstMem = CL.clCreateBuffer(context,
                                CL.CL_MEM_READ_WRITE,
                                Sizeof.cl_float * numVertices, null, null);
                        Pointer dstP = Pointer.to(dstArray);

                        // Create the kernel
                        cl_kernel kernel = CL.clCreateKernel(program, "hdKernel", null);

                        // Set the arguments for the kernel
                        int a = 0;
                        CL.clSetKernelArg(kernel, a++, Sizeof.cl_mem, Pointer.to(srcArrays[finalI]));
                        CL.clSetKernelArg(kernel, a++, Sizeof.cl_mem, Pointer.to(srcArrays[finalJ]));
                        CL.clSetKernelArg(kernel, a++, Sizeof.cl_mem, Pointer.to(dstMem));

                        // Execute the kernel
                        CL.clEnqueueNDRangeKernel(commandQueue, kernel, 1, null,
                                globalWorkSize, null, 0, null, null);

                        // Read the output data
                        CL.clEnqueueReadBuffer(commandQueue, dstMem, CL.CL_TRUE, 0,
                                numVertices * Sizeof.cl_float, dstP, 0, null, null);

                        // Release kernel
                        CL.clReleaseKernel(kernel);
                        CL.clReleaseMemObject(dstMem);

                        // Compute average HD of the face pair
                        double sum = 0.0;
                        int counter = 0;
                        for (int k = 0; k < numVertices; k++) {
                            double d = dstArray[k];
                            if (Double.isFinite(d)) {
                                sum += d;
                                counter++;
                            }
                        }
                        setDistSimilarity(finalI, finalJ, sum / counter);
                        setDistSimilarity(finalJ, finalI, sum / counter);

                        return null;
                    }
                }));
            }
        }
        
        executor.shutdown();
        while (!executor.isTerminated()){}
        try {
            int i = 0;
            for (Future<Void> res: results) {
                res.get(); // waits until all computations are finished
                int progress = (int) Math.round(100.0 * (i+1) / results.size());
                getProgressDialog().setValue(progress);
            }
        } catch (final InterruptedException | ExecutionException ex) {
            java.util.logging.Logger.getLogger(ApproxHausdorffDistTask.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        // Release Program, and memory objects
        for (int i = 0; i < numFaces; i++) {
            CL.clReleaseMemObject(srcArrays[i]);
        }
        CL.clReleaseProgram(program);
    }    
}
+0 −78
Original line number Diff line number Diff line
package cz.fidentis.analyst.registration;

import com.jogamp.opengl.util.FPSAnimator;
import cz.fidentis.analyst.rendering.Direction;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Animator used to animate transformations of model.
 * Based on {@link cz.fidentis.analyst.rendering.RotationAnimator}.
 * 
 * @author Richard Pajersky
 */
public class ModelRotationAnimator {
    
    /**
     * Frequency of the rotation or zoom animations
     */
    private static final int FPS = 60; 
    
    /**
     * Animator used when the rotation or zoom buttons are pressed and held
     */
    private final FPSAnimator animator;
    
    /*
     * Animation timer
     */
    private long startClickTime = 0;
    private TimerTask task;
    private Timer timer;
    
    private Direction direction = Direction.NONE;
    
    /**
     * Constructor.
     */
    public ModelRotationAnimator() {
        this.animator = new FPSAnimator(FPS, true);
    }
    
    /**
     * Starts model animation
     * @param dir Transform direction
     * @param panel Panel
     */
    public void startModelAnimation(Direction dir, RegistrationPanel panel) {
        if (this.direction != Direction.NONE) {
            throw new UnsupportedOperationException(); // this should no happen
        }
        
        animator.start();
        timer = new Timer();
        startClickTime = System.currentTimeMillis();
        task = new TimerTask() {
            @Override
            public void run() {
                panel.transform(dir);
            }
        };
        timer.schedule(task, 500, 100);
        
        this.direction = dir;        
    }
    
    /**
     * Stops the animation.
     */
    public void stopModelAnimation(RegistrationPanel panel) {
        timer.cancel();
        if ((System.currentTimeMillis() - startClickTime) < 500) {
                panel.transform(direction);
        }
        startClickTime = 0;
        animator.stop();
        this.direction = Direction.NONE;
    }    
}
+48 −52

File changed.

Preview size limit exceeded, changes collapsed.

+8 −8
Original line number Diff line number Diff line
package cz.fidentis.analyst.canvas;

import cz.fidentis.analyst.rendering.RotationAnimator;
import cz.fidentis.analyst.rendering.Direction;
import cz.fidentis.analyst.rendering.AnimationDirection;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;

@@ -53,26 +53,26 @@ public class ControlButtonsAction extends AbstractAction {
        
        switch (command) {
            case ACTION_COMMAND_LEFT_PRESSED:
                animator.startAnimation(Direction.ROTATE_LEFT, canvas.getCamera());
                animator.startAnimation(AnimationDirection.ROTATE_LEFT, canvas.getCamera());
                break;
            case ACTION_COMMAND_RIGHT_PRESSED:
                animator.startAnimation(Direction.ROTATE_RIGHT, canvas.getCamera());
                animator.startAnimation(AnimationDirection.ROTATE_RIGHT, canvas.getCamera());
                break;
            case ACTION_COMMAND_UP_PRESSED:
                animator.startAnimation(Direction.ROTATE_UP, canvas.getCamera());
                animator.startAnimation(AnimationDirection.ROTATE_UP, canvas.getCamera());
                break;
            case ACTION_COMMAND_DOWN_PRESSED:
                animator.startAnimation(Direction.ROTATE_DOWN, canvas.getCamera());
                animator.startAnimation(AnimationDirection.ROTATE_DOWN, canvas.getCamera());
                break;
            case ACTION_COMMAND_RESET:
                canvas.getCamera().initLocation();
                canvas.renderScene();
                break;
            case ACTION_COMMAND_PLUS_PRESSED:
                animator.startAnimation(Direction.ZOOM_IN, canvas.getCamera());
                animator.startAnimation(AnimationDirection.ZOOM_IN, canvas.getCamera());
                break;
            case ACTION_COMMAND_MINUS_PRESSED:
                animator.startAnimation(Direction.ZOOM_OUT, canvas.getCamera());
                animator.startAnimation(AnimationDirection.ZOOM_OUT, canvas.getCamera());
                break;
            case ACTION_COMMAND_LEFT_RELEASED:
            case ACTION_COMMAND_RIGHT_RELEASED:
@@ -80,7 +80,7 @@ public class ControlButtonsAction extends AbstractAction {
            case ACTION_COMMAND_DOWN_RELEASED:
            case ACTION_COMMAND_PLUS_RELEASED:
            case ACTION_COMMAND_MINUS_RELEASED:
                animator.stopAnimation(canvas.getCamera());
                animator.stopAnimation();
                break;
             case ACTION_COMMAND_LEFT_DOUBLECLICK:
                canvas.getCamera().initLocation();
Loading