Skip to content

Commit

Permalink
Merge branch 'newsolver' of github.com:saalfeldlab/render into newsolver
Browse files Browse the repository at this point in the history
  • Loading branch information
StephanPreibisch committed Aug 15, 2023
2 parents b1e1591 + 11dd5d7 commit 9488f81
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import mpicbg.models.Affine1D;
import mpicbg.models.Model;
import org.janelia.render.client.parameter.IntensityAdjustParameters;

import java.util.List;

Expand All @@ -20,36 +21,40 @@ public class FIBSEMIntensityCorrectionParameters<M extends Model<M> & Affine1D<M
final private List<Integer> blockOptimizerIterations;
final private List<Integer> blockMaxPlateauWidth;

final private double blockMaxAllowedError;
final private IntensityAdjustParameters intensityParameters;

public FIBSEMIntensityCorrectionParameters(
final M blockSolveModel,
final List<Double> blockOptimizerLambdasTranslation,
final List<Double> blockOptimizerLambdasIdentity,
final List<Integer> blockOptimizerIterations,
final List<Integer> blockMaxPlateauWidth,
final double blockMaxAllowedError,
final String baseDataUrl,
final String owner,
final String project,
final String stack) {
super(baseDataUrl, owner, project, stack, blockSolveModel.copy());
final IntensityAdjustParameters parameters) {
super(baseDataUrl, owner, project, parameters.stack, blockSolveModel.copy());

this.blockOptimizerLambdasTranslation = blockOptimizerLambdasTranslation;
this.blockOptimizerLambdasIdentity = blockOptimizerLambdasIdentity;
this.blockOptimizerIterations = blockOptimizerIterations;
this.blockMaxPlateauWidth = blockMaxPlateauWidth;
this.blockMaxAllowedError = blockMaxAllowedError;
this.intensityParameters = parameters;
}

public List<Double> blockOptimizerLambdasTranslation() { return blockOptimizerLambdasTranslation; }
public List<Double> blockOptimizerLambdasRigid() { return blockOptimizerLambdasIdentity; }
public List<Integer> blockOptimizerIterations() { return blockOptimizerIterations; }
public List<Integer> blockMaxPlateauWidth() {return blockMaxPlateauWidth; }
public double blockMaxAllowedError() { return blockMaxAllowedError; }

