Loading FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/FaceRegistrationServicesOpenCL.java +3 −1 Original line number Diff line number Diff line Loading @@ -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); Loading FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/BatchFaceRegistration.java +6 −2 Original line number Diff line number Diff line Loading @@ -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. */ Loading FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/BatchFaceRegistrationGPUImpl.java 0 → 100644 +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; } } FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/BatchFaceRegistrationServices.java +6 −1 Original line number Diff line number Diff line Loading @@ -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 } /** Loading FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/impl/BatchFaceRegistrationImpl.java +20 −2 Original line number Diff line number Diff line Loading @@ -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()) Loading @@ -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()); } Loading @@ -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 Loading @@ -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 Loading
FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/FaceRegistrationServicesOpenCL.java +3 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/BatchFaceRegistration.java +6 −2 Original line number Diff line number Diff line Loading @@ -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. */ Loading
FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/BatchFaceRegistrationGPUImpl.java 0 → 100644 +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; } }
FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/BatchFaceRegistrationServices.java +6 −1 Original line number Diff line number Diff line Loading @@ -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 } /** Loading
FaceEngines/src/main/java/cz/fidentis/analyst/engines/face/batch/registration/impl/BatchFaceRegistrationImpl.java +20 −2 Original line number Diff line number Diff line Loading @@ -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()) Loading @@ -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()); } Loading @@ -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 Loading @@ -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