Commit 9bc8c8e8 authored by Marek Horský's avatar Marek Horský Committed by Radek Ošlejšek
Browse files

Implements GPU Batch registration task

parent 11422d22
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -33,8 +33,10 @@ public class FaceRegistrationServicesOpenCL implements CLResources {
     * @param icpConfig       ICP configuration
     */
    public void alignMeshes(HumanFace transformedFace, IcpConfig icpConfig) {
        boolean areResultsNeeded = transformedFace.getLandmarks().hasLandmarks() || transformedFace.hasSymmetryPlane();

        // transform mesh:
        var trHistory = icpServicesOpenCL.transform(transformedFace.getMeshModel().getFacets(), icpConfig);
        var trHistory = icpServicesOpenCL.transform(transformedFace.getMeshModel().getFacets(), icpConfig, areResultsNeeded);

        FaceStateServices.updateKdTree(transformedFace, FaceStateServices.Mode.DELETE);
        FaceStateServices.updateLeftBalancedKdTree(transformedFace, FaceStateServices.Mode.DELETE);
+6 −2
Original line number Diff line number Diff line
@@ -21,9 +21,13 @@ public interface BatchFaceRegistration extends CLResources {
    void register(HumanFace face);

    /**
     * Returns the average mesh obtained by the metamorphoses of the {@code template face} se in the constructor.
     * Returns the average mesh obtained by the metamorphoses of the {@code template face} set in the constructor of
     * the implementing class.
     * <p>
     * If the {@link #register} method does not compute the average face, then the original mesh of
     * the {@code template face} is returned.
     * the {@code template face} is returned. Otherwise, the new instance of a mesh is returned. It is computed
     * by metamorphing the {@code template face} mesh according to all registered faces.
     * </p>
     *
     * @return the average mesh obtained by the metamorphoses of the {@code template face} se in the constructor.
     */
+150 −0
Original line number Diff line number Diff line
package cz.fidentis.analyst.engines.face.batch.registration;

import com.jogamp.opencl.CLContext;
import cz.fidentis.analyst.data.face.HumanFace;
import cz.fidentis.analyst.data.mesh.MeshModel;
import cz.fidentis.analyst.engines.avgmesh.AvgMeshConfig;
import cz.fidentis.analyst.engines.avgmesh.AvgMeshVisitor;
import cz.fidentis.analyst.engines.face.FaceRegistrationServicesOpenCL;
import cz.fidentis.analyst.engines.face.FaceStateServices;
import cz.fidentis.analyst.engines.icp.IcpConfig;
import cz.fidentis.analyst.engines.sampling.PointSamplingConfig;
import cz.fidentis.analyst.opencl.memory.CLResources;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;

import static cz.fidentis.analyst.engines.face.batch.registration.BatchFaceRegistrationServices.RegistrationStrategy.NONE;

/**
 * Provides batch face registration and face average functionality powered by GPU.
 *
 * @author Marek Horský
 */
public class BatchFaceRegistrationGPUImpl implements CLResources {
    private final FaceRegistrationServicesOpenCL faceRegistrationServicesOpenCL;
    private HumanFace templateFace;
    private BatchFaceRegistrationConfig config;
    private AvgMeshVisitor avgFaceVisitor;

    private IcpConfig icpConfig;

    public BatchFaceRegistrationGPUImpl(CLContext context) {
        faceRegistrationServicesOpenCL = new FaceRegistrationServicesOpenCL(context);
    }

    /**
     * Sets the configuration for batch registration and average face.
     *
     * @param templateFace a new template face
     * @param config       a new config
     */
    public void setConfig(HumanFace templateFace, BatchFaceRegistrationConfig config) {
        this.templateFace = templateFace;
        this.config = config;
        FaceStateServices.updateLeftBalancedKdTree(templateFace, FaceStateServices.Mode.COMPUTE_IF_ABSENT);
        PointSamplingConfig samplingConfig = (config.icpSubsampling() == 0)
                ? new PointSamplingConfig(PointSamplingConfig.Method.NO_SAMPLING, 0)
                : new PointSamplingConfig(PointSamplingConfig.Method.RANDOM, config.icpSubsampling());
        this.icpConfig = new IcpConfig(
                templateFace.getLeftBalancedKdTree(),
                config.icpIterations(),
                config.scale(),
                config.icpError(),
                samplingConfig,
                config.icpAutoCropSince());
    }

    /**
     * Registers each face of given face batch onto template face
     *
     * @param faceBatch face batch
     */
    public void register(List<HumanFace> faceBatch) {
        for (HumanFace face : faceBatch) {
            faceRegistrationServicesOpenCL.alignMeshes(face, icpConfig);
        }
    }

    /**
     * Registers each face in given queue onto template face, until loading is in progress.
     *
     * @param faceBatch face batch
     */
    public void register(AtomicBoolean isRunning, Queue<HumanFace> faceBatch) {
        while (isRunning.get()) {
            HumanFace face = faceBatch.poll();
            if (face != null) {
                faceRegistrationServicesOpenCL.alignMeshes(face, icpConfig);
            }
        }
    }

    /**
     * Calculates the average face. Must be called after register method.
     *
     * @param faceBatch face batch
     * @return average face.
     */
    public MeshModel getAverageMesh(List<HumanFace> faceBatch) {
        for (HumanFace face : faceBatch) {
            avgFaceVisitor = switch (config.avgFaceStrategy()) {
                case NONE -> null;
                case NEAREST_NEIGHBOURS -> computeAvgFaceNN(templateFace, face, avgFaceVisitor);
                case PROJECTION_CPU -> computeAvgFaceRT(templateFace, face, avgFaceVisitor);
                case PROJECTION_GPU -> computeAvgFaceRTGPU(templateFace, face, avgFaceVisitor);
            };
        }

        return (avgFaceVisitor == null) ? templateFace.getMeshModel() : avgFaceVisitor.getAveragedMeshModel();
    }


    @Override
    public void release() {
        faceRegistrationServicesOpenCL.release();
    }

    protected AvgMeshVisitor computeAvgFaceNN(HumanFace initFace, HumanFace superimposedFace, AvgMeshVisitor avgFaceVisitor) {
        // If the face was moved by registration, then the spatial ordering structure was removed => create it
        // If no transformation was made, then you can use old structure, if exists
        FaceStateServices.updateKdTree(
                superimposedFace,
                config.regStrategy() == NONE ? FaceStateServices.Mode.COMPUTE_IF_ABSENT : FaceStateServices.Mode.COMPUTE_ALWAYS
        );

        AvgMeshVisitor ret = (avgFaceVisitor == null)
                ? (new AvgMeshConfig(initFace.getMeshModel(), null)).getNearestNeighborsVisitor()
                : avgFaceVisitor;
        superimposedFace.getKdTree().accept(ret);

        return ret;
    }

    protected AvgMeshVisitor computeAvgFaceRT(HumanFace initFace, HumanFace superimposedFace, AvgMeshVisitor avgFaceVisitor) {
        // If the face was moved by registration, then the spatial ordering structure was removed => create it
        // If no transformation was made, then you can use old structure, if exists
        FaceStateServices.updateOctree(
                superimposedFace,
                config.regStrategy() == NONE ? FaceStateServices.Mode.COMPUTE_IF_ABSENT : FaceStateServices.Mode.COMPUTE_ALWAYS
        );

        AvgMeshVisitor ret = (avgFaceVisitor == null)
                ? (new AvgMeshConfig(initFace.getMeshModel(), null)).getRayCastingVisitor()
                : avgFaceVisitor;
        superimposedFace.getOctree().accept(ret);

        return ret;
    }

    protected AvgMeshVisitor computeAvgFaceRTGPU(HumanFace initFace, HumanFace superimposedFace, AvgMeshVisitor avgFaceVisitor) {
        AvgMeshVisitor ret = (avgFaceVisitor == null)
                ? (new AvgMeshConfig(initFace.getMeshModel(), config.glContext())).getRayCastingGpuVisitor()
                : avgFaceVisitor;

        superimposedFace.getMeshModel().compute(ret);

        return ret;
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -37,7 +37,12 @@ public class BatchFaceRegistrationServices {
         * Uses Iterative Closets Point algorithm reimplemented for GPU
         * No landmarks are required.
         */
        ICP_GPU
        ICP_GPU,

        /**
         * Purely for testing purposes, until a proper option in UI is implemented.
         */
        GPU_BATCH_REGISTRATION_PLACEHOLDER
    }

    /**
+20 −2
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ public class BatchFaceRegistrationImpl implements BatchFaceRegistration {
                        sampling,
                        config.icpAutoCropSince());
            }
            case ICP_GPU -> {
            case ICP_GPU, GPU_BATCH_REGISTRATION_PLACEHOLDER -> {
                this.faceRegistrationServicesOpenCL = new FaceRegistrationServicesOpenCL(OpenCLServices.createContext());
                PointSamplingConfig sampling = (config.icpSubsampling() == 0)
                        ? new PointSamplingConfig(PointSamplingConfig.Method.NO_SAMPLING, config.icpSubsampling())
@@ -82,7 +82,7 @@ public class BatchFaceRegistrationImpl implements BatchFaceRegistration {
            }
            case ICP -> FaceRegistrationServices.alignMeshes(face, icpConfig);
            case GPA -> FaceRegistrationServices.alignFeaturePoints(templateFace, face, config.scale());
            case ICP_GPU -> faceRegistrationServicesOpenCL.alignMeshes(face, icpConfig);
            case ICP_GPU, GPU_BATCH_REGISTRATION_PLACEHOLDER -> faceRegistrationServicesOpenCL.alignMeshes(face, icpConfig);
            default -> throw new IllegalStateException("Unexpected value: " + config.regStrategy());
        }

@@ -107,6 +107,15 @@ public class BatchFaceRegistrationImpl implements BatchFaceRegistration {
        }
    }

    /**
     * Update the average face geometry by taking into account given superimposed face.
     *
     * @param initFace A template face. Its geometry is used to compute the average geometry. However,
     *                 the geometry of the {@code initFace} remains unchanged (a new mesh is created instead)
     * @param superimposedFace A face newly registered to the {@code initFace}. Its geometry is used to update the average face geometry
     * @param avgFaceVisitor Average face visitor. If {@code null}, then new visitor is created.
     * @return Either newly created visitor or the {@code avgFaceVisitor}
     */
    protected AvgMeshVisitor computeAvgFaceNN(HumanFace initFace, HumanFace superimposedFace, AvgMeshVisitor avgFaceVisitor) {
        // If the face was moved by registration, then the spatial ordering structure was removed => create it
        // If no transformation was made, then you can use old structure, if exists
@@ -123,6 +132,15 @@ public class BatchFaceRegistrationImpl implements BatchFaceRegistration {
        return ret;
    }

    /**
     * Update the average face geometry by taking into account given superimposed face.
     *
     * @param initFace A template face. Its geometry is used to compute the average geometry. However,
     *                 the geometry of the {@code initFace} remains unchanged (a new mesh is created instead)
     * @param superimposedFace A face newly registered to the {@code initFace}. Its geometry is used to update the average face geometry
     * @param avgFaceVisitor Average face visitor. If {@code null}, then new visitor is created.
     * @return Either newly created visitor or the {@code avgFaceVisitor}
     */
    protected AvgMeshVisitor computeAvgFaceRT(HumanFace initFace, HumanFace superimposedFace, AvgMeshVisitor avgFaceVisitor) {
        // If the face was moved by registration, then the spatial ordering structure was removed => create it
        // If no transformation was made, then you can use old structure, if exists
Loading