diff --git a/pom.xml b/pom.xml index e7890aef8c13dfcb63b2c73fb297b3068209b44b..50f16a9020b6df3044be50d4dfe6283208ab4c4a 100644 --- a/pom.xml +++ b/pom.xml @@ -79,11 +79,15 @@ <artifactId>vecmath</artifactId> <version>1.3.1</version> </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>9.1.3.v20140225</version> + </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.2.4</version> - <scope>compile</scope> </dependency> </dependencies> diff --git a/src/main/java/bdv/AbstractViewerImgLoader.java b/src/main/java/bdv/AbstractViewerImgLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..fb1aad336b80a9cee1e5dac4462f3fe940aab85a --- /dev/null +++ b/src/main/java/bdv/AbstractViewerImgLoader.java @@ -0,0 +1,53 @@ +package bdv; + +import java.io.File; + +import mpicbg.spim.data.View; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.Volatile; +import net.imglib2.type.numeric.real.FloatType; + +import org.jdom2.Element; + +public abstract class AbstractViewerImgLoader< T, V extends Volatile< T > > implements ViewerImgLoader< T, V > +{ + protected final T type; + + protected final V volatileType; + + public AbstractViewerImgLoader( final T type, final V volatileType ) + { + this.type = type; + this.volatileType = volatileType; + } + + @Override + public T getImageType() + { + return type; + } + + @Override + public V getVolatileImageType() + { + return volatileType; + } + + @Override + public Element toXml( final File basePath ) + { + throw new UnsupportedOperationException( "not implemented" ); + } + + @Override + public RandomAccessibleInterval< FloatType > getFloatImage( final View view ) + { + throw new UnsupportedOperationException( "not implemented" ); + } + + @Override + public RandomAccessibleInterval< T > getImage( final View view ) + { + return getImage( view, 0 ); + } +} diff --git a/src/main/java/bdv/SpimSource.java b/src/main/java/bdv/SpimSource.java index f5c8e6c89b8c1d1511367f7cdb9428cb0f90d89b..70b34d0655c43308680cca8e9f692e06d4004e18 100644 --- a/src/main/java/bdv/SpimSource.java +++ b/src/main/java/bdv/SpimSource.java @@ -29,15 +29,9 @@ public class SpimSource< T extends NumericType< T > > extends AbstractSpimSource zero.setZero(); final View view = sequenceViews.getView( timepoint, setup ); final AffineTransform3D reg = view.getModel(); - final AffineTransform3D mipmapTransform = new AffineTransform3D(); for ( int level = 0; level < currentSources.length; level++ ) { - final double[] resolution = imgLoader.getMipmapResolutions( setup )[ level ]; - for ( int d = 0; d < 3; ++d ) - { - mipmapTransform.set( resolution[ d ], d, d ); - mipmapTransform.set( 0.5 * ( resolution[ d ] - 1 ), d, 3 ); - } + final AffineTransform3D mipmapTransform = imgLoader.getMipmapTransforms( setup )[ level ]; currentSourceTransforms[ level ].set( reg ); currentSourceTransforms[ level ].concatenate( mipmapTransform ); currentSources[ level ] = imgLoader.getImage( view, level ); diff --git a/src/main/java/bdv/ViewerImgLoader.java b/src/main/java/bdv/ViewerImgLoader.java index 61634d081d28c38d0804f1f79df40875fba84537..c3d24bd667d36e1337a0c81568b50cc1690d5d84 100644 --- a/src/main/java/bdv/ViewerImgLoader.java +++ b/src/main/java/bdv/ViewerImgLoader.java @@ -4,6 +4,8 @@ import mpicbg.spim.data.ImgLoader; import mpicbg.spim.data.View; import net.imglib2.RandomAccessibleInterval; import net.imglib2.Volatile; +import net.imglib2.realtransform.AffineTransform3D; +import bdv.img.cache.Cache; public interface ViewerImgLoader< T, V extends Volatile< T > > extends ImgLoader< T > { @@ -17,5 +19,9 @@ public interface ViewerImgLoader< T, V extends Volatile< T > > extends ImgLoader public double[][] getMipmapResolutions( final int setup ); + public AffineTransform3D[] getMipmapTransforms( final int setup ); + public int numMipmapLevels( final int setup ); + + public Cache getCache(); } diff --git a/src/main/java/bdv/VolatileSpimSource.java b/src/main/java/bdv/VolatileSpimSource.java index 696847f75c71444a76fcc07dfdea0853ed3af877..d2dfb10d279230c222817119966157e6332ee331 100644 --- a/src/main/java/bdv/VolatileSpimSource.java +++ b/src/main/java/bdv/VolatileSpimSource.java @@ -34,15 +34,9 @@ public class VolatileSpimSource< T extends NumericType< T >, V extends Volatile< zero.setZero(); final View view = sequenceViews.getView( timepoint, setup ); final AffineTransform3D reg = view.getModel(); - final AffineTransform3D mipmapTransform = new AffineTransform3D(); for ( int level = 0; level < currentSources.length; level++ ) { - final double[] resolution = imgLoader.getMipmapResolutions( setup )[ level ]; - for ( int d = 0; d < 3; ++d ) - { - mipmapTransform.set( resolution[ d ], d, d ); - mipmapTransform.set( 0.5 * ( resolution[ d ] - 1 ), d, 3 ); - } + final AffineTransform3D mipmapTransform = imgLoader.getMipmapTransforms( setup )[ level ]; currentSourceTransforms[ level ].set( reg ); currentSourceTransforms[ level ].concatenate( mipmapTransform ); currentSources[ level ] = imgLoader.getVolatileImage( view, level ); diff --git a/src/main/java/bdv/img/cache/VolatileGlobalCellCache.java b/src/main/java/bdv/img/cache/VolatileGlobalCellCache.java index 08a7c956c4e2b3096006b21006f505ef4e208be1..6d509aaf99e13d1b10d6e54a6f209247dd3b0fa9 100644 --- a/src/main/java/bdv/img/cache/VolatileGlobalCellCache.java +++ b/src/main/java/bdv/img/cache/VolatileGlobalCellCache.java @@ -217,6 +217,7 @@ public class VolatileGlobalCellCache< A extends VolatileAccess > implements Cach { final Fetcher f = new Fetcher(); f.setDaemon( true ); + f.setName( "Fetcher-" + i ); fetchers.add( f ); f.start(); } diff --git a/src/main/java/bdv/img/catmaid/CatmaidImageLoader.java b/src/main/java/bdv/img/catmaid/CatmaidImageLoader.java index 4467b86d63c564e701bee3bceace96ec6a54a0b8..f7b839f0e384667fa62859ae40f160e3d3ccde28 100644 --- a/src/main/java/bdv/img/catmaid/CatmaidImageLoader.java +++ b/src/main/java/bdv/img/catmaid/CatmaidImageLoader.java @@ -9,20 +9,18 @@ import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray; import net.imglib2.img.cell.CellImg; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.ARGBType; -import net.imglib2.type.numeric.real.FloatType; import net.imglib2.type.volatiles.VolatileARGBType; import org.jdom2.Element; -import bdv.ViewerImgLoader; -import bdv.img.cache.Cache; +import bdv.AbstractViewerImgLoader; import bdv.img.cache.VolatileCell; import bdv.img.cache.VolatileGlobalCellCache; import bdv.img.cache.VolatileGlobalCellCache.LoadingStrategy; import bdv.img.cache.VolatileImgCells; import bdv.img.cache.VolatileImgCells.CellCache; -public class CatmaidImageLoader implements ViewerImgLoader< ARGBType, VolatileARGBType > +public class CatmaidImageLoader extends AbstractViewerImgLoader< ARGBType, VolatileARGBType > { private long width; @@ -50,6 +48,11 @@ public class CatmaidImageLoader implements ViewerImgLoader< ARGBType, VolatileAR protected VolatileGlobalCellCache< VolatileIntArray > cache; + public CatmaidImageLoader() + { + super( new ARGBType(), new VolatileARGBType() ); + } + @Override public void init( final Element elem, final File basePath ) { @@ -61,7 +64,7 @@ public class CatmaidImageLoader implements ViewerImgLoader< ARGBType, VolatileAR resZ = Double.parseDouble( elem.getChildText( "resZ" ) ); urlFormat = elem.getChildText( "urlFormat" ); - + tileWidth = Integer.parseInt( elem.getChildText( "tileWidth" ) ); tileHeight = Integer.parseInt( elem.getChildText( "tileHeight" ) ); @@ -76,7 +79,6 @@ public class CatmaidImageLoader implements ViewerImgLoader< ARGBType, VolatileAR blockDimensions = new int[ numScales ][]; for ( int l = 0; l < numScales; ++l ) { - mipmapResolutions[ l ] = new double[] { 1 << l, 1 << l, 1 }; imageDimensions[ l ] = new long[] { width >> l, height >> l, depth }; blockDimensions[ l ] = new int[] { tileWidth, tileHeight, 1 }; @@ -97,24 +99,6 @@ public class CatmaidImageLoader implements ViewerImgLoader< ARGBType, VolatileAR return i; } - @Override - public Element toXml( final File basePath ) - { - throw new UnsupportedOperationException( "not implemented" ); - } - - @Override - public RandomAccessibleInterval< FloatType > getFloatImage( final View view ) - { - throw new UnsupportedOperationException( "not implemented" ); - } - - @Override - public RandomAccessibleInterval< ARGBType > getImage( final View view ) - { - return getImage( view, 0 ); - } - @Override public RandomAccessibleInterval< ARGBType > getImage( final View view, final int level ) { @@ -161,25 +145,9 @@ public class CatmaidImageLoader implements ViewerImgLoader< ARGBType, VolatileAR return img; } - public Cache getCache() - { - return cache; - } - - private final ARGBType type = new ARGBType(); - - private final VolatileARGBType volatileType = new VolatileARGBType(); - - @Override - public ARGBType getImageType() - { - return type; - } - @Override - public VolatileARGBType getVolatileImageType() + public VolatileGlobalCellCache< VolatileIntArray > getCache() { - return volatileType; + return cache; } - } diff --git a/src/main/java/bdv/img/catmaid/CatmaidVolatileIntArrayLoader.java b/src/main/java/bdv/img/catmaid/CatmaidVolatileIntArrayLoader.java index c9dba0152173148ef56b7141486c0b71eda478d2..762693a9db71ccad96e39508fdb3d3c29ac4608d 100644 --- a/src/main/java/bdv/img/catmaid/CatmaidVolatileIntArrayLoader.java +++ b/src/main/java/bdv/img/catmaid/CatmaidVolatileIntArrayLoader.java @@ -61,7 +61,7 @@ public class CatmaidVolatileIntArrayLoader implements CacheArrayLoader< Volatile @Override public int getBytesPerElement() { - return 1; + return 4; } @Override diff --git a/src/main/java/bdv/img/catmaid/CatmaidVolatileShortArrayLoader.java b/src/main/java/bdv/img/catmaid/CatmaidVolatileShortArrayLoader.java index 483f45b925053128ef76f7525a6799e61c356ebf..e6d1d6141093b18830c5cb79c5fc64760227c768 100644 --- a/src/main/java/bdv/img/catmaid/CatmaidVolatileShortArrayLoader.java +++ b/src/main/java/bdv/img/catmaid/CatmaidVolatileShortArrayLoader.java @@ -31,7 +31,7 @@ public class CatmaidVolatileShortArrayLoader implements CacheArrayLoader< Volati @Override public int getBytesPerElement() { - return 1; + return 2; } @Override diff --git a/src/main/java/bdv/img/hdf5/Hdf5ImageLoader.java b/src/main/java/bdv/img/hdf5/Hdf5ImageLoader.java index 1b6e87a1390a00635aa649d27631159ff55547d2..526891d2fc8af7b4694f6886620071d11d7161f0 100644 --- a/src/main/java/bdv/img/hdf5/Hdf5ImageLoader.java +++ b/src/main/java/bdv/img/hdf5/Hdf5ImageLoader.java @@ -15,16 +15,16 @@ import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.NativeImg; import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray; import net.imglib2.img.cell.CellImg; +import net.imglib2.realtransform.AffineTransform3D; import net.imglib2.sampler.special.ConstantRandomAccessible; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.integer.UnsignedShortType; -import net.imglib2.type.numeric.real.FloatType; import net.imglib2.type.volatiles.VolatileUnsignedShortType; import net.imglib2.view.Views; import org.jdom2.Element; -import bdv.ViewerImgLoader; +import bdv.AbstractViewerImgLoader; import bdv.img.cache.VolatileCell; import bdv.img.cache.VolatileGlobalCellCache; import bdv.img.cache.VolatileGlobalCellCache.LoadingStrategy; @@ -34,7 +34,7 @@ import ch.systemsx.cisd.hdf5.HDF5DataSetInformation; import ch.systemsx.cisd.hdf5.HDF5Factory; import ch.systemsx.cisd.hdf5.IHDF5Reader; -public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, VolatileUnsignedShortType > +public class Hdf5ImageLoader extends AbstractViewerImgLoader< UnsignedShortType, VolatileUnsignedShortType > { protected File hdf5File; @@ -44,6 +44,8 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola protected final ArrayList< double[][] > perSetupMipmapResolutions; + protected final ArrayList< AffineTransform3D[] > perSetupMipmapTransforms; + protected final ArrayList< int[][] > perSetupSubdivisions; /** @@ -77,8 +79,6 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola */ protected Boolean[] cachedExistence; - protected final boolean isCoarsestLevelBlocking = true; - public Hdf5ImageLoader() { this( null ); @@ -86,10 +86,12 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola public Hdf5ImageLoader( final ArrayList< Partition > hdf5Partitions ) { + super( new UnsignedShortType(), new VolatileUnsignedShortType() ); hdf5File = null; hdf5Reader = null; cache = null; perSetupMipmapResolutions = new ArrayList< double[][] >(); + perSetupMipmapTransforms = new ArrayList< AffineTransform3D[] >(); perSetupSubdivisions = new ArrayList< int[][] >(); partitions = new ArrayList< Partition >(); if ( hdf5Partitions != null ) @@ -106,7 +108,9 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola public Hdf5ImageLoader( final File hdf5File, final ArrayList< Partition > hdf5Partitions, final boolean doOpen ) { + super( new UnsignedShortType(), new VolatileUnsignedShortType() ); this.hdf5File = hdf5File; + perSetupMipmapTransforms = new ArrayList< AffineTransform3D[] >(); perSetupMipmapResolutions = new ArrayList< double[][] >(); perSetupSubdivisions = new ArrayList< int[][] >(); partitions = new ArrayList< Partition >(); @@ -128,13 +132,27 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola perSetupSubdivisions.clear(); for ( int setup = 0; setup < numSetups; ++setup ) { - final double [][] mipmapResolutions = hdf5Reader.readDoubleMatrix( getResolutionsPath( setup ) ); + final double[][] mipmapResolutions = hdf5Reader.readDoubleMatrix( getResolutionsPath( setup ) ); perSetupMipmapResolutions.add( mipmapResolutions ); if ( mipmapResolutions.length > maxNumLevels ) maxNumLevels = mipmapResolutions.length; maxLevels[ setup ] = mipmapResolutions.length - 1; - final int [][] subdivisions = hdf5Reader.readIntMatrix( getSubdivisionsPath( setup ) ); + final AffineTransform3D[] mipmapTransforms = new AffineTransform3D[ mipmapResolutions.length ]; + for ( int level = 0; level < mipmapResolutions.length; level++ ) + { + final AffineTransform3D mipmapTransform = new AffineTransform3D(); + final double[] resolution = mipmapResolutions[ level ]; + for ( int d = 0; d < 3; ++d ) + { + mipmapTransform.set( resolution[ d ], d, d ); + mipmapTransform.set( 0.5 * ( resolution[ d ] - 1 ), d, 3 ); + } + mipmapTransforms[ level ] = mipmapTransform; + } + perSetupMipmapTransforms.add( mipmapTransforms ); + + final int[][] subdivisions = hdf5Reader.readIntMatrix( getSubdivisionsPath( setup ) ); perSetupSubdivisions.add( subdivisions ); } @@ -221,18 +239,6 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola return partitions; } - @Override - public RandomAccessibleInterval< FloatType > getFloatImage( final View view ) - { - throw new UnsupportedOperationException( "currently not used" ); - } - - @Override - public RandomAccessibleInterval< UnsignedShortType > getImage( final View view ) - { - return getImage( view, 0 ); - } - @Override public RandomAccessibleInterval< UnsignedShortType > getImage( final View view, final int level ) { @@ -261,6 +267,7 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola return img; } + @Override public VolatileGlobalCellCache< VolatileShortArray > getCache() { return cache; @@ -272,6 +279,12 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola return perSetupMipmapResolutions.get( setup ); } + @Override + public AffineTransform3D[] getMipmapTransforms( final int setup ) + { + return perSetupMipmapTransforms.get( setup ); + } + public int[][] getSubdivisions( final int setup ) { return perSetupSubdivisions.get( setup ); @@ -289,10 +302,21 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola * * @return true, if the given image data is present. */ - protected boolean existsImageData( final View view, final int level ) + public boolean existsImageData( final View view, final int level ) { final int timepoint = view.getTimepointIndex(); final int setup = view.getSetupIndex(); + return existsImageData( timepoint, setup, level ); + } + + /** + * Checks whether the given image data is present in the hdf5. Missing data + * may be caused by missing partition files + * + * @return true, if the given image data is present. + */ + public boolean existsImageData( final int timepoint, final int setup, final int level ) + { final int index = getViewInfoCacheIndex( timepoint, setup, level ); if ( cachedExistence[ index ] == null ) // will set cachedExistence[ index ] as a side effect @@ -316,7 +340,7 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola return Views.interval( new ConstantRandomAccessible< T >( constant, 3 ), new FinalInterval( d ) ); } - protected long[] getImageDimension( final int timepoint, final int setup, final int level ) + public long[] getImageDimension( final int timepoint, final int setup, final int level ) { final int index = getViewInfoCacheIndex( timepoint, setup, level ); if ( cachedDimensions[ index ] == null ) @@ -392,20 +416,4 @@ public class Hdf5ImageLoader implements ViewerImgLoader< UnsignedShortType, Vola } } } - - private final UnsignedShortType type = new UnsignedShortType(); - - private final VolatileUnsignedShortType volatileType = new VolatileUnsignedShortType(); - - @Override - public UnsignedShortType getImageType() - { - return type; - } - - @Override - public VolatileUnsignedShortType getVolatileImageType() - { - return volatileType; - } } diff --git a/src/main/java/bdv/img/remote/RemoteImageLoader.java b/src/main/java/bdv/img/remote/RemoteImageLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..c6d937387e3911be48c38ff2b2fcc87c5f21ed89 --- /dev/null +++ b/src/main/java/bdv/img/remote/RemoteImageLoader.java @@ -0,0 +1,238 @@ +package bdv.img.remote; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; + +import mpicbg.spim.data.View; +import net.imglib2.FinalInterval; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.NativeImg; +import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray; +import net.imglib2.img.cell.CellImg; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.sampler.special.ConstantRandomAccessible; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.integer.UnsignedShortType; +import net.imglib2.type.volatiles.VolatileUnsignedShortType; +import net.imglib2.util.IntervalIndexer; +import net.imglib2.view.Views; + +import org.jdom2.Element; + +import bdv.AbstractViewerImgLoader; +import bdv.img.cache.VolatileCell; +import bdv.img.cache.VolatileGlobalCellCache; +import bdv.img.cache.VolatileGlobalCellCache.LoadingStrategy; +import bdv.img.cache.VolatileImgCells; +import bdv.img.cache.VolatileImgCells.CellCache; + +import com.google.gson.Gson; + +public class RemoteImageLoader extends AbstractViewerImgLoader< UnsignedShortType, VolatileUnsignedShortType > +{ + protected String baseUrl; + + protected RemoteImageLoaderMetaData metadata; + + protected final ArrayList< AffineTransform3D[] > perSetupMipmapTransforms; + + protected int[][] cellsDimensions; + + protected VolatileGlobalCellCache< VolatileShortArray > cache; + + public RemoteImageLoader() + { + super( new UnsignedShortType(), new VolatileUnsignedShortType() ); + perSetupMipmapTransforms = new ArrayList< AffineTransform3D[] >(); + } + + private void open() throws IOException + { + final URL url = new URL( baseUrl + "?p=init" ); + metadata = new Gson().fromJson( + new InputStreamReader( url.openStream() ), + RemoteImageLoaderMetaData.class ); + cache = new VolatileGlobalCellCache< VolatileShortArray >( + new RemoteVolatileShortArrayLoader( this ), + metadata.numTimepoints, + metadata.numSetups, + metadata.maxNumLevels, + metadata.maxLevels, + 10 ); + for ( int setup = 0; setup < metadata.numSetups; ++setup ) + { + final double[][] mipmapResolutions = metadata.perSetupMipmapResolutions.get( setup ); + final AffineTransform3D[] mipmapTransforms = new AffineTransform3D[ mipmapResolutions.length ]; + for ( int level = 0; level < mipmapResolutions.length; level++ ) + { + final AffineTransform3D mipmapTransform = new AffineTransform3D(); + final double[] resolution = mipmapResolutions[ level ]; + for ( int d = 0; d < 3; ++d ) + { + mipmapTransform.set( resolution[ d ], d, d ); + mipmapTransform.set( 0.5 * ( resolution[ d ] - 1 ), d, 3 ); + } + mipmapTransforms[ level ] = mipmapTransform; + } + perSetupMipmapTransforms.add( mipmapTransforms ); + } + cellsDimensions = metadata.createCellsDimensions(); + } + + @Override + public void init( final Element elem, final File basePath ) + { + try + { + baseUrl = elem.getChildText( "baseUrl" ); + open(); + } + catch ( final Exception e ) + { + throw new RuntimeException( e ); + } + } + + @Override + public RandomAccessibleInterval< UnsignedShortType > getImage( final View view, final int level ) + { + if ( ! existsImageData( view, level ) ) + { + System.err.println( "image data for " + view.getBasename() + " level " + level + " could not be found. Partition file missing?" ); + return getMissingDataImage( view, level, new UnsignedShortType() ); + } + final CellImg< UnsignedShortType, VolatileShortArray, VolatileCell< VolatileShortArray > > img = prepareCachedImage( view, level, LoadingStrategy.BLOCKING ); + final UnsignedShortType linkedType = new UnsignedShortType( img ); + img.setLinkedType( linkedType ); + return img; + } + + @Override + public RandomAccessibleInterval< VolatileUnsignedShortType > getVolatileImage( final View view, final int level ) + { + if ( ! existsImageData( view, level ) ) + { + System.err.println( "image data for " + view.getBasename() + " level " + level + " could not be found." ); + return getMissingDataImage( view, level, new VolatileUnsignedShortType() ); + } + final CellImg< VolatileUnsignedShortType, VolatileShortArray, VolatileCell< VolatileShortArray > > img = prepareCachedImage( view, level, LoadingStrategy.BUDGETED ); + final VolatileUnsignedShortType linkedType = new VolatileUnsignedShortType( img ); + img.setLinkedType( linkedType ); + return img; + } + + @Override + public VolatileGlobalCellCache< VolatileShortArray > getCache() + { + return cache; + } + + @Override + public double[][] getMipmapResolutions( final int setup ) + { + return metadata.perSetupMipmapResolutions.get( setup ); + } + + @Override + public AffineTransform3D[] getMipmapTransforms( final int setup ) + { + return perSetupMipmapTransforms.get( setup ); + } + + public int[][] getSubdivisions( final int setup ) + { + return metadata.perSetupSubdivisions.get( setup ); + } + + @Override + public int numMipmapLevels( final int setup ) + { + return getMipmapResolutions( setup ).length; + } + + /** + * Checks whether the given image data is present on the server. + * + * @return true, if the given image data is present. + */ + public boolean existsImageData( final View view, final int level ) + { + final int timepoint = view.getTimepointIndex(); + final int setup = view.getSetupIndex(); + return existsImageData( timepoint, setup, level ); + } + + /** + * Checks whether the given image data is present on the server. + * + * @return true, if the given image data is present. + */ + public boolean existsImageData( final int timepoint, final int setup, final int level ) + { + final int index = getViewInfoCacheIndex( timepoint, setup, level ); + return metadata.existence[ index ]; + } + + /** + * For images that are missing on the server, a constant image is created. + * If the dimension of the missing image is present in + * {@link RemoteImageLoaderMetaData#dimensions} then use that. Otherwise + * create a 1x1x1 image. + */ + protected < T > RandomAccessibleInterval< T > getMissingDataImage( final View view, final int level, final T constant ) + { + final int t = view.getTimepointIndex(); + final int s = view.getSetupIndex(); + final int index = getViewInfoCacheIndex( t, s, level ); + long[] d = metadata.dimensions[ index ]; + if ( d == null ) + d = new long[] { 1, 1, 1 }; + return Views.interval( new ConstantRandomAccessible< T >( constant, 3 ), new FinalInterval( d ) ); + } + + public long[] getImageDimension( final int timepoint, final int setup, final int level ) + { + final int index = getViewInfoCacheIndex( timepoint, setup, level ); + return metadata.dimensions[ index ]; + } + + private int getViewInfoCacheIndex( final int timepoint, final int setup, final int level ) + { + return level + metadata.maxNumLevels * ( setup + metadata.numSetups * timepoint ); + } + + int getCellIndex( final int timepoint, final int setup, final int level, final long[] globalPosition ) + { + final int index = getViewInfoCacheIndex( timepoint, setup, level ); + final int[] cellSize = getSubdivisions( setup )[ level ]; + final int[] cellPos = new int[] { + ( int ) globalPosition[ 0 ] / cellSize[ 0 ], + ( int ) globalPosition[ 1 ] / cellSize[ 1 ], + ( int ) globalPosition[ 2 ] / cellSize[ 2 ] }; + return IntervalIndexer.positionToIndex( cellPos, cellsDimensions[ index ] ); + } + + /** + * (Almost) create a {@link CellImg} backed by the cache. + * The created image needs a {@link NativeImg#setLinkedType(net.imglib2.type.Type) linked type} before it can be used. + * The type should be either {@link UnsignedShortType} and {@link VolatileUnsignedShortType}. + */ + protected < T extends NativeType< T > > CellImg< T, VolatileShortArray, VolatileCell< VolatileShortArray > > prepareCachedImage( final View view, final int level, final LoadingStrategy loadingStrategy ) + { + if ( cache == null ) + throw new RuntimeException( "no connection open" ); + + final int timepoint = view.getTimepointIndex(); + final int setup = view.getSetupIndex(); + final long[] dimensions = getImageDimension( timepoint, setup, level ); + final int[] cellDimensions = metadata.perSetupSubdivisions.get( setup )[ level ]; + + final CellCache< VolatileShortArray > c = cache.new VolatileCellCache( timepoint, setup, level, loadingStrategy ); + final VolatileImgCells< VolatileShortArray > cells = new VolatileImgCells< VolatileShortArray >( c, 1, dimensions, cellDimensions ); + final CellImg< T, VolatileShortArray, VolatileCell< VolatileShortArray > > img = new CellImg< T, VolatileShortArray, VolatileCell< VolatileShortArray > >( null, cells ); + return img; + } +} diff --git a/src/main/java/bdv/img/remote/RemoteImageLoaderMetaData.java b/src/main/java/bdv/img/remote/RemoteImageLoaderMetaData.java new file mode 100644 index 0000000000000000000000000000000000000000..4283ecf3ed92e6bcbe6a5de21b6fb41708bc506b --- /dev/null +++ b/src/main/java/bdv/img/remote/RemoteImageLoaderMetaData.java @@ -0,0 +1,97 @@ +package bdv.img.remote; + +import java.util.ArrayList; + +import bdv.img.hdf5.Hdf5ImageLoader; + +public class RemoteImageLoaderMetaData +{ + protected final ArrayList< double[][] > perSetupMipmapResolutions; + + protected final ArrayList< int[][] > perSetupSubdivisions; + + protected int numTimepoints; + + protected int numSetups; + + protected int[] maxLevels; + + protected int maxNumLevels; + + /** + * An array of long[] arrays with {@link #numTimepoints} * + * {@link #numSetups} * {@link #maxNumLevels} entries. Every entry is the + * dimensions of one image (identified by flattened index of level, setup, + * and timepoint). + */ + protected long[][] dimensions; + + /** + * An array of Booleans with {@link #numTimepoints} * {@link #numSetups} * + * {@link #maxNumLevels} entries. Every entry is the existence of one image + * (identified by flattened index of level, setup, and timepoint). + */ + protected boolean[] existence; + + public RemoteImageLoaderMetaData() + { + perSetupMipmapResolutions = new ArrayList< double[][] >(); + perSetupSubdivisions = new ArrayList< int[][] >(); + } + + public RemoteImageLoaderMetaData( final Hdf5ImageLoader imgLoader, final int numTimepoints, final int numSetups ) + { + perSetupMipmapResolutions = new ArrayList< double[][] >(); + perSetupSubdivisions = new ArrayList< int[][] >(); + this.numTimepoints = numTimepoints; + this.numSetups = numSetups; + maxLevels = new int[ numSetups ]; + maxNumLevels = 0; + for ( int setup = 0; setup < numSetups; ++setup ) + { + perSetupMipmapResolutions.add( imgLoader.getMipmapResolutions( setup ) ); + perSetupSubdivisions.add( imgLoader.getSubdivisions( setup ) ); + final int numLevels = imgLoader.numMipmapLevels( setup ); + maxLevels[ setup ] = numLevels - 1; + if ( numLevels > maxNumLevels ) + maxNumLevels = numLevels; + } + final int numImages = numTimepoints * numSetups * maxNumLevels; + dimensions = new long[ numImages ][]; + existence = new boolean[ numImages ]; + + for ( int t = 0; t < numTimepoints; ++t ) + for ( int s = 0; s < numSetups; ++s ) + for ( int l = 0; l <= maxLevels[ s ]; ++l ) + { + final int i = l + maxNumLevels * ( s + numSetups * t ); + existence[ i ] = imgLoader.existsImageData( t, s, l ); + dimensions[ i ] = imgLoader.getImageDimension( t, s, l ); + } + } + + /** + * Create an array of int[] with {@link #numTimepoints} * {@link #numSetups} + * * {@link #maxNumLevels} entries. Every entry is the dimensions in cells + * (instead of pixels) of one image (identified by flattened index of level, + * setup, and timepoint). + */ + protected int[][] createCellsDimensions() + { + final int numImages = numTimepoints * numSetups * maxNumLevels; + final int[][] cellsDimensions = new int[ numImages ][]; + for ( int t = 0; t < numTimepoints; ++t ) + for ( int s = 0; s < numSetups; ++s ) + for ( int l = 0; l <= maxLevels[ s ]; ++l ) + { + final int i = l + maxNumLevels * ( s + numSetups * t ); + final long[] imageDimensions = dimensions[ i ]; + final int[] cellSize = perSetupSubdivisions.get( s )[ l ]; + cellsDimensions[ i ] = new int[] { + ( int ) imageDimensions[ 0 ] / cellSize[ 0 ], + ( int ) imageDimensions[ 1 ] / cellSize[ 1 ], + ( int ) imageDimensions[ 2 ] / cellSize[ 2 ] }; + } + return cellsDimensions; + } +} diff --git a/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..6db202a2bae109f96471e0a140995930257f52a9 --- /dev/null +++ b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java @@ -0,0 +1,76 @@ +package bdv.img.remote; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray; +import bdv.img.cache.CacheArrayLoader; + +public class RemoteVolatileShortArrayLoader implements CacheArrayLoader< VolatileShortArray > +{ + private VolatileShortArray theEmptyArray; + + private final RemoteImageLoader imgLoader; + + public RemoteVolatileShortArrayLoader( final RemoteImageLoader imgLoader ) + { + theEmptyArray = new VolatileShortArray( 32 * 32 * 32, false ); + this.imgLoader = imgLoader; + } + + @Override + public VolatileShortArray loadArray( final int timepoint, final int setup, final int level, final int[] dimensions, final long[] min ) throws InterruptedException + { + final int index = imgLoader.getCellIndex( timepoint, setup, level, min ); + final short[] data = new short[ dimensions[ 0 ] * dimensions[ 1 ] * dimensions[ 2 ] ]; + try + { + final URL url = new URL( String.format( "%s?p=cell/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d", + imgLoader.baseUrl, + index, + timepoint, + setup, + level, + dimensions[ 0 ], + dimensions[ 1 ], + dimensions[ 2 ], + min[ 0 ], + min[ 1 ], + min[ 2 ] ) ); + final InputStream s = url.openStream(); + final byte[] buf = new byte[ data.length * 2 ]; + for ( int i = 0, l = s.read( buf, 0, buf.length ); l != -1; i += l, l = s.read( buf, i, buf.length - i ) ); + for ( int i = 0, j = 0; i < data.length; ++i, j += 2 ) + data[ i ] = ( short ) ( ( ( buf[ j ] & 0xff ) << 8 ) | ( buf[ j + 1 ] & 0xff ) ); + s.close(); + } + catch ( final MalformedURLException e ) + { + e.printStackTrace(); + } + catch ( final IOException e ) + { + e.printStackTrace(); + } + return new VolatileShortArray( data, true ); + } + + @Override + public VolatileShortArray emptyArray( final int[] dimensions ) + { + int numEntities = 1; + for ( int i = 0; i < dimensions.length; ++i ) + numEntities *= dimensions[ i ]; + if ( theEmptyArray.getCurrentStorageArray().length < numEntities ) + theEmptyArray = new VolatileShortArray( numEntities, false ); + return theEmptyArray; + } + + @Override + public int getBytesPerElement() { + return 2; + } + +} diff --git a/src/main/java/bdv/server/BigDataServer.java b/src/main/java/bdv/server/BigDataServer.java new file mode 100644 index 0000000000000000000000000000000000000000..3356227d6978879ce9560a6ae4ae7f85fe717a42 --- /dev/null +++ b/src/main/java/bdv/server/BigDataServer.java @@ -0,0 +1,112 @@ +package bdv.server; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import mpicbg.spim.data.SequenceDescription; +import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.jdom2.JDOMException; + +import bdv.SequenceViewsLoader; +import bdv.img.cache.VolatileCell; +import bdv.img.cache.VolatileGlobalCellCache; +import bdv.img.cache.VolatileGlobalCellCache.LoadingStrategy; +import bdv.img.hdf5.Hdf5ImageLoader; +import bdv.img.remote.RemoteImageLoaderMetaData; + +import com.google.gson.Gson; + +public class BigDataServer +{ + public static void main( final String[] args ) throws Exception + { + final String fn = args.length > 0 ? args[ 0 ] : "/Users/pietzsch/Desktop/data/fibsem.xml"; + final Server server = new Server( 8080 ); + server.setHandler( new CellHandler( fn ) ); + server.start(); + server.join(); + } + + static class CellHandler extends AbstractHandler + { + private final VolatileGlobalCellCache< VolatileShortArray > cache; + + private final String metadataJson; + + private final RemoteImageLoaderMetaData metadata; + + public CellHandler( final String xmlFilename ) throws JDOMException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException + { + final SequenceViewsLoader loader = new SequenceViewsLoader( xmlFilename ); + final SequenceDescription seq = loader.getSequenceDescription(); + final Hdf5ImageLoader imgLoader = ( Hdf5ImageLoader ) seq.imgLoader; + cache = imgLoader.getCache(); + metadata = new RemoteImageLoaderMetaData( imgLoader, seq.numTimepoints(), seq.numViewSetups() ); + metadataJson = new Gson().toJson( metadata ); + } + + @Override + public void handle( final String target, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response ) throws IOException, ServletException + { + final String cellString = request.getParameter( "p" ); + if ( cellString == null ) + return; + final String[] parts = cellString.split( "/" ); + if ( parts[ 0 ].equals( "cell" ) ) + { + final int index = Integer.parseInt( parts[ 1 ] ); + final int timepoint = Integer.parseInt( parts[ 2 ] ); + final int setup = Integer.parseInt( parts[ 3 ] ); + final int level = Integer.parseInt( parts[ 4 ] ); + VolatileCell< VolatileShortArray > cell = cache.getGlobalIfCached( timepoint, setup, level, index, LoadingStrategy.BLOCKING ); + if ( cell == null ) + { + final int[] cellDims = new int[] { + Integer.parseInt( parts[ 5 ] ), + Integer.parseInt( parts[ 6 ] ), + Integer.parseInt( parts[ 7 ] ) }; + final long[] cellMin = new long[] { + Long.parseLong( parts[ 8 ] ), + Long.parseLong( parts[ 9 ] ), + Long.parseLong( parts[ 10 ] ) }; + cell = cache.createGlobal( cellDims, cellMin, timepoint, setup, level, index, LoadingStrategy.BLOCKING ); + } + + final short[] data = cell.getData().getCurrentStorageArray(); + final byte[] buf = new byte[ 2 * data.length ]; + for ( int i = 0, j = 0; i < data.length; i++ ) + { + final short s = data[ i ]; + buf[ j++ ] = ( byte ) ( ( s >> 8 ) & 0xff ); + buf[ j++ ] = ( byte ) ( s & 0xff ); + } + + response.setContentType( "application/octet-stream" ); + response.setContentLength( buf.length ); + response.setStatus( HttpServletResponse.SC_OK ); + baseRequest.setHandled( true ); + final OutputStream os = response.getOutputStream(); + os.write( buf ); + os.close(); + } + else if ( parts[ 0 ].equals( "init" ) ) + { + response.setContentType( "application/octet-stream" ); + response.setStatus( HttpServletResponse.SC_OK ); + baseRequest.setHandled( true ); + final PrintWriter ow = response.getWriter(); + ow.write( metadataJson ); + ow.close(); + } + } + } +} diff --git a/src/main/java/bdv/viewer/render/AccumulateProjector.java b/src/main/java/bdv/viewer/render/AccumulateProjector.java index 980125cae767e75fc1926aa6bf60bfa83a0db365..c184991cc236c8dd4cac58a34946f1bf92ff012f 100644 --- a/src/main/java/bdv/viewer/render/AccumulateProjector.java +++ b/src/main/java/bdv/viewer/render/AccumulateProjector.java @@ -1,9 +1,9 @@ package bdv.viewer.render; import java.util.ArrayList; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import net.imglib2.Cursor; @@ -43,6 +43,8 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector */ protected final int numThreads; + protected final ExecutorService executorService; + /** * Time needed for rendering the last frame, in nano-seconds. */ @@ -57,7 +59,8 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector final ArrayList< ? extends RandomAccessible< A > > sources, final Converter< ? super A, B > converter, final RandomAccessibleInterval< B > target, - final int numThreads ) + final int numThreads, + final ExecutorService executorService ) { this.sourceProjectors = sourceProjectors; this.sources = new ArrayList< IterableInterval< A > >(); @@ -67,6 +70,7 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector this.target = target; this.iterableTarget = Views.flatIterable( target ); this.numThreads = numThreads; + this.executorService = executorService; lastFrameRenderNanoTime = -1; } @@ -96,23 +100,25 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector final int height = ( int ) target.dimension( 1 ); final int length = width * height; - final ExecutorService ex = Executors.newFixedThreadPool( numThreads ); - final int numTasks = Math.max( numThreads * 10, height ); + final boolean createExecutor = ( executorService == null ); + final ExecutorService ex = createExecutor ? Executors.newFixedThreadPool( numThreads ) : executorService; + final int numTasks = Math.min( numThreads * 10, height ); final double taskLength = ( double ) length / numTasks; final int numSources = sources.size(); + final ArrayList< Callable< Void > > tasks = new ArrayList< Callable< Void > >( numTasks ); for ( int taskNum = 0; taskNum < numTasks; ++taskNum ) { final int myOffset = ( int ) ( taskNum * taskLength ); final int myLength = ( (taskNum == numTasks - 1 ) ? length : ( int ) ( ( taskNum + 1 ) * taskLength ) ) - myOffset; - final Runnable r = new Runnable() + final Callable< Void > r = new Callable< Void >() { @SuppressWarnings( "unchecked" ) @Override - public void run() + public Void call() { if ( interrupted.get() ) - return; + return null; final Cursor< A >[] sourceCursors = new Cursor[ numSources ]; for ( int s = 0; s < numSources; ++s ) @@ -130,19 +136,21 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector sourceCursors[ s ].fwd(); accumulate( sourceCursors, targetCursor.next() ); } + return null; } }; - ex.execute( r ); + tasks.add( r ); } - ex.shutdown(); try { - ex.awaitTermination( 1, TimeUnit.HOURS ); + ex.invokeAll( tasks ); } catch ( final InterruptedException e ) { e.printStackTrace(); } + if ( createExecutor ) + ex.shutdown(); lastFrameRenderNanoTime = stopWatch.nanoTime(); diff --git a/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java b/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java index 481a9c4189619def08de1b2ab290a385a15db7a2..4f54315de4ae4160822f8568dad5bee1d866b0f9 100644 --- a/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java +++ b/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java @@ -1,6 +1,7 @@ package bdv.viewer.render; import java.util.ArrayList; +import java.util.concurrent.ExecutorService; import net.imglib2.Cursor; import net.imglib2.RandomAccessible; @@ -14,9 +15,10 @@ public class AccumulateProjectorARGB extends AccumulateProjector< ARGBType, ARGB final ArrayList< VolatileProjector > sourceProjectors, final ArrayList< ? extends RandomAccessible< ARGBType > > sources, final RandomAccessibleInterval< ARGBType > target, - final int numThreads ) + final int numThreads, + final ExecutorService executorService ) { - super( sourceProjectors, sources, null, target, numThreads ); + super( sourceProjectors, sources, null, target, numThreads, executorService ); } @Override diff --git a/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java b/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java index b9ca971cd43d7ab3f69e62e1787c0f23e4d09a3f..e794a060b02b0cb7f7099aa9843ec723806f6c24 100644 --- a/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java +++ b/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java @@ -571,7 +571,7 @@ public class MultiResolutionRenderer sourceProjectors.add( p ); sourceImages.add( renderImage ); } - projector = new AccumulateProjectorARGB( sourceProjectors, sourceImages, screenImage, numRenderingThreads ); + projector = new AccumulateProjectorARGB( sourceProjectors, sourceImages, screenImage, numRenderingThreads, renderingExecutorService ); } previousTimepoint = viewerState.getCurrentTimepoint(); cache.initIoTimeBudget( iobudget ); @@ -680,7 +680,7 @@ public class MultiResolutionRenderer } // for ( int i = bestLevel - 1; i >= 0; --i ) // levels.add( getTransformedSource( viewerState, spimSource, screenScaleTransform, i ) ); - return new VolatileHierarchyProjector< T, ARGBType >( levels, source.getConverter(), screenImage, numRenderingThreads, renderingExecutorService ); + return new VolatileHierarchyProjector< T, ARGBType >( levels, source.getConverter(), screenImage, maskArray, numRenderingThreads, renderingExecutorService ); } private static < T > RandomAccessible< T > getTransformedSource( final ViewerState viewerState, final Source< T > source, final AffineTransform3D screenScaleTransform, final int mipmapIndex )