@Override
public FIBSEMIntensityCorrectionParameters<M> createInstance(final boolean hasIssue) {
return null;
}
public FIBSEMIntensityCorrectionParameters<M> createInstance(final boolean hasIssue) { return null; }
public String intensityCorrectedFilterStack() { return intensityParameters.intensityCorrectedFilterStack; }
public String matchCollection() { return intensityParameters.matchCollection; }
public long maxNumberOfCachedPixels() { return intensityParameters.getMaxNumberOfCachedPixels(); }
public double lambdaTranslation() { return intensityParameters.lambda1; }
public double lambdaIdentity() { return intensityParameters.lambda2; }
public double renderScale() { return intensityParameters.renderScale; }
public int numCoefficients() { return intensityParameters.numCoefficients; }
public Integer zDistance() { return intensityParameters.zDistance; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.janelia.alignment.spec.ResolvedTileSpecCollection;
import org.janelia.alignment.spec.TileSpec;
import org.janelia.render.client.RenderDataClient;
import org.janelia.render.client.intensityadjust.MinimalTileSpecWrapper;
import org.janelia.render.client.newsolver.BlockData;
import org.janelia.render.client.newsolver.blockfactories.BlockFactory;
import org.janelia.render.client.newsolver.blocksolveparameters.FIBSEMAlignmentParameters;
Expand All @@ -25,9 +24,7 @@
import org.janelia.render.client.newsolver.solvers.WorkerTools;
import org.janelia.render.client.newsolver.solvers.WorkerTools.LayerDetails;
import org.janelia.render.client.solver.ConstantAffineModel2D;
import org.janelia.render.client.solver.DistributedSolveWorker;
import org.janelia.render.client.solver.Graph;
import org.janelia.render.client.solver.MinimalTileSpec;
import org.janelia.render.client.solver.SolveTools;
import org.janelia.render.client.solver.SerializableValuePair;
import org.janelia.render.client.solver.SolveItem;
Expand Down Expand Up @@ -359,7 +356,7 @@ protected boolean assignRegularizationModel(

final Model<?> model = ((InterpolatedAffineModel2D<?,?>) zToGroupedTileList.get(allZ.get(0)).get(0).getModel()).getB();

if ( ConstantAffineModel2D.class.isInstance( model ) )
if (model instanceof ConstantAffineModel2D)
{
//
// it is based on ConstantAffineModels, meaning we extract metadata and use that as regularizer
Expand Down Expand Up @@ -461,7 +458,7 @@ protected boolean assignRegularizationModel(
}
return true;
}
else if ( StabilizingAffineModel2D.class.isInstance( model ) )
else if (model instanceof StabilizingAffineModel2D)
{
//
// it is based on StabilizingAffineModel2Ds, meaning each image wants to sit where its corresponding one in the above layer sits
Expand Down Expand Up @@ -535,7 +532,7 @@ else if ( StabilizingAffineModel2D.class.isInstance( model ) )
if ( neighboringTile.tileCol == tileCol && neighboringTile.tileRow == tileRow )
neighbors.add( neighboringTile );

if ( neighbors.size() == 0 )
if (neighbors.isEmpty())
{
// this can happen when number of tiles per layer changes for example
LOG.info( "could not find corresponding tile for: " + tileId );
Expand Down Expand Up @@ -703,7 +700,7 @@ protected void stitchSectionsAndCreateGroupedTiles(
{
LOG.info( "block " + solveItem.blockData().getId() + ": unconnected tileId " + tileId );

final Tile< S > tile = new Tile< S >( solveItem.blockData().solveTypeParameters().stitchingSolveModelInstance( z ).copy() );
final Tile< S > tile = new Tile<>(solveItem.blockData().solveTypeParameters().stitchingSolveModelInstance(z).copy());
idTotile.put( tileId, tile );
tileToId.put( tile, tileId );
}
Expand Down Expand Up @@ -840,7 +837,7 @@ protected List< AffineBlockDataWrapper< M, S, F > > splitSolveItem( final Affine

LOG.info( "block " + inputSolveItem.blockData().getId() + ": Graph of SolveItem " + inputSolveItem.blockData().getId() + " consists of " + graphs.size() + " subgraphs." );

if ( graphs.size() == 0 )
if (graphs.isEmpty())
{
throw new RuntimeException( "Something went wrong. The inputsolve item has 0 subgraphs. stopping." );
}
Expand Down Expand Up @@ -877,13 +874,13 @@ else if ( graphs.size() == 1 )

final AffineBlockDataWrapper< M, S, F > solveItem =
new AffineBlockDataWrapper<>(
new BlockData< M, AffineModel2D, FIBSEMAlignmentParameters< M, S >, F >(
new BlockData<>(
inputSolveItem.blockData().blockFactory(), // no copy necessary
inputSolveItem.blockData().solveTypeParameters(), // no copy necessary
id,
inputSolveItem.blockData().weightFunctions(), // no copy necessary
allTileIdsNew,
idToTileSpecNew ) );
idToTileSpecNew) );

++id;

Expand Down Expand Up @@ -933,7 +930,7 @@ else if ( graphs.size() == 1 )
myTilesPerZ.add( tileId );
}

if ( myTilesPerZ.size() == 0 )
if (myTilesPerZ.isEmpty())
{
LOG.info( "block " + solveItem.blockData().getId() + ": ERROR: z=" + z + " of new graph has 0 tileIds, the must not happen, this is a bug." );
System.exit( 0 );
Expand Down Expand Up @@ -1082,7 +1079,7 @@ else if ( preAlign == PreAlign.TRANSLATION )
final String tileId = solveItem.tileToIdMap().get( tile );

tileIds.add( tileId );
tileIdToGroupModel.put( tileId, SolveTools.createAffine( (Affine2D<?>)solveItem.tileToGroupedTile().get( tile ).getModel() ) );
tileIdToGroupModel.put( tileId, SolveTools.createAffine(solveItem.tileToGroupedTile().get(tile ).getModel()) );
}

Collections.sort( tileIds );
Expand Down Expand Up @@ -1200,7 +1197,7 @@ private void safelyTraceConnectedGraph(final Tile<?> forTile,
final Set< Tile< ? > > deferredTiles = new HashSet<>();
safelyTraceConnectedGraph(tile, currentGraph, deferredTiles, 0);

while (deferredTiles.size() > 0) {
while (!deferredTiles.isEmpty()) {
LOG.info("safelyIdentifyConnectedGraphs: {} max recursion deferred tiles, current graph size is {}",
deferredTiles.size(), currentGraph.size());
final List<Tile<?>> toDoList = new ArrayList<>(deferredTiles);
Expand All @@ -1223,5 +1220,5 @@ private void safelyTraceConnectedGraph(final Tile<?> forTile,
return graphs;
}

private static final Logger LOG = LoggerFactory.getLogger(DistributedSolveWorker.class);
private static final Logger LOG = LoggerFactory.getLogger(Worker.class);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package org.janelia.render.client.newsolver.solvers.intensity;

import mpicbg.models.Affine1D;
import mpicbg.models.Model;
import mpicbg.models.NoninvertibleModelException;
import net.imglib2.util.ValuePair;
import org.janelia.alignment.filter.FilterSpec;
import org.janelia.alignment.filter.IntensityMap8BitFilter;
import org.janelia.alignment.match.CanvasMatches;
import org.janelia.alignment.spec.ResolvedTileSpecCollection;
import org.janelia.alignment.spec.TileSpec;
import org.janelia.alignment.spec.stack.StackMetaData;
import org.janelia.alignment.util.ImageProcessorCache;
import org.janelia.render.client.RenderDataClient;
import org.janelia.render.client.intensityadjust.AdjustBlock;
import org.janelia.render.client.intensityadjust.AffineIntensityCorrectionStrategy;
import org.janelia.render.client.intensityadjust.IntensityCorrectionStrategy;
import org.janelia.render.client.intensityadjust.MinimalTileSpecWrapper;
import org.janelia.render.client.intensityadjust.virtual.OnTheFlyIntensity;
import org.janelia.render.client.newsolver.BlockData;
import org.janelia.render.client.newsolver.blockfactories.BlockFactory;
import org.janelia.render.client.newsolver.blocksolveparameters.FIBSEMIntensityCorrectionParameters;
import org.janelia.render.client.newsolver.solvers.Worker;
import org.janelia.render.client.parameter.IntensityAdjustParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

public class AffineIntensityCorrectionBlockWorker<M extends Model<M> & Affine1D<M>, F extends BlockFactory<F>>
extends Worker<M, M, FIBSEMIntensityCorrectionParameters<M>, F> {

private final FIBSEMIntensityCorrectionParameters<M> parameters;

public AffineIntensityCorrectionBlockWorker(
final BlockData<M, M, FIBSEMIntensityCorrectionParameters<M>, F> blockData,
final int startId,
final int numThreads) throws IOException {
super(startId, blockData, numThreads);
parameters = blockData.solveTypeParameters();

if (parameters.intensityCorrectedFilterStack() != null) {
final StackMetaData stackMetaData = renderDataClient.getStackMetaData(renderStack);
renderDataClient.setupDerivedStack(stackMetaData, parameters.intensityCorrectedFilterStack());
}
}

/**
* runs the Worker
*/
@Override
public void run() throws IOException, ExecutionException, InterruptedException, NoninvertibleModelException {

// TODO: blockData.idToTileSpec() already resolved?
final ResolvedTileSpecCollection resolvedTileSpecs = getResolvedTileSpecs();

// if specified, use match collection to determine patch pairs instead of tile bounds w/distanceZ
final List<CanvasMatches> tilePairs;
if (parameters.matchCollection() != null) {
tilePairs = getMatchPairsFromCollection(parameters.matchCollection());
} else {
tilePairs = new ArrayList<>();
}

deriveAndStoreIntensityFilterData(renderDataClient, resolvedTileSpecs, tilePairs);
LOG.info("AffineIntensityCorrectionBlockWorker: exit, minZ={}, maxZ={}", blockData.minZ(), blockData.maxZ());
}

private List<CanvasMatches> getMatchPairsFromCollection(final String matchCollection) throws IOException {

final RenderDataClient matchClient = new RenderDataClient(renderDataClient.getBaseDataUrl(), renderDataClient.getOwner(), matchCollection);
final List<CanvasMatches> tilePairs = new ArrayList<>();
final Set<String> alreadyConsidered = new HashSet<>();

for (final TileSpec tileSpec : blockData.idToTileSpec().values()) {
final String pGroupId = tileSpec.getLayout().getSectionId();
final boolean isInBlock = blockData.idToTileSpec().containsKey(pGroupId);
if (!alreadyConsidered.contains(pGroupId) && isInBlock) {
for (final CanvasMatches pair : matchClient.getMatchesWithPGroupId(pGroupId, true)) {
if (blockData.idToTileSpec().containsKey(pair.getqId()))
tilePairs.add(pair);
}
alreadyConsidered.add(pGroupId);
}
}
return tilePairs;
}

private ResolvedTileSpecCollection getResolvedTileSpecs() {
final ResolvedTileSpecCollection resolvedTiles;
try {
if (blockData.minZ() == blockData.maxZ()) {
resolvedTiles = renderDataClient.getResolvedTiles(renderStack, (double) blockData.minZ());
} else {
resolvedTiles = renderDataClient.getResolvedTilesForZRange(renderStack, (double) blockData.minZ(), (double) blockData.maxZ());
}
} catch (final IOException e) {
LOG.error("Error getting resolved tiles for stack {} and z range {}-{}", renderStack, blockData.minZ(), blockData.maxZ());
throw new RuntimeException(e);
}

final Set<String> outOfBlockIds = resolvedTiles.getTileIds();
outOfBlockIds.removeAll(blockData.idToTileSpec().keySet());
resolvedTiles.removeTileSpecs(outOfBlockIds);

return resolvedTiles;
}

protected void deriveAndStoreIntensityFilterData(
final RenderDataClient dataClient,
final ResolvedTileSpecCollection resolvedTiles,
final List<CanvasMatches> tilePairs) throws ExecutionException, InterruptedException, IOException {

LOG.info("deriveAndStoreIntensityFilterData: entry");

if (resolvedTiles.getTileCount() > 1) {
final long maxCachedPixels = parameters.maxNumberOfCachedPixels();
final ImageProcessorCache imageProcessorCache = (maxCachedPixels == 0)
? ImageProcessorCache.DISABLED_CACHE
: new ImageProcessorCache(parameters.maxNumberOfCachedPixels(), true, false);

final List<MinimalTileSpecWrapper> wrappedTiles = AdjustBlock.wrapTileSpecs(resolvedTiles);
final IntensityCorrectionStrategy strategy = new AffineIntensityCorrectionStrategy(parameters.lambdaTranslation(), parameters.lambdaIdentity());
final List<OnTheFlyIntensity> corrected;

if (!tilePairs.isEmpty()) {
final List<ValuePair<MinimalTileSpecWrapper, MinimalTileSpecWrapper>> patchPairs =
tilePairs.stream()
.map(tp -> new ValuePair<>(resolvedTiles.getTileSpec(tp.getpId()), resolvedTiles.getTileSpec(tp.getqId())))
.filter(vp -> (vp.getA() != null) && (vp.getB() != null))
.map(vp -> new ValuePair<>(new MinimalTileSpecWrapper(vp.getA()), new MinimalTileSpecWrapper(vp.getB())))
.collect(Collectors.toList());

corrected = AdjustBlock.correctIntensitiesForPatchPairs(patchPairs,
parameters.renderScale(),
imageProcessorCache,
parameters.numCoefficients(),
strategy,
numThreads);
} else {
corrected = AdjustBlock.correctIntensitiesForSliceTiles(wrappedTiles,
parameters.renderScale(),
parameters.zDistance(),
imageProcessorCache,
parameters.numCoefficients(),
strategy,
numThreads);
}

for (final OnTheFlyIntensity onTheFlyIntensity : corrected) {
final String tileId = onTheFlyIntensity.getMinimalTileSpecWrapper().getTileId();
final TileSpec tileSpec = resolvedTiles.getTileSpec(tileId);
final IntensityMap8BitFilter filter = onTheFlyIntensity.toFilter();
final FilterSpec filterSpec = new FilterSpec(filter.getClass().getName(), filter.toParametersMap());

tileSpec.setFilterSpec(filterSpec);
tileSpec.convertSingleChannelSpecToLegacyForm();
}
} else {
final String tileCountMsg = resolvedTiles.getTileCount() == 1 ? "1 tile" : "0 tiles";
LOG.info("deriveAndStoreIntensityFilterData: skipping correction because collection contains {}", tileCountMsg);
}

dataClient.saveResolvedTiles(resolvedTiles, parameters.intensityCorrectedFilterStack(), null);
}

/**
* @return - the result(s) of the solve, multiple ones if they were not connected
*/
@Override
public List<BlockData<M, M, FIBSEMIntensityCorrectionParameters<M>, F>> getBlockDataList() {
return null;
}

private static final Logger LOG = LoggerFactory.getLogger(Worker.class);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.janelia.render.client.newsolver.solvers.intensity;

import mpicbg.models.Affine1D;
import mpicbg.models.Model;
import mpicbg.models.Tile;
import org.janelia.render.client.newsolver.BlockData;
import org.janelia.render.client.newsolver.blockfactories.BlockFactory;
import org.janelia.render.client.newsolver.blocksolveparameters.FIBSEMIntensityCorrectionParameters;

import java.util.HashMap;
import java.util.Map;


/**
* Wrapper for all data needed to run the intensity correction solver
*/
public class IntensityBlockDataWrapper<M extends Model<M> & Affine1D<M>, F extends BlockFactory<F>> {

final BlockData<M, ?, FIBSEMIntensityCorrectionParameters<M>, F> blockData;
final private Map<String, Tile<M>> idToTile = new HashMap<>();

public IntensityBlockDataWrapper(final BlockData<M, ?, FIBSEMIntensityCorrectionParameters<M>, F> blockData) {
this.blockData = blockData;
}

public BlockData<M, ?, FIBSEMIntensityCorrectionParameters<M>, F> blockData() { return blockData; }
public Map<String, Tile<M>> idToTile() { return idToTile; }
}
Loading

0 comments on commit 9488f81

Please sign in to comment.