Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.scijava</groupId>
<artifactId>pom-scijava</artifactId>
<version>40.0.0</version>
<version>41.0.0</version>
<relativePath />
</parent>

Expand Down Expand Up @@ -143,8 +143,7 @@
<license.licenseName>bsd_2</license.licenseName>
<license.copyrightOwners>BigDataViewer developers.</license.copyrightOwners>

<imglib2.version>7.1.5</imglib2.version>
<imglib2-algorithm.version>0.18.1</imglib2-algorithm.version>
<imglib2.version>8.0.0</imglib2.version>

<!-- NB: Deploy releases to the SciJava Maven repository. -->
<releaseProfiles>sign,deploy-to-scijava</releaseProfiles>
Expand Down
101 changes: 75 additions & 26 deletions src/main/java/bdv/AbstractSpimSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Expand All @@ -34,6 +34,7 @@
import java.util.Set;

import bdv.viewer.Interpolation;
import bdv.viewer.MaskUtils;
import bdv.viewer.Source;
import mpicbg.spim.data.generic.AbstractSpimData;
import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription;
Expand All @@ -52,7 +53,11 @@
import net.imglib2.interpolation.randomaccess.ClampingNLinearInterpolatorFactory;
import net.imglib2.interpolation.randomaccess.NearestNeighborInterpolatorFactory;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.mask.Masked;
import net.imglib2.type.numeric.ARGBType;
import net.imglib2.type.numeric.NumericType;
import net.imglib2.type.volatiles.VolatileARGBType;
import net.imglib2.util.Cast;
import net.imglib2.view.Views;

public abstract class AbstractSpimSource< T extends NumericType< T > > implements Source< T >
Expand Down Expand Up @@ -129,19 +134,14 @@ public boolean equals( final Object obj )

private final int numMipmapLevels;

private final static int numInterpolationMethods = 2;

private final static int iNearestNeighborMethod = 0;

private final static int iNLinearMethod = 1;

private final InterpolatorFactory< T, RandomAccessible< T > >[] interpolatorFactories;

private final UncheckedCache< ImgKey, RandomAccessibleInterval< T > > cachedSources;

private final UncheckedCache< ImgKey, RealRandomAccessible< T > > cachedInterpolatedSources;

@SuppressWarnings( "unchecked" )
private final UncheckedCache< ImgKey, RandomAccessibleInterval< ? extends Masked< T > > > cachedMaskedSources;

private final UncheckedCache< ImgKey, RealRandomAccessible< ? extends Masked< T > > > cachedInterpolatedMaskedSources;

