diff --git a/src/main/java/bdv/img/catmaid/TiledImageLoader.java b/src/main/java/bdv/img/catmaid/TiledImageLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..c7bdb703e771f6d9dc3dd2fd888886127ffe69e2 --- /dev/null +++ b/src/main/java/bdv/img/catmaid/TiledImageLoader.java @@ -0,0 +1,352 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies + * %% + * Copyright (C) 2012 - 2016 Tobias Pietzsch, Stephan Saalfeld, Stephan Preibisch, + * Jean-Yves Tinevez, HongKee Moon, Johannes Schindelin, Curtis Rueden, John Bogovic + * %% + * 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 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.img.catmaid; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; + +import bdv.AbstractViewerSetupImgLoader; +import bdv.ViewerImgLoader; +import bdv.ViewerSetupImgLoader; +import bdv.cache.CacheHints; +import bdv.cache.LoadingStrategy; +import bdv.img.cache.CacheArrayLoader; +import bdv.img.cache.CachedCellImg; +import bdv.img.cache.VolatileGlobalCellCache; +import bdv.img.cache.VolatileImgCells; +import bdv.img.cache.VolatileImgCells.CellCache; +import mpicbg.spim.data.generic.sequence.ImgLoaderHint; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.Volatile; +import net.imglib2.img.NativeImg; +import net.imglib2.img.basictypeaccess.volatiles.VolatileAccess; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.type.NativeType; +import net.imglib2.util.Fraction; + +public class TiledImageLoader< A extends VolatileAccess, T extends NativeType< T >, V extends Volatile< T > & NativeType< V > > implements ViewerImgLoader +{ + protected final int numScales; + + protected final double[][] mipmapResolutions; + + protected final AffineTransform3D[] mipmapTransforms; + + protected final long[][] imageDimensions; + + protected final int[][] blockDimensions; + + protected VolatileGlobalCellCache cache; + + final protected HashMap< Integer, TiledSetupImageLoader > setupLoaders = new HashMap<>(); + + final static private int[][] blockDimensions( + final int tileWidth, + final int tileHeight, + final int numScales ) + { + final int[][] blockDimensions = new int[ numScales ][]; + for ( int i = 0; i < numScales; ++i ) + blockDimensions[ i ] = new int[]{ tileWidth, tileHeight, 1 }; + + return blockDimensions; + } + + public TiledImageLoader( + final List< CacheArrayLoader< A > > loaders, + final T type, + final V vType, + final long width, + final long height, + final long depth, + final double zScale, + final int tileWidth, + final int tileHeight, + final int[][] blockDimensions, + final boolean topLeft ) + { + this.numScales = blockDimensions.length; + + mipmapResolutions = new double[ numScales ][]; + imageDimensions = new long[ numScales ][]; + mipmapTransforms = new AffineTransform3D[ numScales ]; + final int[] zScales = new int[ numScales ]; + this.blockDimensions = new int[ numScales ][]; + for ( int l = 0; l < numScales; ++l ) + { + final int sixy = 1 << l; + final int siz = Math.max( 1, ( int )Math.round( sixy / zScale ) ); + + mipmapResolutions[ l ] = new double[] { sixy, sixy, siz }; + imageDimensions[ l ] = new long[] { width >> l, height >> l, depth / siz }; + this.blockDimensions[ l ] = blockDimensions[ l ].clone(); + zScales[ l ] = siz; + + final AffineTransform3D mipmapTransform = new AffineTransform3D(); + + mipmapTransform.set( sixy, 0, 0 ); + mipmapTransform.set( sixy, 1, 1 ); + mipmapTransform.set( zScale * siz, 2, 2 ); + + if ( topLeft ) + { + mipmapTransform.set( 0.5 * ( sixy - 1 ), 0, 3 ); + mipmapTransform.set( 0.5 * ( sixy - 1 ), 1, 3 ); + } + mipmapTransform.set( 0.5 * ( zScale * siz - 1 ), 2, 3 ); + + mipmapTransforms[ l ] = mipmapTransform; + } + + for ( int i = 0; i < loaders.size(); ++i ) + setupLoaders.put( i, new TiledSetupImageLoader( loaders.get( i ), type, vType, i ) ); + + cache = new VolatileGlobalCellCache( numScales, 10 ); + } + + public TiledImageLoader( + final List< CacheArrayLoader< A > > loaders, + final T type, + final V vType, + final long width, + final long height, + final long depth, + final double zScale, + final int tileWidth, + final int tileHeight, + final int[][] blockDimensions ) + { + this( loaders, type, vType, width, height, depth, zScale, tileWidth, tileHeight, blockDimensions, true ); + } + + public TiledImageLoader( + final List< CacheArrayLoader< A > > loaders, + final T type, + final V vType, + final long width, + final long height, + final long depth, + final double zScale, + final int numScales, + final int tileWidth, + final int tileHeight, + final int blockWidth, + final int blockHeight, + final boolean topLeft ) + { + this( loaders, type, vType, width, height, depth, zScale, tileWidth, tileHeight, blockDimensions( blockWidth, blockHeight, numScales ), topLeft ); + } + + public TiledImageLoader( + final List< CacheArrayLoader< A > > loaders, + final T type, + final V vType, + final long width, + final long height, + final long depth, + final double zScale, + final int numScales, + final int tileWidth, + final int tileHeight, + final int blockWidth, + final int blockHeight ) + { + this( loaders, type, vType, width, height, depth, zScale, tileWidth, tileHeight, blockDimensions( blockWidth, blockHeight, numScales ), true ); + } + + public TiledImageLoader( + final List< CacheArrayLoader< A > > loaders, + final T type, + final V vType, + final long width, + final long height, + final long depth, + final double zScale, + final int numScales, + final int tileWidth, + final int tileHeight, + final boolean topLeft ) + { + this( loaders, type, vType, width, height, depth, zScale, numScales, tileWidth, tileHeight, tileWidth, tileHeight, topLeft ); + } + + public TiledImageLoader( + final List< CacheArrayLoader< A > > loaders, + final T type, + final V vType, + final long width, + final long height, + final long depth, + final double zScale, + final int numScales, + final int tileWidth, + final int tileHeight ) + { + this( loaders, type, vType, width, height, depth, zScale, numScales, tileWidth, tileHeight, true ); + } + + final static public int getNumScales( long width, long height, final long tileWidth, final long tileHeight ) + { + int i = 1; + + while ( ( width >>= 1 ) > tileWidth && ( height >>= 1 ) > tileHeight ) + ++i; + + return i; + } + + /** + * (Almost) create a {@link CachedCellImg} backed by the cache. + * The created image needs a {@link NativeImg#setLinkedType(net.imglib2.type.Type) linked type} before it can be used. + */ + protected < N extends NativeType< N > > CachedCellImg< N, A > prepareCachedImage( + final CacheArrayLoader< A > loader, + final int timepointId, + final int setupId, + final int level, + final LoadingStrategy loadingStrategy ) + { + final long[] dimensions = imageDimensions[ level ]; + + final int priority = numScales - 1 - level; + final CacheHints cacheHints = new CacheHints( loadingStrategy, priority, false ); + final CellCache< A > c = cache.new VolatileCellCache<>( timepointId, setupId, level, cacheHints, loader ); + final VolatileImgCells< A > cells = new VolatileImgCells<>( c, new Fraction(), dimensions, blockDimensions[ level ] ); + final CachedCellImg< N, A > img = new CachedCellImg<>( cells ); + return img; + } + + @Override + public VolatileGlobalCellCache getCacheControl() + { + return cache; + } + + @Override + public ViewerSetupImgLoader< ?, ? > getSetupImgLoader( final int setupId ) + { + return setupLoaders.get( setupId ); + } + + /** + * Reflection hack because there is no T NativeType<T>.create(NativeImg<?, A>) method in ImgLib2 + * Note that for this method to be introduced, NativeType would need an additional generic parameter A + * that specifies the accepted family of access objects that can be used in the NativeImg... big change + * + * @throws SecurityException + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InstantiationException + */ + @SuppressWarnings( { "rawtypes", "unchecked" } ) + protected void linkType( final NativeType t, final CachedCellImg img ) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException + { + final Constructor constructor = t.getClass().getDeclaredConstructor( NativeImg.class ); + if ( constructor != null ) + { + final NativeType linkedType = ( NativeType )constructor.newInstance( img ); + img.setLinkedType( linkedType ); + } + } + + public void setCache( final VolatileGlobalCellCache cache ) + { + this.cache = cache; + } + + public class TiledSetupImageLoader extends AbstractViewerSetupImgLoader< T, V > + { + final protected int setupId; + + final protected CacheArrayLoader< A > loader; + + public TiledSetupImageLoader( final CacheArrayLoader< A > loader, final T type, final V volatileType, final int setupId ) + { + super( type, volatileType ); + + this.loader = loader; + this.setupId = setupId; + } + + @Override + public RandomAccessibleInterval< T > getImage( final int timepointId, final int level, final ImgLoaderHint... hints ) + { + try + { + final CachedCellImg< T, A > img = prepareCachedImage( loader, timepointId, setupId, level, LoadingStrategy.BLOCKING ); + linkType( type, img ); + return img; + } + catch ( final Exception e ) + { + e.printStackTrace( System.err ); + return null; + } + } + + @Override + public RandomAccessibleInterval< V > getVolatileImage( final int timepointId, final int level, final ImgLoaderHint... hints ) + { + try + { + final CachedCellImg< V, A > img = prepareCachedImage( loader, timepointId, setupId, level, LoadingStrategy.VOLATILE ); + linkType( volatileType, img ); + return img; + } + catch ( final Exception e ) + { + e.printStackTrace( System.err ); + return null; + } + } + + @Override + public double[][] getMipmapResolutions() + { + return mipmapResolutions; + } + + @Override + public AffineTransform3D[] getMipmapTransforms() + { + return mipmapTransforms; + } + + @Override + public int numMipmapLevels() + { + return numScales; + } + } +} diff --git a/src/main/java/bdv/img/catmaid/VolatileFloatArrayTileLoader.java b/src/main/java/bdv/img/catmaid/VolatileFloatArrayTileLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..d999735e4ab53bbe35b1a5241abf1250c66388e9 --- /dev/null +++ b/src/main/java/bdv/img/catmaid/VolatileFloatArrayTileLoader.java @@ -0,0 +1,227 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies + * %% + * Copyright (C) 2012 - 2016 Tobias Pietzsch, Stephan Saalfeld, Stephan Preibisch, + * Jean-Yves Tinevez, HongKee Moon, Johannes Schindelin, Curtis Rueden, John Bogovic + * %% + * 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 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.img.catmaid; + +import bdv.img.cache.CacheArrayLoader; +import ij.ImagePlus; +import ij.io.Opener; +import ij.process.Blitter; +import ij.process.FloatProcessor; +import net.imglib2.img.basictypeaccess.volatiles.array.VolatileFloatArray; + +public class VolatileFloatArrayTileLoader implements CacheArrayLoader< VolatileFloatArray > +{ + private VolatileFloatArray theEmptyArray; + + private final String urlFormat; + + private final int tileWidth; + + private final int tileHeight; + + final private int[] zScales; + + /** + * <p>Create a {@link CacheArrayLoader} for a tiled image source. Tiles are + * addressed, in this order, by their</p> + * <ul> + * <li>scale level,</li> + * <li>scale,</li> + * <li>x,</li> + * <li>y,</li> + * <li>z,</li> + * <li>tile width,</li> + * <li>tile height,</li> + * <li>tile row, and</li> + * <li>tile column.</li> + * </ul> + * <p><code>urlFormat</code> specifies how these parameters are used + * to generate a URL referencing the tile. Examples:</p> + * + * <dl> + * <dt>"http://catmaid.org/my-data/xy/%5$d/%8$d_%9$d_%1$d.jpg"</dt> + * <dd>CATMAID DefaultTileSource (type 1)</dd> + * <dt>"http://catmaid.org/my-data/xy/?x=%3$d&y=%4$d&width=%6d&height=%7$d&row=%8$d&col=%9$d&scale=%2$f&z=%4$d"</dt> + * <dd>CATMAID RequestTileSource (type 2)</dd> + * <dt>"http://catmaid.org/my-data/xy/%1$d/%5$d/%8$d/%9$d.jpg"</dt> + * <dd>CATMAID LargeDataTileSource (type 5)</dd> + * </dl> + * + * @param urlFormat + * @param tileWidth + * @param tileHeight + */ + public VolatileFloatArrayTileLoader( final String urlFormat, final int tileWidth, final int tileHeight, final int[] zScales ) + { + theEmptyArray = new VolatileFloatArray( tileWidth * tileHeight, false ); + this.urlFormat = urlFormat; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.zScales = zScales; + } + + @Override + public int getBytesPerElement() + { + return 4; + } + + final private void loadSliceArray( + final float[] slice, + final int level, + final double scale, + final long c0, + final long r0, + final long x0, + final long y0, + final long z, + final long xm, + final long ym, + final long[] min, + final int w, + final int h ) throws InterruptedException + { + final FloatProcessor ip = new FloatProcessor( w, h, slice ); + + for ( + long c = c0, x = x0; + x < xm; + ++c, x += tileWidth ) + { + for ( + long r = r0, y = y0; + y < ym; + ++r, y += tileHeight ) + { + final String urlString = String.format( urlFormat, level, scale, x, y, z, tileWidth, tileHeight, r, c ); +// System.out.println( "r=" + r + " c=" + c + " : " + urlString ); + final ImagePlus tile; + // ij.io.Opener cannot handle "file:" URLs... + if ( urlString.startsWith( "file:" ) ) + tile = new Opener().openImage( urlString.substring( 5 ) ); + else + tile = new Opener().openURL( urlString ); + if ( tile == null ) + System.out.println( "failed loading r=" + r + " c=" + c ); + else + { + final FloatProcessor tp = tile.getProcessor().convertToFloatProcessor(); + ip.copyBits( tp, ( int )( x - min[ 0 ] ), ( int )( y - min[ 1 ] ), Blitter.COPY ); + } + } + } + } + + + final private void averageSlice( + final float[] slice, + final int level, + final double scale, + final long c0, + final long r0, + final long x0, + final long y0, + final long xm, + final long ym, + final long[] min, + final int w, + final int h ) throws InterruptedException + { + final double[] fs = new double[ slice.length ]; + for ( int z = ( int ) min[ 2 ] * zScales[ level ], dz = 0; dz < zScales[ level ]; ++dz ) + { + loadSliceArray( slice, level, scale, c0, r0, x0, y0, z + dz, xm, ym, min, w, h ); + for ( int i = 0; i < slice.length; ++i ) + fs[ i ] += slice[ i ]; + } + for ( int i = 0; i < slice.length; ++i ) + slice[ i ] = ( float )fs[ i ]; + } + + + @Override + public VolatileFloatArray loadArray( + final int timepoint, + final int setup, + final int level, + final int[] dimensions, + final long[] min ) throws InterruptedException + { + final int w = dimensions[ 0 ]; + final int h = dimensions[ 1 ]; + final long xm = min[ 0 ] + w; + final long ym = min[ 1 ] + h; + final double scale = 1.0 / Math.pow(2.0, level); + final float[] slice = new float[ w * h ]; + + final long c0 = min[ 0 ] / tileWidth; + final long r0 = min[ 1 ] / tileHeight; + final long x0 = c0 * tileWidth; + final long y0 = r0 * tileHeight; + + final float[] data; + if ( dimensions[ 2 ] > 1 ) + { + data = new float[ w * h * dimensions[ 2 ] ]; + final long[] zMin = min.clone(); + for ( int z = 0; z < dimensions[ 2 ]; ++z ) + { + zMin[ 2 ] = min[ 2 ] + z; + if ( zScales[ level ] > 1 ) + averageSlice( slice, level, scale, c0, r0, x0, y0, xm, ym, zMin, w, h ); + else + loadSliceArray( slice, level, scale, c0, r0, x0, y0, zMin[ 2 ], xm, ym, zMin, w, h ); + + System.arraycopy( slice, 0, data, z * slice.length, slice.length ); + } + } + else + { + data = slice; + if ( zScales[ level ] > 1 ) + averageSlice( slice, level, scale, c0, r0, x0, y0, xm, ym, min, w, h ); + else + loadSliceArray( slice, level, scale, c0, r0, x0, y0, min[ 2 ], xm, ym, min, w, h ); + } + + return new VolatileFloatArray( data, true ); + } + + @Override + public VolatileFloatArray 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 VolatileFloatArray( numEntities, false ); + return theEmptyArray; + } +} diff --git a/src/main/java/bdv/img/catmaid/VolatileUnsignedByteArrayTileLoader.java b/src/main/java/bdv/img/catmaid/VolatileUnsignedByteArrayTileLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..f2bcd6fbab68bc8912ac6393e2680d71a4186434 --- /dev/null +++ b/src/main/java/bdv/img/catmaid/VolatileUnsignedByteArrayTileLoader.java @@ -0,0 +1,229 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies + * %% + * Copyright (C) 2012 - 2016 Tobias Pietzsch, Stephan Saalfeld, Stephan Preibisch, + * Jean-Yves Tinevez, HongKee Moon, Johannes Schindelin, Curtis Rueden, John Bogovic + * %% + * 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 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.img.catmaid; + +import bdv.img.cache.CacheArrayLoader; +import ij.ImagePlus; +import ij.io.Opener; +import ij.process.Blitter; +import ij.process.ByteProcessor; +import net.imglib2.img.basictypeaccess.volatiles.array.VolatileByteArray; + +public class VolatileUnsignedByteArrayTileLoader implements CacheArrayLoader< VolatileByteArray > +{ + private VolatileByteArray theEmptyArray; + + private final String urlFormat; + + private final int tileWidth; + + private final int tileHeight; + + final private int[] zScales; + + /** + * <p>Create a {@link CacheArrayLoader} for a tiled image source. Tiles are + * addressed, in this order, by their</p> + * <ul> + * <li>scale level,</li> + * <li>scale,</li> + * <li>x,</li> + * <li>y,</li> + * <li>z,</li> + * <li>tile width,</li> + * <li>tile height,</li> + * <li>tile row, and</li> + * <li>tile column.</li> + * </ul> + * <p><code>urlFormat</code> specifies how these parameters are used + * to generate a URL referencing the tile. Examples:</p> + * + * <dl> + * <dt>"http://catmaid.org/my-data/xy/%5$d/%8$d_%9$d_%1$d.jpg"</dt> + * <dd>CATMAID DefaultTileSource (type 1)</dd> + * <dt>"http://catmaid.org/my-data/xy/?x=%3$d&y=%4$d&width=%6d&height=%7$d&row=%8$d&col=%9$d&scale=%2$f&z=%4$d"</dt> + * <dd>CATMAID RequestTileSource (type 2)</dd> + * <dt>"http://catmaid.org/my-data/xy/%1$d/%5$d/%8$d/%9$d.jpg"</dt> + * <dd>CATMAID LargeDataTileSource (type 5)</dd> + * </dl> + * + * @param urlFormat + * @param tileWidth + * @param tileHeight + */ + public VolatileUnsignedByteArrayTileLoader( final String urlFormat, final int tileWidth, final int tileHeight, final int[] zScales ) + { + theEmptyArray = new VolatileByteArray( tileWidth * tileHeight, false ); + this.urlFormat = urlFormat; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.zScales = zScales; + } + + @Override + public int getBytesPerElement() + { + return 1; + } + + final private void loadSliceArray( + final byte[] slice, + final int level, + final double scale, + final long c0, + final long r0, + final long x0, + final long y0, + final long z, + final long xm, + final long ym, + final long[] min, + final int w, + final int h ) throws InterruptedException + { + final ByteProcessor ip = new ByteProcessor( w, h, slice ); + + for ( + long c = c0, x = x0; + x < xm; + ++c, x += tileWidth ) + { + for ( + long r = r0, y = y0; + y < ym; + ++r, y += tileHeight ) + { + final String urlString = String.format( urlFormat, level, scale, x, y, z, tileWidth, tileHeight, r, c ); +// System.out.println( urlString ); + final ImagePlus tile; + // ij.io.Opener cannot handle "file:" URLs... + if ( urlString.startsWith( "file:" ) ) + tile = new Opener().openImage( urlString.substring( 5 ) ); + else + tile = new Opener().openURL( urlString ); + if ( tile == null ) + System.out.println( "failed loading r=" + r + " c=" + c ); + else + { + final ByteProcessor tp = tile.getProcessor().convertToByteProcessor(); + ip.copyBits( tp, ( int )( x - min[ 0 ] ), ( int )( y - min[ 1 ] ), Blitter.COPY ); + } + } + } + } + + + final private void averageSlice( + final byte[] slice, + final int level, + final double scale, + final long c0, + final long r0, + final long x0, + final long y0, + final long xm, + final long ym, + final long[] min, + final int w, + final int h ) throws InterruptedException + { + final double[] fs = new double[ slice.length ]; + for ( int z = ( int ) min[ 2 ] * zScales[ level ], dz = 0; dz < zScales[ level ]; ++dz ) + { + loadSliceArray( slice, level, scale, c0, r0, x0, y0, z + dz, xm, ym, min, w, h ); + for ( int i = 0; i < slice.length; ++i ) + fs[ i ] += slice[ i ]; + } + for ( int i = 0; i < slice.length; ++i ) + slice[ i ] = ( byte )Math.round( fs[ i ] ); + } + + + @Override + public VolatileByteArray loadArray( + final int timepoint, + final int setup, + final int level, + final int[] dimensions, + final long[] min ) throws InterruptedException + { + final int w = dimensions[ 0 ]; + final int h = dimensions[ 1 ]; + final long xm = min[ 0 ] + w; + final long ym = min[ 1 ] + h; + final double scale = 1.0 / Math.pow(2.0, level); + final byte[] slice = new byte[ w * h ]; + + final long c0 = min[ 0 ] / tileWidth; + final long r0 = min[ 1 ] / tileHeight; + final long x0 = c0 * tileWidth; + final long y0 = r0 * tileHeight; + + final byte[] data; + if ( dimensions[ 2 ] > 1 ) + { + data = new byte[ w * h * dimensions[ 2 ] ]; + final long[] zMin = min.clone(); + for ( int z = 0; z < dimensions[ 2 ]; ++z ) + { + zMin[ 2 ] = min[ 2 ] + z; + if ( zScales[ level ] > 1 ) + averageSlice( slice, level, scale, c0, r0, x0, y0, xm, ym, zMin, w, h ); + else + loadSliceArray( slice, level, scale, c0, r0, x0, y0, zMin[ 2 ], xm, ym, zMin, w, h ); + + System.arraycopy( slice, 0, data, z * slice.length, slice.length ); + } + } + else + { + data = slice; + if ( zScales[ level ] > 1 ) + averageSlice( slice, level, scale, c0, r0, x0, y0, xm, ym, min, w, h ); + else + loadSliceArray( slice, level, scale, c0, r0, x0, y0, min[ 2 ], xm, ym, min, w, h ); + } + +// new ImagePlus( "", new ByteProcessor( w, h, data) ).show(); + + return new VolatileByteArray( data, true ); + } + + @Override + public VolatileByteArray 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 VolatileByteArray( numEntities, false ); + return theEmptyArray; + } +} diff --git a/src/main/java/bdv/img/catmaid/XmlIoTiledImageLoader.java b/src/main/java/bdv/img/catmaid/XmlIoTiledImageLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..2b3ece4281810a658bf757811c38c8d80d2cee04 --- /dev/null +++ b/src/main/java/bdv/img/catmaid/XmlIoTiledImageLoader.java @@ -0,0 +1,135 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies + * %% + * Copyright (C) 2012 - 2016 Tobias Pietzsch, Stephan Saalfeld, Stephan Preibisch, + * Jean-Yves Tinevez, HongKee Moon, Johannes Schindelin, Curtis Rueden, John Bogovic + * %% + * 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 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.img.catmaid; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.jdom2.Element; + +import bdv.img.cache.CacheArrayLoader; +import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription; +import mpicbg.spim.data.generic.sequence.ImgLoaderIo; +import mpicbg.spim.data.generic.sequence.XmlIoBasicImgLoader; +import net.imglib2.Volatile; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.type.volatiles.VolatileFloatType; +import net.imglib2.type.volatiles.VolatileUnsignedByteType; + +@SuppressWarnings( "rawtypes" ) +@ImgLoaderIo( format = "tiled", type = TiledImageLoader.class ) +public class XmlIoTiledImageLoader + implements XmlIoBasicImgLoader< TiledImageLoader > +{ + @Override + public Element toXml( final TiledImageLoader imgLoader, final File basePath ) + { + throw new UnsupportedOperationException( "not implemented" ); + } + + @SuppressWarnings( { "unchecked" } ) + @Override + public TiledImageLoader fromXml( final Element elem, final File basePath, final AbstractSequenceDescription< ?, ?, ? > sequenceDescription ) + { + final long width = Long.parseLong( elem.getChildText( "width" ) ); + final long height = Long.parseLong( elem.getChildText( "height" ) ); + final long depth = Long.parseLong( elem.getChildText( "depth" ) ); + + final double resXY = Double.parseDouble( elem.getChildText( "resXY" ) ); + final double resZ = Double.parseDouble( elem.getChildText( "resZ" ) ); + final double zScale = resZ / resXY; + + final List< String > urlFormats = new ArrayList<>(); + elem.getChildren( "urlFormat" ).forEach( a -> urlFormats.add( a.getText() ) ); + + final int tileWidth = Integer.parseInt( elem.getChildText( "tileWidth" ) ); + final int tileHeight = Integer.parseInt( elem.getChildText( "tileHeight" ) ); + + final String blockWidthString = elem.getChildText( "blockWidth" ); + final String blockHeightString = elem.getChildText( "blockHeight" ); + final String blockDepthString = elem.getChildText( "blockDepth" ); + + final int blockWidth = blockWidthString == null ? tileWidth : Integer.parseInt( blockWidthString ); + final int blockHeight = blockHeightString == null ? tileHeight : Integer.parseInt( blockHeightString ); + final int blockDepth = blockDepthString == null ? 1 : Integer.parseInt( blockDepthString ); + + System.out.println( String.format( "Block size = (%d, %d, %d)", blockWidth, blockHeight, blockDepth ) ); + + final String numScalesString = elem.getChildText( "numScales" ); + int numScales; + if ( numScalesString == null ) + numScales = CatmaidImageLoader.getNumScales( width, height, tileWidth, tileHeight ); + else + numScales = Integer.parseInt( numScalesString ); + + final int[][] blockSize = new int[ numScales ][]; + final int[] zScales = new int[ numScales ]; + for ( int i = 0; i < numScales; ++i ) + { + blockSize[ i ] = new int[]{ blockWidth, blockHeight, blockDepth }; + final int sixy = 1 << i; + final int siz = Math.max( 1, ( int )Math.round( sixy / zScale ) ); + zScales[ i ] = siz; + } + + final String type = elem.getChildText( "type" ); + + final ArrayList< CacheArrayLoader< ? > > arrayLoaders = new ArrayList<>(); + final NativeType t; + final NativeType v; + switch ( type ) + { + case "float32": + urlFormats.forEach( urlFormat -> arrayLoaders.add( new VolatileFloatArrayTileLoader( urlFormat, tileWidth, tileHeight, zScales ) ) ); + t = new FloatType(); + v = new VolatileFloatType(); + break; + default: + urlFormats.forEach( urlFormat -> arrayLoaders.add( new VolatileUnsignedByteArrayTileLoader( urlFormat, tileWidth, tileHeight, zScales ) ) ); + t = new UnsignedByteType(); + v =new VolatileUnsignedByteType(); + } + + return new TiledImageLoader( + arrayLoaders, + t, + ( Volatile & NativeType )v, + width, + height, + depth, + resZ / resXY, + tileWidth, + tileHeight, + blockSize ); + } +} diff --git a/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java b/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java index 7f0ffefcea08cc066abd4488e48f7cb5a4641c88..e30b94a93b1ef9f381144ca5fca4b33d0b3f61b0 100644 --- a/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java +++ b/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java @@ -7,13 +7,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 @@ -40,6 +40,7 @@ import java.util.Map; import org.jdom2.Element; import bdv.img.catmaid.XmlIoCatmaidImageLoader; +import bdv.img.catmaid.XmlIoTiledImageLoader; import bdv.img.hdf5.Hdf5ImageLoader; import bdv.img.hdf5.Partition; import bdv.img.openconnectome.XmlIoOpenConnectomeImageLoader; @@ -177,6 +178,10 @@ public class XmlIoSpimDataMinimalLegacy { return new XmlIoCatmaidImageLoader().fromXml( elem, basePath, sequenceDescription ); } + else if ( classn.equals( "bdv.img.catmaid.TiledImageLoader" ) ) + { + return new XmlIoTiledImageLoader().fromXml( elem, basePath, sequenceDescription ); + } else if ( classn.equals( "bdv.img.openconnectome.OpenConnectomeImageLoader" ) ) { return new XmlIoOpenConnectomeImageLoader().fromXml( elem, basePath, sequenceDescription );