diff --git a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/DistributedIntensityCorrectionSolver.java b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/DistributedIntensityCorrectionSolver.java new file mode 100644 index 000000000..6afe3c4da --- /dev/null +++ b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/DistributedIntensityCorrectionSolver.java @@ -0,0 +1,174 @@ +package org.janelia.render.client.newsolver; + +import mpicbg.models.AffineModel1D; +import mpicbg.models.TranslationModel1D; +import org.janelia.render.client.newsolver.assembly.Assembler; +import org.janelia.render.client.newsolver.assembly.ZBlockSolver; +import org.janelia.render.client.newsolver.assembly.matches.SameTileMatchCreatorAffineIntensity; +import org.janelia.render.client.newsolver.blockfactories.ZBlockFactory; +import org.janelia.render.client.newsolver.blocksolveparameters.FIBSEMIntensityCorrectionParameters; +import org.janelia.render.client.newsolver.setup.IntensityCorrectionSetup; +import org.janelia.render.client.newsolver.setup.RenderSetup; +import org.janelia.render.client.newsolver.solvers.Worker; +import org.janelia.render.client.newsolver.solvers.WorkerTools; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +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; + +public class DistributedIntensityCorrectionSolver { + final IntensityCorrectionSetup cmdLineSetup; + final RenderSetup renderSetup; + BlockCollection, ? extends FIBSEMIntensityCorrectionParameters, ZBlockFactory> col; + ZBlockFactory blockFactory; + + public DistributedIntensityCorrectionSolver( + final IntensityCorrectionSetup cmdLineSetup, + final RenderSetup renderSetup) { + this.cmdLineSetup = cmdLineSetup; + this.renderSetup = renderSetup; + } + + public static void main(final String[] args) throws IOException { + final IntensityCorrectionSetup cmdLineSetup = new IntensityCorrectionSetup(); + + // TODO: remove testing hack ... + if (args.length == 0) { + final String[] testArgs = { + "--baseDataUrl", "http://em-services-1.int.janelia.org:8080/render-ws/v1", + "--owner", "Z0720_07m_BR", + "--project", "Sec24", + "--stack", "v5_acquire_trimmed_align", + "--targetStack", "v5_acquire_trimmed_test", + "--completeCorrectedStack", + "--numThreads", "12", + // for entire stack minZ is 1 and maxZ is 63,300 + "--zDistance", "1", "--minZ", "1000", "--maxZ", "1001" + }; + cmdLineSetup.parse(testArgs); + } else { + cmdLineSetup.parse(args); + } + + final RenderSetup renderSetup = RenderSetup.setupSolve(cmdLineSetup); + + // Note: different setups can be used if specific things need to be done for the solve or certain blocks + final DistributedIntensityCorrectionSolver solverSetup = new DistributedIntensityCorrectionSolver(cmdLineSetup, renderSetup); + + // create all block instances + final BlockCollection, ?, ZBlockFactory> blockCollection = solverSetup.setupSolve(); + + // + // multi-threaded solve + // + LOG.info("Multithreading with thread num=" + cmdLineSetup.threadsGlobal); + + final ArrayList, ?, ZBlockFactory>>>> workers = new ArrayList<>(); + + + for (final BlockData, ?, ZBlockFactory> block : blockCollection.allBlocks()) { + workers.add(() -> + { + final Worker, ?, ZBlockFactory> worker = block.createWorker( + solverSetup.col.maxId() + 1, + cmdLineSetup.threadsWorker); + + worker.run(); + + return new ArrayList<>(worker.getBlockDataList()); + }); + } + + final ArrayList, ?, ZBlockFactory>> allItems = new ArrayList<>(); + + try { + final ExecutorService taskExecutor = Executors.newFixedThreadPool(cmdLineSetup.threadsGlobal); + + taskExecutor.invokeAll(workers).forEach(future -> { + try { + allItems.addAll(future.get()); + } catch (final InterruptedException | ExecutionException e) { + LOG.error("Failed to compute alignments: " + e); + e.printStackTrace(); + } + }); + + taskExecutor.shutdown(); + } catch (final InterruptedException e) { + LOG.error("Failed to compute alignments: " + e); + e.printStackTrace(); + return; + } + + // avoid duplicate id assigned while splitting solveitems in the workers + // but do keep ids that are smaller or equal to the maxId of the initial solveset + final int maxId = WorkerTools.fixIds(allItems, solverSetup.col.maxId()); + + LOG.info("computed " + allItems.size() + " blocks, maxId=" + maxId); + + final ZBlockSolver, TranslationModel1D, ArrayList> solver = + new ZBlockSolver<>( + new TranslationModel1D(), + new SameTileMatchCreatorAffineIntensity(), + cmdLineSetup.distributedSolve.maxPlateauWidthGlobal, + cmdLineSetup.distributedSolve.maxAllowedErrorGlobal, + cmdLineSetup.distributedSolve.maxIterationsGlobal, + cmdLineSetup.threadsGlobal); + + final Assembler, TranslationModel1D, ArrayList, ZBlockFactory> assembler = new Assembler<>(allItems, solver); + assembler.createAssembly(); + } + + public BlockCollection, FIBSEMIntensityCorrectionParameters, ZBlockFactory> setupSolve() { + + final ZBlockFactory blockFactory = setupBlockFactory(); + this.blockFactory = blockFactory; + + final BlockCollection, FIBSEMIntensityCorrectionParameters, ZBlockFactory> col = + setupBlockCollection(blockFactory); + + this.col = col; + + return col; + } + + protected ZBlockFactory setupBlockFactory() { + final int minZ = (int) Math.round(renderSetup.minZ); + final int maxZ = (int) Math.round(renderSetup.maxZ); + final int blockSize = cmdLineSetup.distributedSolve.blockSize; + final int minBlockSize = cmdLineSetup.distributedSolve.minBlockSize; + + return new ZBlockFactory(minZ, maxZ, blockSize, minBlockSize); + } + + protected FIBSEMIntensityCorrectionParameters setupSolveParameters() { + + return new FIBSEMIntensityCorrectionParameters<>( + null, + cmdLineSetup.renderWeb.baseDataUrl, + cmdLineSetup.renderWeb.owner, + cmdLineSetup.renderWeb.project, + cmdLineSetup.stack, + cmdLineSetup.intensityCorrectedFilterStack, + cmdLineSetup.maxPixelCacheGb, + cmdLineSetup.lambdaTranslation, + cmdLineSetup.lambdaIdentity, + cmdLineSetup.renderScale, + cmdLineSetup.numCoefficients, + cmdLineSetup.zDistance); + } + + protected BlockCollection, FIBSEMIntensityCorrectionParameters, ZBlockFactory> setupBlockCollection( final ZBlockFactory blockFactory){ + + final FIBSEMIntensityCorrectionParameters defaultSolveParams = setupSolveParameters(); + return blockFactory.defineBlockCollection(rtsc -> defaultSolveParams); + } + + private static final Logger LOG = LoggerFactory.getLogger(DistributedIntensityCorrectionSolver.class); +} diff --git a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/Assembler.java b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/Assembler.java index 849875057..3d28f3d22 100644 --- a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/Assembler.java +++ b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/Assembler.java @@ -24,7 +24,7 @@ public class Assembler< Z, G, R, F extends BlockFactory< F > > /** * @param blocks - all individually computed blocks - * @param startId - we may need to create DummyBlocks where z slices do not overlap with anything (beginning and end of stack) + * @param blockSolver - solver to use for the final assembly */ public Assembler( final List > blocks, @@ -49,7 +49,7 @@ public AssemblyMaps< Z > createAssembly() { blockSolver.globalSolve( blocks, am ); } - catch ( Exception e) + catch (final Exception e) { e.printStackTrace(); } @@ -82,7 +82,7 @@ protected AssemblyMaps< Z > handleTrivialCase() final HashSet< String > tileIds = solveItem.zToTileId().get( z ); // if there are none, we continue with the next - if ( tileIds.size() == 0 ) + if (tileIds.isEmpty()) continue; am.zToTileIdGlobal.putIfAbsent( z, new HashSet<>() ); diff --git a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/matches/SameTileMatchCreatorAffineIntensity.java b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/matches/SameTileMatchCreatorAffineIntensity.java index f950b0edd..4ca0e879c 100644 --- a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/matches/SameTileMatchCreatorAffineIntensity.java +++ b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/assembly/matches/SameTileMatchCreatorAffineIntensity.java @@ -1,5 +1,6 @@ package org.janelia.render.client.newsolver.assembly.matches; +import java.util.ArrayList; import java.util.List; import org.janelia.alignment.spec.TileSpec; @@ -9,7 +10,7 @@ import mpicbg.models.Point; import mpicbg.models.PointMatch; -public class SameTileMatchCreatorAffineIntensity implements SameTileMatchCreator< List< AffineModel1D > > +public class SameTileMatchCreatorAffineIntensity implements SameTileMatchCreator> { final int samplesPerDimension; @@ -21,7 +22,7 @@ public SameTileMatchCreatorAffineIntensity( final int samplesPerDimension ) public SameTileMatchCreatorAffineIntensity() { this( 2 ); } @Override - public void addMatches(TileSpec tileSpec, List< AffineModel1D > modelA, List< AffineModel1D > modelB, List matchesAtoB) + public void addMatches(TileSpec tileSpec, ArrayList< AffineModel1D > modelA, ArrayList< AffineModel1D > modelB, List matchesAtoB) { // TODO: make 64 matches that map A to p and B to q diff --git a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/setup/IntensityCorrectionSetup.java b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/setup/IntensityCorrectionSetup.java new file mode 100644 index 000000000..8e130178d --- /dev/null +++ b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/setup/IntensityCorrectionSetup.java @@ -0,0 +1,132 @@ +package org.janelia.render.client.newsolver.setup; + +import com.beust.jcommander.IStringConverter; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParametersDelegate; +import org.janelia.render.client.intensityadjust.AdjustBlock; +import org.janelia.render.client.intensityadjust.AffineIntensityCorrectionStrategy; +import org.janelia.render.client.parameter.CommandLineParameters; +import org.janelia.render.client.parameter.RenderWebServiceParameters; +import org.janelia.render.client.parameter.ZRangeParameters; +import org.janelia.render.client.solver.SerializableValuePair; + +import java.util.List; + +public class IntensityCorrectionSetup extends CommandLineParameters { + private static final long serialVersionUID = -932686804562684884L; + + @ParametersDelegate + public RenderWebServiceParameters renderWeb = new RenderWebServiceParameters(); + + @ParametersDelegate + public DistributedSolveParameters distributedSolve = new DistributedSolveParameters(); + + @Parameter( + names = "--stack", + description = "Stack name", + required = true) + public String stack; + + @Parameter( + names = "--intensityCorrectedFilterStack", + description = "Name of stack to store tile specs with intensity corrected filter data. " + + "Omit to render intensity corrected scape-images to disk.") + public String intensityCorrectedFilterStack; + + @ParametersDelegate + public ZRangeParameters layerRange = new ZRangeParameters(); + + @Parameter( + names = "--z", + description = "Explicit z values for sections to be processed", + variableArity = true) // e.g. --z 20.0 21.0 22.0 + public List zValues; + + @Parameter( + names = "--numThreads", + description = "Number of threads to use") + public Integer numThreads = 1; + + @Parameter( + names = "--lambdaTranslation", + description = "Lambda for regularization with translation model") + public Double lambdaTranslation = AffineIntensityCorrectionStrategy.DEFAULT_LAMBDA; + + @Parameter( + names = "--lambdaIdentity", + description = "Lambda for regularization with identity model") + public Double lambdaIdentity = AffineIntensityCorrectionStrategy.DEFAULT_LAMBDA; + + @Parameter( + names = { "--maxPixelCacheGb" }, + description = "Maximum number of gigabytes of pixels to cache" + ) + public Integer maxPixelCacheGb = 1; + + @Parameter( + names = "--renderScale", + description = "Scale for rendered tiles used during intensity comparison") + public double renderScale = 0.1; + + @Parameter( + names = "--zDistance", + description = "If specified, apply correction across this many z-layers from the current z-layer " + + "(omit to only correct in 2D)") + public Integer zDistance; + + @Parameter( + names = { "--numCoefficients" }, + description = "Number of correction coefficients to derive in each dimension " + + "(e.g. value of 8 will divide each tile into 8x8 = 64 sub-regions)" + ) + public int numCoefficients = AdjustBlock.DEFAULT_NUM_COEFFICIENTS; + + // + // for saving and running + // + + @Parameter( + names = "--targetOwner", + description = "Owner name for intensity corrected result stack (default is same as owner)" + ) + public String targetOwner; + + @Parameter( + names = "--targetProject", + description = "Project name for intensity corrected result stack (default is same as project)" + ) + public String targetProject; + + @Parameter( + names = "--targetStack", + description = "Name for intensity corrected result stack (if omitted, models are simply logged)") + public String targetStack; + + @Parameter( + names = "--completeTargetStack", + description = "Complete the target stack after processing", + arity = 0) + public boolean completeTargetStack = false; + + @Parameter(names = "--threadsWorker", description = "Number of threads to be used within each worker job (default:1)") + public int threadsWorker = 1; + + @Parameter(names = "--threadsGlobal", description = "Number of threads to be used for global intensity correction (default: numProcessors/2)") + public int threadsGlobal = Math.max( 1, Runtime.getRuntime().availableProcessors() / 2 ); + + @Parameter( + names = "--visualizeResults", + description = "Visualize results (if running interactively)", + arity = 0) + public boolean visualizeResults = false; + + public void initDefaultValues() { + // owner for target is the same as owner for render, if not specified otherwise + if ( this.targetOwner == null ) + this.targetOwner = renderWeb.owner; + + // project for target is the same as project for render, if not specified otherwise + if ( this.targetProject == null ) + this.targetProject = renderWeb.project; + } +} diff --git a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/solvers/intensity/AffineIntensityCorrectionBlockWorker.java b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/solvers/intensity/AffineIntensityCorrectionBlockWorker.java index e6185f1fa..6f583460a 100644 --- a/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/solvers/intensity/AffineIntensityCorrectionBlockWorker.java +++ b/render-ws-java-client/src/main/java/org/janelia/render/client/newsolver/solvers/intensity/AffineIntensityCorrectionBlockWorker.java @@ -57,13 +57,13 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; -public class AffineIntensityCorrectionBlockWorker & Affine1D, F extends BlockFactory> - extends Worker, F> { +public class AffineIntensityCorrectionBlockWorker> + extends Worker, FIBSEMIntensityCorrectionParameters, F> { private final FIBSEMIntensityCorrectionParameters parameters; public AffineIntensityCorrectionBlockWorker( - final BlockData, F> blockData, + final BlockData, FIBSEMIntensityCorrectionParameters, F> blockData, final int startId, final int numThreads) throws IOException { super(startId, blockData, numThreads); @@ -311,7 +311,7 @@ public ArrayList convertModelsToOtfIntensities( * @return - the result(s) of the solve, multiple ones if they were not connected */ @Override - public ArrayList, F>> getBlockDataList() { + public ArrayList, FIBSEMIntensityCorrectionParameters, F>> getBlockDataList() { return null; }