public AbstractSpimSource( final AbstractSpimData< ? > spimData, final int setupId, final String name )
{
this.setupId = setupId;
Expand All @@ -155,35 +155,54 @@ public AbstractSpimSource( final AbstractSpimData< ? > spimData, final int setup
voxelDimensions = seq.getViewSetups().get( setupId ).getVoxelSize();
numMipmapLevels = ( ( ViewerImgLoader ) seq.getImgLoader() ).getSetupImgLoader( setupId ).numMipmapLevels();

interpolatorFactories = new InterpolatorFactory[ numInterpolationMethods ];
interpolatorFactories[ iNearestNeighborMethod ] = new NearestNeighborInterpolatorFactory<>();
interpolatorFactories[ iNLinearMethod ] = new ClampingNLinearInterpolatorFactory<>();

final CacheLoader< ImgKey, RandomAccessibleInterval< T > > loader = key -> {
return getImage( timePointsOrdered.get( key.timepoint ).getId(), key.level );
};
final CacheLoader< ImgKey, RandomAccessibleInterval< T > > loader = key ->
getImage( timePointsOrdered.get( key.timepoint ).getId(), key.level );
cachedSources = Caches.unchecked( Caches.withLoader(
new BoundedSoftRefLoaderCache<>( 3 * numMipmapLevels ),
loader ) );

final CacheLoader< ImgKey, RealRandomAccessible< T > > interpolLoader = key -> {
final T zero = getType().createVariable();
zero.setZero();
final int i = key.method == Interpolation.NLINEAR ? iNLinearMethod : iNearestNeighborMethod;
final InterpolatorFactory< T, RandomAccessible< T > > factory = interpolatorFactories[ i ];
return Views.interpolate( Views.extendValue( getSource( key.timepoint, key.level ), zero ), factory );
};
final CacheLoader< ImgKey, RealRandomAccessible< T > > interpolLoader = key ->
Views.interpolate( Views.extendZero( getSource( key.timepoint, key.level, key.threadGroup ) ), interpolator( key.method ) );
cachedInterpolatedSources = Caches.unchecked( Caches.withLoader(
new BoundedSoftRefLoaderCache<>( 3 * numMipmapLevels * numInterpolationMethods ),
new BoundedSoftRefLoaderCache<>( 3 * numMipmapLevels * Interpolation.values().length ),
interpolLoader ) );

final CacheLoader< ImgKey, RandomAccessibleInterval< ? extends Masked< T > > > maskedLoader = key ->
Masked.withConstant( getSource( key.timepoint, key.level, key.threadGroup ), 1 );
cachedMaskedSources = Caches.unchecked( Caches.withLoader(
new BoundedSoftRefLoaderCache<>( 3 * numMipmapLevels ),
maskedLoader ) );

final CacheLoader< ImgKey, RealRandomAccessible< ? extends Masked< T > > > maskedInterpolLoader = key -> {
if ( getType() instanceof ARGBType || getType() instanceof VolatileARGBType )
{
// TODO: How to correctly handle ARGBType sources? We could make a
// Masked<ARGBType> wrapper that uses the ARGBType's A as mask?
return Masked.withConstant( getInterpolatedSource( key.timepoint, key.level, key.method, key.threadGroup ), 1 );
}
else
{
return MaskUtils.extendAndInterpolateMasked( Cast.unchecked( getMaskedSource( key.timepoint, key.level, key.threadGroup ) ), key.method );
}
};
cachedInterpolatedMaskedSources = Caches.unchecked( Caches.withLoader(
new BoundedSoftRefLoaderCache<>( 3 * numMipmapLevels * Interpolation.values().length ),
maskedInterpolLoader ) );

currentSourceTransforms = new AffineTransform3D[ numMipmapLevels ];
for ( int level = 0; level < numMipmapLevels; level++ )
currentSourceTransforms[ level ] = new AffineTransform3D();

currentTimePointIndex = -1;
}

private static < T extends NumericType< T > > InterpolatorFactory< T, RandomAccessible< T > > interpolator( final Interpolation method )
{
return method == Interpolation.NEARESTNEIGHBOR
? new NearestNeighborInterpolatorFactory<>()
: new ClampingNLinearInterpolatorFactory<>();
}

void loadTimepoint( final int timepointIndex )
{
currentTimePointIndex = timepointIndex;
Expand Down Expand Up @@ -291,4 +310,34 @@ public void reload()
{
currentTimePointIndex = -1;
}

@Override
public RandomAccessibleInterval< ? extends Masked< T > > getMaskedSource( final int t, final int level )
{
return getMaskedSource( t, level, Thread.currentThread().getThreadGroup() );
}

public synchronized RandomAccessibleInterval< ? extends Masked< T > > getMaskedSource( final int t, final int level, final ThreadGroup threadGroup )
{
if ( t != currentTimePointIndex )
loadTimepoint( t );
return currentTimePointIsPresent
? cachedMaskedSources.get( new ImgKey( t, level, null, threadGroup ) )
: null;
}

@Override
public RealRandomAccessible< ? extends Masked< T > > getInterpolatedMaskedSource( int t, int level, final Interpolation method )
{
return getInterpolatedMaskedSource( t, level, method, Thread.currentThread().getThreadGroup() );
}

public synchronized RealRandomAccessible< ? extends Masked< T > > getInterpolatedMaskedSource( final int t, final int level, final Interpolation method, final ThreadGroup threadGroup )
{
if ( t != currentTimePointIndex )
loadTimepoint( t );
return currentTimePointIsPresent
? cachedInterpolatedMaskedSources.get( new ImgKey( t, level, method, threadGroup ) )
: null;
}
}
56 changes: 35 additions & 21 deletions src/main/java/bdv/tools/InitializeViewerState.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import net.imglib2.histogram.Histogram1d;
import net.imglib2.histogram.Real1dBinMapper;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.integer.UnsignedByteType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import net.imglib2.util.LinAlgHelpers;
Expand Down Expand Up @@ -198,34 +199,47 @@ public static Bounds estimateSourceRange( final Source< ? > source, final int ti
{
@SuppressWarnings( "unchecked" )
final RandomAccessibleInterval< UnsignedShortType > img = ( RandomAccessibleInterval< UnsignedShortType > ) source.getSource( timepoint, source.getNumMipmapLevels() - 1 );
final long z = ( img.min( 2 ) + img.max( 2 ) + 1 ) / 2;

final int numBins = 6535;
final Histogram1d< ? > histogram = new Histogram1d<>( Views.hyperSlice( img, 2, z ), new Real1dBinMapper<>( 0, 65535, numBins, false ) );
final DiscreteFrequencyDistribution dfd = histogram.dfd();
final long[] bin = new long[] { 0 };
double cumulative = 0;
int i = 0;
for ( ; i < numBins && cumulative < cumulativeMinCutoff; ++i )
{
bin[ 0 ] = i;
cumulative += dfd.relativeFrequency( bin );
}
final int min = i * 65535 / numBins;
for ( ; i < numBins && cumulative < cumulativeMaxCutoff; ++i )
{
bin[ 0 ] = i;
cumulative += dfd.relativeFrequency( bin );
}
final int max = i * 65535 / numBins;
return new Bounds( min, max );
return estimateBounds( img, cumulativeMinCutoff, cumulativeMaxCutoff);
}
else if ( type instanceof UnsignedByteType )
return new Bounds( 0, 255 );
else
return new Bounds( 0, 65535 );
}

/**
* @param img
* 3D image, histogram of the center plane is used for estimating instensity range.
* @param cumulativeMinCutoff
* fraction of pixels that are allowed to be saturated at the lower end of the range.
* @param cumulativeMaxCutoff
* fraction of pixels that are allowed to be saturated at the upper end of the range.
*/
private static < T extends RealType< T > > Bounds estimateBounds( final RandomAccessibleInterval< T > img, final double cumulativeMinCutoff, final double cumulativeMaxCutoff )
{
final long z = ( img.min( 2 ) + img.max( 2 ) + 1 ) / 2;

final int numBins = 6535;
final Histogram1d< ? > histogram = new Histogram1d<>( Views.hyperSlice( img, 2, z ), new Real1dBinMapper<>( 0, 65535, numBins, false ) );
final DiscreteFrequencyDistribution dfd = histogram.dfd();
final long[] bin = new long[] { 0 };
double cumulative = 0;
int i = 0;
for ( ; i < numBins && cumulative < cumulativeMinCutoff; ++i )
{
bin[ 0 ] = i;
cumulative += dfd.relativeFrequency( bin );
}
final int min = i * 65535 / numBins;
for ( ; i < numBins && cumulative < cumulativeMaxCutoff; ++i )
{
bin[ 0 ] = i;
cumulative += dfd.relativeFrequency( bin );
}
final int max = i * 65535 / numBins;
return new Bounds( min, max );
}

@Deprecated
public static AffineTransform3D initTransform( final int viewerWidth, final int viewerHeight, final boolean zoomedIn, final bdv.viewer.state.ViewerState state )
{
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/bdv/tools/RecordMaxProjectionDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ public int getHeight()
final MyTarget target = new MyTarget();
final MultiResolutionRenderer renderer = new MultiResolutionRenderer(
target, () -> {}, new double[] { 1 }, 0, 1, null, false,
viewer.getOptionValues().getAccumulateProjectorFactory(), new CacheControl.Dummy() );
new CacheControl.Dummy() );
progressWriter.setProgress( 0 );
for ( int timepoint = minTimepointIndex; timepoint <= maxTimepointIndex; ++timepoint )
{
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/bdv/tools/RecordMovieDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ public int getHeight()
final MyTarget target = new MyTarget();
final MultiResolutionRenderer renderer = new MultiResolutionRenderer(
target, () -> {}, new double[] { 1 }, 0, 1, null, false,
viewer.getOptionValues().getAccumulateProjectorFactory(), new CacheControl.Dummy() );
new CacheControl.Dummy() );
progressWriter.setProgress( 0 );
for ( int timepoint = minTimepointIndex; timepoint <= maxTimepointIndex; ++timepoint )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
import net.imglib2.RealLocalizable;
import net.imglib2.RealRandomAccess;
import net.imglib2.RealRandomAccessible;
import net.imglib2.Sampler;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.mask.Masked;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import net.imglib2.util.Intervals;
import net.imglib2.view.Views;
Expand Down Expand Up @@ -106,6 +106,18 @@ public RealRandomAccessible< Void > getInterpolatedSource( final int t, final in
return rra;
}

@Override
public RandomAccessibleInterval< ? extends Masked< Void > > getMaskedSource( final int t, final int level )
{
return Views.interval( Views.raster( Masked.withConstant( rra, 0 ) ), Intervals.smallestContainingInterval( bbSource.getInterval() ) );
}

@Override
public RealRandomAccessible< ? extends Masked< Void > > getInterpolatedMaskedSource( final int t, final int level, final Interpolation method )
{
return Masked.withConstant( rra, 0 );
}

@Override
public void getSourceTransform( final int t, final int level, final AffineTransform3D transform )
{
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/bdv/tools/transformation/TransformedSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealRandomAccessible;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.mask.Masked;

/**
* A {@link Source} that wraps another {@link Source} and allows to decorate it
Expand Down Expand Up @@ -295,4 +296,16 @@ public Source< T > getWrappedSource()
{
return source;
}

@Override
public RandomAccessibleInterval< ? extends Masked< T > > getMaskedSource( final int t, final int level )
{
return source.getMaskedSource( t, level );
}

@Override
public RealRandomAccessible< ? extends Masked< T > > getInterpolatedMaskedSource( final int t, final int level, final Interpolation method )
{
return source.getInterpolatedMaskedSource( t, level, method );
}
}
Loading