diff --git a/MIGRATION.md b/MIGRATION.md index 1b3fa39..b0e977f 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -13,9 +13,9 @@ Unfortunately, `imglib/imglib2-ij` cannot be used with Java 11+, because of the To resolve these errors (and for organization) this library changed many names. Below you'll find commonly-used functions from `imglib/imglib2-ij`, as well as their drop-in replacements from `imglib/imglib2-imagej`: -**`net.imglib2.img.display.imagej.ImgToVirtualStack.wrap*` have been renamed to `net.imglib2.imagej.ImgPlusToImagePlus.wrap*`**. -**`net.imglib2.img.VirtualStackAdapter.wrap*` have been renamed to `net.imglib2.imagej.ImagePlusToImgPlus.wrap*`**. -**`net.imglib2.img.ImagePlusAdapter.wrap*` have been renamed to `net.imglib2.imagej.ImagePlusToImg.wrap*`**. +**`net.imglib2.img.display.imagej.ImgToVirtualStack.wrap*` have been renamed to `net.imglib2.imagej.RAIToImagePlus.wrapVirtualStack*`**. +**`net.imglib2.img.VirtualStackAdapter.wrap*` have been renamed to `net.imglib2.imagej.ImagePlusToImg.wrapCached*`**. +**`net.imglib2.img.ImagePlusAdapter.wrap*` have been renamed to `net.imglib2.imagej.ImagePlusToImg.wrapDirect*`**. **`net.imglib2.img.ImageJFunctions.wrap*(ImagePlus)` have been renamed to `net.imglib2.imagej.ImagePlusToImg.wrap*`**. **`net.imglib2.img.display.imagej.ImageJFunctions.wrap*(RandomAccessibleInterval, ...)` have been renamed to `net.imglib2.imagej.RAIToImagePlus.wrap*`**. diff --git a/pom.xml b/pom.xml index ccc8f18..7f41cb7 100644 --- a/pom.xml +++ b/pom.xml @@ -185,10 +185,6 @@ Jean-Yves Tinevez and Michael Zinsmaier. net.imagej ij - - net.imagej - imagej-common - diff --git a/src/main/java/net/imglib2/imagej/ImagePlusToImg.java b/src/main/java/net/imglib2/imagej/ImagePlusToImg.java index ed120c0..5b12417 100644 --- a/src/main/java/net/imglib2/imagej/ImagePlusToImg.java +++ b/src/main/java/net/imglib2/imagej/ImagePlusToImg.java @@ -35,25 +35,27 @@ package net.imglib2.imagej; import ij.ImagePlus; -import net.imglib2.Cursor; import net.imglib2.cache.Cache; import net.imglib2.cache.ref.SoftRefLoaderCache; -import net.imglib2.converter.Converter; -import net.imglib2.imagej.imageplus.*; -import net.imglib2.img.Img; -import net.imglib2.img.basictypeaccess.array.ArrayDataAccess; +import net.imglib2.imagej.imageplus.ByteImagePlus; +import net.imglib2.imagej.imageplus.FloatImagePlus; +import net.imglib2.imagej.imageplus.IntImagePlus; +import net.imglib2.imagej.imageplus.ShortImagePlus; +import net.imglib2.img.basictypeaccess.array.*; import net.imglib2.img.planar.PlanarImg; -import net.imglib2.type.Type; +import net.imglib2.type.NativeType; +import net.imglib2.type.NativeTypeFactory; import net.imglib2.type.numeric.ARGBType; -import net.imglib2.type.numeric.ComplexType; import net.imglib2.type.numeric.integer.UnsignedByteType; -import net.imglib2.type.numeric.integer.UnsignedIntType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Fraction; import java.util.AbstractList; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.function.Function; +import java.util.stream.LongStream; /** * Provides convenience functions to wrap ImageJ 1.x data structures as ImgLib2 @@ -63,31 +65,54 @@ * @author Stephan Preibisch * @author Stephan Saalfeld * @author Matthias Arzt + * @author Gabriel Selzer */ public class ImagePlusToImg { - public static PlanarImg< ?, ? > wrap( final ImagePlus imp ) + /** + * Wraps an {@link ImagePlus} into a {@link PlanarImg}. + *

+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is + * wrapped into an {@link ArrayDataAccess}. The resulting {@link List} of + * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}. + *

+ * + * @param imp the {@link ImagePlus} to wrap + * @return a {@link PlanarImg} directly wrapping {@code imp} + */ + public static PlanarImg< ?, ? > wrapDirect(final ImagePlus imp ) { switch ( imp.getType() ) { case ImagePlus.GRAY8: - return wrapByte( imp ); + return wrapByteDirect( imp ); case ImagePlus.GRAY16: - return wrapShort( imp ); + return wrapShortDirect( imp ); case ImagePlus.GRAY32: - return wrapFloat( imp ); + return wrapFloatDirect( imp ); case ImagePlus.COLOR_RGB: - return wrapRGBA( imp ); + return wrapRGBADirect( imp ); default: throw new RuntimeException( "Only 8, 16, 32-bit and RGB supported!" ); } } - public static PlanarImg< UnsignedByteType, ? > wrapByte(final ImagePlus imp ) + /** + * Wraps an {@link ImagePlus} into a {@link PlanarImg} of unsigned bytes. + *

+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is + * wrapped into an {@link ByteArray}. The resulting {@link List} of + * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}. + *

+ * + * @param imp the {@link ImagePlus} to wrap + * @return a {@link PlanarImg} of unsigned bytes directly wrapping {@code imp} + */ + public static PlanarImg< UnsignedByteType, ? > wrapByteDirect(final ImagePlus imp ) { if ( imp.getType() != ImagePlus.GRAY8 ) - return null; + throw new IllegalArgumentException(imp + " does not contain unsigned bytes!"); final ByteImagePlus< UnsignedByteType > container = new ByteImagePlus<>( imp ); @@ -100,10 +125,21 @@ public class ImagePlusToImg return container; } - public static PlanarImg< UnsignedShortType, ? > wrapShort(final ImagePlus imp ) + /** + * Wraps an {@link ImagePlus} into a {@link PlanarImg} of unsigned shorts. + *

+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is + * wrapped into an {@link ShortArray}. The resulting {@link List} of + * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}. + *

+ * + * @param imp the {@link ImagePlus} to wrap + * @return a {@link PlanarImg} of unsigned shorts directly wrapping {@code imp} + */ + public static PlanarImg< UnsignedShortType, ? > wrapShortDirect(final ImagePlus imp ) { if ( imp.getType() != ImagePlus.GRAY16 ) - return null; + throw new IllegalArgumentException(imp + " does not contain unsigned shorts!"); final ShortImagePlus< UnsignedShortType > container = new ShortImagePlus<>( imp ); @@ -116,26 +152,21 @@ public class ImagePlusToImg return container; } - public static PlanarImg< UnsignedIntType, ? > wrapInt(final ImagePlus imp ) - { - if( imp.getType() != ImagePlus.COLOR_RGB ) - return null; - - final IntImagePlus< UnsignedIntType > container = new IntImagePlus<>( imp ); - - // create a Type that is linked to the container - final UnsignedIntType linkedType = new UnsignedIntType( container ); - - // pass it to the DirectAccessContainer - container.setLinkedType( linkedType ); - - return container; - } - - public static PlanarImg< ARGBType, ? > wrapRGBA( final ImagePlus imp ) + /** + * Wraps an {@link ImagePlus} into a {@link PlanarImg} of RGBA tuples. + *

+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is + * wrapped into an {@link IntArray}. The resulting {@link List} of + * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}. + *

+ * + * @param imp the {@link ImagePlus} to wrap + * @return a {@link PlanarImg} of ARGB tuples directly wrapping {@code imp} + */ + public static PlanarImg< ARGBType, ? > wrapRGBADirect(final ImagePlus imp ) { if ( imp.getType() != ImagePlus.COLOR_RGB ) - return null; + throw new IllegalArgumentException(imp + " does not contain RGB tuples!"); final IntImagePlus< ARGBType > container = new IntImagePlus<>( imp ); @@ -148,10 +179,21 @@ public class ImagePlusToImg return container; } - public static PlanarImg< FloatType, ? > wrapFloat(final ImagePlus imp ) + /** + * Wraps an {@link ImagePlus} into a {@link PlanarImg} of floats. + *

+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is + * wrapped into an {@link FloatArray}. The resulting {@link List} of + * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}. + *

+ * + * @param imp the {@link ImagePlus} to wrap + * @return a {@link PlanarImg} of floats directly wrapping {@code imp} + */ + public static PlanarImg< FloatType, ? > wrapFloatDirect(final ImagePlus imp ) { if ( imp.getType() != ImagePlus.GRAY32 ) - return null; + throw new IllegalArgumentException(imp + " does not contain floats!"); final FloatImagePlus< FloatType > container = new FloatImagePlus<>( imp ); @@ -164,61 +206,99 @@ public class ImagePlusToImg return container; } - public static PlanarImg< FloatType, ? > convertFloat( final ImagePlus imp ) + /** + * Wraps an 8 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed + * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if + * needed, and caches them. + * @param image the {@link ImagePlus} to wrap. Must contain unsigned bytes. + * @return a {@link PlanarImg} wrapping {@code image}. + */ + public static PlanarImg< UnsignedByteType, ByteArray > wrapByteCached(final ImagePlus image ) { - - switch ( imp.getType() ) - { - case ImagePlus.GRAY8: - return convertToFloat( wrapByte( imp ), new NumberToFloatConverter< UnsignedByteType >() ); - case ImagePlus.GRAY16: - return convertToFloat( wrapShort( imp ), new NumberToFloatConverter< UnsignedShortType >() ); - case ImagePlus.GRAY32: - return wrapFloat( imp ); - case ImagePlus.COLOR_RGB: - return convertToFloat( wrapRGBA( imp ), new ARGBtoFloatConverter() ); - default: - throw new RuntimeException( "Only 8, 16, 32-bit and RGB supported!" ); - } + return internWrap( image, ImagePlus.GRAY8, new UnsignedByteType(), array -> new ByteArray( ( byte[] ) array ) ); } - static private class ARGBtoFloatConverter implements Converter< ARGBType, FloatType > + /** + * Wraps a 16 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed + * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if + * needed, and caches them. + * @param image the {@link ImagePlus} to wrap. Must contain unsigned shorts. + * @return a {@link PlanarImg} wrapping {@code image}. + */ + public static PlanarImg< UnsignedShortType, ShortArray > wrapShortCached(final ImagePlus image ) { - /** Luminance times alpha. */ - @Override - public void convert( final ARGBType input, final FloatType output ) - { - final int v = input.get(); - output.setReal( ( ( v >> 24 ) & 0xff ) * ( ( ( v >> 16 ) & 0xff ) * 0.299 + ( ( v >> 8 ) & 0xff ) * 0.587 + ( v & 0xff ) * 0.144 ) ); - } + return internWrap( image, ImagePlus.GRAY16, new UnsignedShortType(), array -> new ShortArray( ( short[] ) array ) ); } - static private class NumberToFloatConverter< T extends ComplexType< T > > implements Converter< T, FloatType > + /** + * Wraps a 32 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed + * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if + * needed, and caches them. + * @param image the {@link ImagePlus} to wrap. Must contain floats. + * @return a {@link PlanarImg} wrapping {@code image}. + */ + public static PlanarImg< FloatType, FloatArray > wrapFloatCached(final ImagePlus image ) { - @Override - public void convert( final T input, final FloatType output ) - { - output.setReal( input.getRealFloat() ); - } + return internWrap( image, ImagePlus.GRAY32, new FloatType(), array -> new FloatArray( ( float[] ) array ) ); } - protected static < T extends Type< T > > PlanarImg< FloatType, ?> convertToFloat( - final Img< T > input, final Converter< T, FloatType > c ) + /** + * Wraps a 24 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed + * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if + * needed, and caches them. + * @param image the {@link ImagePlus} to wrap. Must contain RGB tuples. + * @return a {@link PlanarImg} wrapping {@code image}. + */ + public static PlanarImg< ARGBType, IntArray > wrapRGBACached(final ImagePlus image ) { - final ImagePlusImg< FloatType, ? > output = new ImagePlusImgFactory<>( new FloatType() ).create( input ); - - final Cursor< T > in = input.cursor(); - final Cursor< FloatType > out = output.cursor(); + return internWrap( image, ImagePlus.COLOR_RGB, new ARGBType(), array -> new IntArray( ( int[] ) array ) ); + } - while ( in.hasNext() ) + /** + * Wraps an {@link ImagePlus}, into an {@link PlanarImg}, that is backed by a + * {@link PlanarImg}. The {@link PlanarImg} loads the planes only if needed, + * and caches them. The pixel type of the returned image depends on the type + * of the ImagePlus. + * @param image the {@link ImagePlus} to wrap + * @return a {@link PlanarImg} wrapping {@code image}. + */ + public static PlanarImg< ?, ? > wrapCached(final ImagePlus image ) + { + switch ( image.getType() ) { - in.fwd(); - out.fwd(); - - c.convert( in.get(), out.get() ); + case ImagePlus.GRAY8: + return wrapByteCached( image ); + case ImagePlus.GRAY16: + return wrapShortCached( image ); + case ImagePlus.GRAY32: + return wrapFloatCached( image ); + case ImagePlus.COLOR_RGB: + return wrapRGBACached( image ); } + throw new RuntimeException( "Only 8, 16, 32-bit and RGB supported!" ); + } - return output; + private static < T extends NativeType< T >, A extends ArrayDataAccess< A > > PlanarImg< T, A > internWrap( + final ImagePlus image, + final int expectedType, + final T type, + final Function< Object, A > createArrayAccess + ) { + if ( image.getType() != expectedType ) + throw new IllegalArgumentException(); + final ImagePlusLoader< A > loader = new ImagePlusLoader<>( image, createArrayAccess ); + final long[] dimensions = getNonTrivialDimensions( image ); + final PlanarImg< T, A > cached = new PlanarImg<>( loader, dimensions, new Fraction() ); + cached.setLinkedType( ( (NativeTypeFactory< T, A >) type.getNativeTypeFactory() ).createLinkedType( cached ) ); + // TODO: Preserve metadata + return cached; + } + + private static long[] getNonTrivialDimensions(final ImagePlus image ) + { + final LongStream xy = LongStream.of( image.getWidth(), image.getHeight() ); + final LongStream czt = LongStream.of( image.getNChannels(), image.getNSlices(), image.getNFrames() ); + return LongStream.concat( xy, czt.filter( x -> x > 1 ) ).toArray(); } private static class ImagePlusLoader< A extends ArrayDataAccess< A >> extends AbstractList< A > @@ -260,6 +340,4 @@ public int size() return image.getStackSize(); } } - - // } diff --git a/src/main/java/net/imglib2/imagej/ImagePlusToImgPlus.java b/src/main/java/net/imglib2/imagej/ImagePlusToImgPlus.java deleted file mode 100644 index 4d473fe..0000000 --- a/src/main/java/net/imglib2/imagej/ImagePlusToImgPlus.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * 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 net.imglib2.imagej; - -import ij.ImagePlus; -import net.imagej.ImgPlus; -import net.imagej.axis.CalibratedAxis; -import net.imglib2.cache.Cache; -import net.imglib2.cache.ref.SoftRefLoaderCache; -import net.imglib2.imagej.img.CalibrationUtils; -import net.imglib2.img.basictypeaccess.array.*; -import net.imglib2.img.planar.PlanarImg; -import net.imglib2.type.NativeType; -import net.imglib2.type.NativeTypeFactory; -import net.imglib2.type.numeric.ARGBType; -import net.imglib2.type.numeric.integer.UnsignedByteType; -import net.imglib2.type.numeric.integer.UnsignedIntType; -import net.imglib2.type.numeric.integer.UnsignedShortType; -import net.imglib2.type.numeric.real.FloatType; -import net.imglib2.util.Fraction; - -import java.util.AbstractList; -import java.util.concurrent.ExecutionException; -import java.util.function.Function; -import java.util.stream.LongStream; - -/** - * Provides convenience functions to wrap ImageJ 1.x data structures as ImageJ ones. - * Many functions wrap lazily, using imglib2-caches. These functions load - * planes on-demand, which is especially useful when wrapping a virtual stack. - * - * - * @author Stephan Preibisch - * @author Stephan Saalfeld - * @author Matthias Arzt - */ -public class ImagePlusToImgPlus -{ - - /** - * Wraps a 8 bit {@link ImagePlus}, into an {@link ImgPlus}, that is backed - * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if - * needed, and caches them. The axes of the returned image are set according - * to the calibration of the given image. - */ - public static ImgPlus< UnsignedByteType > wrapByte(final ImagePlus image ) - { - return internWrap( image, ImagePlus.GRAY8, new UnsignedByteType(), array -> new ByteArray( ( byte[] ) array ) ); - } - - /** - * Wraps a 16 bit {@link ImagePlus}, into an {@link ImgPlus}, that is backed - * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if - * needed, and caches them. The axes of the returned image are set according - * to the calibration of the given image. - */ - public static ImgPlus< UnsignedShortType > wrapShort(final ImagePlus image ) - { - return internWrap( image, ImagePlus.GRAY16, new UnsignedShortType(), array -> new ShortArray( ( short[] ) array ) ); - } - - /** - * Wraps a 32 bit {@link ImagePlus}, into an {@link ImgPlus}, that is backed - * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if - * needed, and caches them. The axes of the returned image are set according - * to the calibration of the given image. - */ - public static ImgPlus< FloatType > wrapFloat(final ImagePlus image ) - { - return internWrap( image, ImagePlus.GRAY32, new FloatType(), array -> new FloatArray( ( float[] ) array ) ); - } - - /** - * Wraps a 32 bit {@link ImagePlus}, into an {@link ImgPlus}, that is backed - * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if - * needed, and caches them. The axes of the returned image are set according - * to the calibration of the given image. - */ - public static ImgPlus< UnsignedIntType > wrapInt(final ImagePlus image ) - { - return internWrap( image, ImagePlus.COLOR_RGB, new UnsignedIntType(), array -> new IntArray( ( int[] ) array ) ); - } - - /** - * Wraps a 24 bit {@link ImagePlus}, into an {@link ImgPlus}, that is backed - * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if - * needed, and caches them. The axes of the returned image are set according - * to the calibration of the given image. - */ - public static ImgPlus< ARGBType > wrapRGBA(final ImagePlus image ) - { - return internWrap( image, ImagePlus.COLOR_RGB, new ARGBType(), array -> new IntArray( ( int[] ) array ) ); - } - - /** - * Wraps an {@link ImagePlus}, into an {@link ImgPlus}, that is backed by a - * {@link PlanarImg}. The {@link PlanarImg} loads the planes only if needed, - * and caches them. The pixel type of the returned image depends on the type - * of the ImagePlus. The axes of the returned image are set according to the - * calibration of the given image. - */ - public static ImgPlus< ? > wrap(final ImagePlus image ) - { - switch ( image.getType() ) - { - case ImagePlus.GRAY8: - return wrapByte( image ); - case ImagePlus.GRAY16: - return wrapShort( image ); - case ImagePlus.GRAY32: - return wrapFloat( image ); - case ImagePlus.COLOR_RGB: - return wrapRGBA( image ); - } - throw new RuntimeException( "Only 8, 16, 32-bit and RGB supported!" ); - } - - private static < T extends NativeType< T >, A extends ArrayDataAccess< A > > ImgPlus< T > internWrap(final ImagePlus image, final int expectedType, final T type, final Function< Object, A > createArrayAccess ) - { - if ( image.getType() != expectedType ) - throw new IllegalArgumentException(); - final ImagePlusLoader< A > loader = new ImagePlusLoader<>( image, createArrayAccess ); - final long[] dimensions = getNonTrivialDimensions( image ); - final PlanarImg< T, A > cached = new PlanarImg<>( loader, dimensions, new Fraction() ); - cached.setLinkedType( ( (NativeTypeFactory< T, A >) type.getNativeTypeFactory() ).createLinkedType( cached ) ); - final CalibratedAxis[] axes = CalibrationUtils.getNonTrivialAxes( image ); - return new ImgPlus<>( cached, image.getTitle(), axes ); - } - - private static long[] getNonTrivialDimensions(final ImagePlus image ) - { - final LongStream xy = LongStream.of( image.getWidth(), image.getHeight() ); - final LongStream czt = LongStream.of( image.getNChannels(), image.getNSlices(), image.getNFrames() ); - return LongStream.concat( xy, czt.filter( x -> x > 1 ) ).toArray(); - } - - private static class ImagePlusLoader< A extends ArrayDataAccess< A >> extends AbstractList< A > - { - private final ImagePlus image; - - private final Cache< Integer, A > cache; - - private final Function< Object, A > arrayFactory; - - public ImagePlusLoader( final ImagePlus image, final Function< Object, A > arrayFactory ) - { - this.arrayFactory = arrayFactory; - this.image = image; - cache = new SoftRefLoaderCache< Integer, A >().withLoader( this::load ); - } - - @Override - public A get( final int key ) - { - try - { - return cache.get( key ); - } - catch ( final ExecutionException e ) - { - throw new RuntimeException( e ); - } - } - - private A load( final Integer key ) - { - return arrayFactory.apply( image.getStack().getPixels( key + 1 ) ); - } - - @Override - public int size() - { - return image.getStackSize(); - } - } - - // -} diff --git a/src/main/java/net/imglib2/imagej/ImgPlusToImagePlus.java b/src/main/java/net/imglib2/imagej/ImgPlusToImagePlus.java deleted file mode 100644 index 99941ec..0000000 --- a/src/main/java/net/imglib2/imagej/ImgPlusToImagePlus.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * 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 net.imglib2.imagej; - -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.imagej.img.*; -import net.imglib2.transform.integer.MixedTransform; -import net.imglib2.type.logic.BitType; -import net.imglib2.type.numeric.ARGBType; -import net.imglib2.type.numeric.RealType; -import net.imglib2.view.MixedTransformView; -import net.imglib2.view.Views; - -import ij.ImagePlus; - -public class ImgPlusToImagePlus -{ - // TODO move to image-legacy - public static ImagePlus wrap( final ImgPlus< ? extends RealType< ? > > imgPlus, final boolean mergeRGB ) - { - final ImgPlus< ? > imgPlus2 = mergeRGB && ImgPlusViews.canFuseColor( imgPlus ) ? ImgPlusViews.fuseColor( imgPlus ) : imgPlus; - return wrap( imgPlus2 ); - } - - /** - * Wraps an {@link ImgPlus} into an {@link ImagePlus}. The image can be - * {@link RealType} or {@link ARGBType}. The {@link ImagePlus} is backed by - * a special {@link ij.VirtualStack}, which copies an plane from the given - * image, instead of it plane from a file. - *

- * Only up to five dimensions are support. Axes can might be arbitrary. The - * image title and calibration are derived from the given image. - * - * @see ArrayImgToImagePlus - * @see ImgPlusToImagePlus - */ - public static ImagePlus wrap( final ImgPlus< ? > imgPlus ) - { - return wrap( imgPlus, ImgPlusToImagePlus::createVirtualStack ); - } - - /** - * Similar to {@link #wrap(ImgPlus)}, but works only for {@link ImgPlus} of - * {@link BitType}. The pixel values of 0 and 1 are scaled to 0 and 255. - * - * @see ArrayImgToImagePlus - * @see ImgPlusToImagePlus - */ - public static ImagePlus wrapAndScaleBitType( final ImgPlus< BitType > imgPlus ) - { - return wrap( imgPlus, ImgPlusToImagePlus::createVirtualStackBits ); - } - - private static < T > ImagePlus wrap( ImgPlus< T > imgPlus, final Function< RandomAccessibleInterval< T >, ImageJVirtualStack> imageStackWrapper ) - { - imgPlus = ImgPlusViews.fixAxes( imgPlus ); - final RandomAccessibleInterval< T > sorted = ensureXYCZT( imgPlus ); - final ImageJVirtualStack stack = imageStackWrapper.apply( sorted ); - final ImagePlus result = new ImagePlus( imgPlus.getName(), stack ); - // NB: setWritable after the ImagePlus is created. Otherwise a useless stack.setPixels(...) call would be performed. - stack.setWritable( true ); - result.setDimensions( ( int ) sorted.dimension( 2 ), ( int ) sorted.dimension( 3 ), ( int ) sorted.dimension( 4 ) ); - CalibrationUtils.copyCalibrationToImagePlus( imgPlus, result ); - return result; - } - - private static ImageJVirtualStack createVirtualStackBits( final RandomAccessibleInterval< BitType > sorted ) - { - return ImageJVirtualStackUnsignedByte.wrapAndScaleBitType( sorted ); - } - - private static ImageJVirtualStack createVirtualStack( final RandomAccessibleInterval< ? > rai ) - { - final Object type = rai.randomAccess().get(); - if ( type instanceof RealType ) - return createVirtualStackRealType( cast( rai ) ); - if ( type instanceof ARGBType ) - return ImageJVirtualStackARGB.wrap( cast( rai ) ); - throw new IllegalArgumentException( "Unsupported type" ); - } - - private static < T > T cast( final Object in ) - { - @SuppressWarnings( "unchecked" ) - final - T out = ( T ) in; - return out; - } - - private static ImageJVirtualStack< ? > createVirtualStackRealType( final RandomAccessibleInterval< ? extends RealType< ? > > rai ) - { - final RealType< ? extends RealType< ? > > type = rai.randomAccess().get(); - final int bitDepth = type.getBitsPerPixel(); - final boolean isSigned = type.getMinValue() < 0; - - if ( bitDepth <= 8 && !isSigned ) - return ImageJVirtualStackUnsignedByte.wrap( rai ); - if ( bitDepth <= 16 && !isSigned ) - return ImageJVirtualStackUnsignedShort.wrap( rai ); - - // other types translated as 32-bit float data - return ImageJVirtualStackFloat.wrap( rai ); - } - - private static < T > RandomAccessibleInterval< T > ensureXYCZT( final ImgPlus< T > imgPlus ) - { - final int[] axes = getPermutation( getAxes( imgPlus ) ); - return permute( imgPlus, axes ); - } - - private static int[] getPermutation( final List< AxisType > axes ) - { - return axes.stream().mapToInt( axis -> { - final int index = imagePlusAxisOrder.indexOf( axis ); - if ( index < 0 ) - throw new IllegalArgumentException( "Unsupported axis type: " + axis ); - return index; - } ).toArray(); - } - - private static List< AxisType > getAxes( final ImgPlus< ? > imgPlus ) - { - return IntStream.range( 0, imgPlus.numDimensions() ) - .mapToObj( i -> imgPlus.axis( i ).type() ) - .collect( Collectors.toList() ); - } - - private static < T > RandomAccessibleInterval< T > permute( final ImgPlus< T > imgPlus, int[] axes ) - { - boolean inNaturalOrder = true; - final boolean[] matchedDimensions = new boolean[ 5 ]; - final long[] min = new long[ 5 ], max = new long[ 5 ]; - for ( int d = 0; d < axes.length; d++ ) - { - final int index = axes[ d ]; - matchedDimensions[ index ] = true; - min[ index ] = imgPlus.min( d ); - max[ index ] = imgPlus.max( d ); - if ( index != d ) - inNaturalOrder = false; - } - - if ( imgPlus.numDimensions() != 5 ) - inNaturalOrder = false; - if ( inNaturalOrder ) - return imgPlus; - - axes = Arrays.copyOf( axes, 5 ); - RandomAccessibleInterval< T > rai = imgPlus; - // pad the image to at least 5D - for ( int i = 0; i < 5; i++ ) - { - if ( matchedDimensions[ i ] ) - continue; - axes[ rai.numDimensions() ] = i; - min[ i ] = 0; - max[ i ] = 0; - rai = Views.addDimension( rai, 0, 0 ); - } - - // permute the axis order to XYCZT... - final MixedTransform t = new MixedTransform( rai.numDimensions(), 5 ); - t.setComponentMapping( axes ); - return Views.interval( new MixedTransformView<>( rai, t ), min, max ); - } - - private static final List< AxisType > imagePlusAxisOrder = - Arrays.asList( Axes.X, Axes.Y, Axes.CHANNEL, Axes.Z, Axes.TIME ); -} diff --git a/src/main/java/net/imglib2/imagej/ImgPlusViews.java b/src/main/java/net/imglib2/imagej/ImgPlusViews.java deleted file mode 100644 index 909f242..0000000 --- a/src/main/java/net/imglib2/imagej/ImgPlusViews.java +++ /dev/null @@ -1,220 +0,0 @@ -/*- - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * 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 net.imglib2.imagej; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.function.IntUnaryOperator; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; -import net.imagej.axis.CalibratedAxis; -import net.imglib2.RandomAccessible; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.converter.Converters; -import net.imglib2.img.Img; -import net.imglib2.img.ImgView; -import net.imglib2.type.Type; -import net.imglib2.type.numeric.ARGBType; -import net.imglib2.type.numeric.RealType; -import net.imglib2.util.Util; -import net.imglib2.view.Views; -import net.imglib2.view.composite.Composite; - -// TODO: migrate to imagej-common -public class ImgPlusViews -{ - /** - * Same as {@link Views#hyperSlice(RandomAccessible, int, long)}. But works - * on {@link ImgPlus} and manages axes information too. - */ - public static < T extends Type< T > > ImgPlus< T > hyperSlice( final ImgPlus< T > image, final int d, final long position ) - { - final IntUnaryOperator axesMapping = i -> ( i < d ) ? i : i + 1; - return newImgPlus( image, Views.hyperSlice( image.getImg(), d, position ), axesMapping ); - } - - /** - * Same as {@link Views#permute(RandomAccessible, int, int)}. But works on - * {@link ImgPlus}. But works on {@link ImgPlus} and manages axes - * information too. - */ - public static < T extends Type< T > > ImgPlus< T > permute( final ImgPlus< T > image, final int fromAxis, final int toAxis ) - { - if ( fromAxis == toAxis ) - return image; - final IntUnaryOperator axesMapping = i -> { - if ( i == fromAxis ) - return toAxis; - if ( i == toAxis ) - return fromAxis; - return i; - }; - return newImgPlus( image, Views.permute( image.getImg(), fromAxis, toAxis ), axesMapping ); - } - - /** - * Permutes the axes of the image. One axis is moved, while the order of the - * other axes is preserved. If an image has axis order XYCZT, fromAxis=2 and - * toAxis=4, then the returned axis order is XYZTC. - */ - public static < T extends Type< T > > ImgPlus< T > moveAxis( final ImgPlus< T > image, final int fromAxis, final int toAxis ) - { - if ( fromAxis == toAxis ) - return image; - final int direction = toAxis > fromAxis ? 1 : -1; - ImgPlus< T > res = image; - for ( int i = fromAxis; i != toAxis; i += direction ) - res = permute( res, i, i + direction ); - return res; - } - - /** - * Indicates it {@link #fuseColor(ImgPlus)} can by used. - */ - public static boolean canFuseColor( final ImgPlus< ? extends RealType< ? > > image ) - { - final int d = image.dimensionIndex( Axes.CHANNEL ); - return d >= 0 && image.dimension( d ) == 3; - } - - /** - * Generates a color image from a gray scale image that has a color axes of - * dimension 3. - */ - public static ImgPlus< ARGBType > fuseColor( final ImgPlus< ? extends RealType< ? > > image ) - { - final int d = image.dimensionIndex( Axes.CHANNEL ); - final RandomAccessibleInterval< ARGBType > colors = fuseColor( image.getImg(), d ); - final IntUnaryOperator axisMapping = i -> ( i < d ) ? i : i + 1; - return newImgPlus( image, colors, axisMapping ); - } - - private static RandomAccessibleInterval< ARGBType > fuseColor( final Img< ? extends RealType< ? > > image, final int d ) - { - if ( d < 0 || image.dimension( d ) != 3 ) - throw new IllegalArgumentException(); - return Converters.convert( - Views.collapse( Views.moveAxis( image, d, image.numDimensions() - 1 ) ), - ImgPlusViews::convertToColor, - new ARGBType() ); - } - - /** - * Change the axis types of an image, such that each axis is uniquely typed - * as X, Y, Z, channel or time. Existing unique axis of type: X, Y, Z, - * channel or time are preserved. - */ - public static < T > ImgPlus< T > fixAxes( final ImgPlus< T > in ) - { - final List< AxisType > newAxisTypes = fixAxes( getAxes( in ) ); - final CalibratedAxis[] newAxes = IntStream.range( 0, in.numDimensions() ).mapToObj( i -> { - final CalibratedAxis newAxis = in.axis( i ).copy(); - newAxis.setType( newAxisTypes.get( i ) ); - return newAxis; - } ).toArray( CalibratedAxis[]::new ); - return new ImgPlus<>( in.getImg(), in.getName(), newAxes ); - } - - // -- Helper methods -- - - private static < T extends Type< T > > ImgPlus< T > newImgPlus( final ImgPlus< ? > image, final RandomAccessibleInterval< T > newContent, final IntUnaryOperator axesMapping ) - { - final T type = Util.getTypeFromInterval( newContent ); - final Img< T > newImg = ImgView.wrap( newContent, image.factory().imgFactory( type ) ); - final ImgPlus< T > result = new ImgPlus<>( newImg, image.getName() ); - for ( int i = 0; i < result.numDimensions(); i++ ) - result.setAxis( image.axis( axesMapping.applyAsInt( i ) ).copy(), i ); - return result; - } - - private static void convertToColor( final Composite< ? extends RealType< ? > > in, final ARGBType out ) - { - out.set( ARGBType.rgba( toInt( in.get( 0 ) ), toInt( in.get( 1 ) ), toInt( in.get( 2 ) ), 255 ) ); - } - - private static int toInt( final RealType< ? > realType ) - { - return ( int ) realType.getRealFloat(); - } - - private static final List< AxisType > imagePlusAxisOrder = - Arrays.asList( Axes.X, Axes.Y, Axes.CHANNEL, Axes.Z, Axes.TIME ); - - private static List< AxisType > fixAxes( final List< AxisType > in ) - { - final List< AxisType > unusedAxis = new ArrayList<>( imagePlusAxisOrder ); - unusedAxis.removeAll( in ); - final Predicate< AxisType > isDuplicate = createIsDuplicatePredicate(); - final Predicate< AxisType > replaceIf = axis -> isDuplicate.test( axis ) || !imagePlusAxisOrder.contains( axis ); - final Iterator< AxisType > iterator = unusedAxis.iterator(); - final Supplier< AxisType > replacements = () -> iterator.hasNext() ? iterator.next() : Axes.unknown(); - return replaceMatches( in, replaceIf, replacements ); - } - - // NB: Package-private to allow tests. - static List< AxisType > getAxes( final ImgPlus< ? > in ) - { - return IntStream.range( 0, in.numDimensions() ) - .mapToObj( in::axis ).map( CalibratedAxis::type ) - .collect( Collectors.toList() ); - } - - // NB: Package-private to allow tests. - static < T > Predicate< T > createIsDuplicatePredicate() - { - final Set< T > before = new HashSet<>(); - return element -> { - final boolean isDuplicate = before.contains( element ); - if ( !isDuplicate ) - before.add( element ); - return isDuplicate; - }; - } - - // NB: Package-private to allow tests. - static < T > List< T > replaceMatches( final List< T > in, final Predicate< T > predicate, final Supplier< T > replacements ) - { - return in.stream().map( value -> predicate.test( value ) ? replacements.get() : value ).collect( Collectors.toList() ); - } -} diff --git a/src/main/java/net/imglib2/imagej/RAIToImagePlus.java b/src/main/java/net/imglib2/imagej/RAIToImagePlus.java index 0ce5fa3..4440336 100644 --- a/src/main/java/net/imglib2/imagej/RAIToImagePlus.java +++ b/src/main/java/net/imglib2/imagej/RAIToImagePlus.java @@ -2,35 +2,42 @@ import ij.ImagePlus; import ij.VirtualStack; -import net.imagej.ImgPlus; import net.imglib2.Dimensions; import net.imglib2.RandomAccessibleInterval; +import net.imglib2.Sampler; import net.imglib2.converter.ComplexPowerGLogFloatConverter; import net.imglib2.converter.Converter; import net.imglib2.converter.Converters; -import net.imglib2.converter.RealUnsignedByteConverter; +import net.imglib2.converter.readwrite.SamplerConverter; import net.imglib2.imagej.img.*; +import net.imglib2.img.basictypeaccess.IntAccess; +import net.imglib2.type.BooleanType; +import net.imglib2.type.NativeType; import net.imglib2.type.logic.BitType; import net.imglib2.type.numeric.*; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.util.Cast; +import net.imglib2.view.Views; +import net.imglib2.view.composite.Composite; +import net.imglib2.view.composite.GenericComposite; import java.util.concurrent.ExecutorService; +import java.util.function.Function; /** * Utilities for wrapping {@link RandomAccessibleInterval}s * into {@link ImagePlus}es. *

- * Under the hood, conversion utilizes {@link ImageJVirtualStack}, - * meaning the resulting objects are read-only unless - * {@link ImageJVirtualStack#setWritable(boolean)} is called. + * Under the hood, conversion utilizes {@link ImageJVirtualStack}, allowing + * read/write operations to the backing {@link RandomAccessibleInterval}. *

* * @author Tobis Pietzsch * @author Stephan Preibisch * @author Stephan Saalfeld + * @author Gabriel Selzer */ public class RAIToImagePlus { @@ -44,6 +51,7 @@ private RAIToImagePlus() {} * or ImagePlus.COLOR_RGB) is inferred from the generic type of the input * {@link RandomAccessibleInterval}. * + * @param element type in source image * @param img the {@link RandomAccessibleInterval} to wrap * @param title the name to assign to the wrapped {@link ImagePlus} * @param service an {@link ExecutorService} to manage asynchronous tasks @@ -58,12 +66,16 @@ public static < T extends NumericType< T > > ImagePlus wrap( final RandomAccessi if ( t instanceof ARGBType ) target = wrapRGB( Cast.unchecked( img ), title, service ); - else if ( t instanceof UnsignedByteType ) - target = wrapUnsignedByte( Cast.unchecked( img ), title, service ); - else if ( t instanceof BitType ) - target = wrapBit( Cast.unchecked( img ), title, service ); - else if ( t instanceof IntegerType ) - target = wrapUnsignedShort( Cast.unchecked( img ), title, service ); + else if ( t instanceof IntegerType ) { + final int bitDepth = ((IntegerType) t).getBitsPerPixel(); + final boolean isSigned = ((IntegerType) t).getMinValue() < 0; + if (bitDepth <= 8 && !isSigned) + target = wrapUnsignedByte(Cast.unchecked(img), title, service); + else if (bitDepth <= 16 && !isSigned) + target = wrapUnsignedShort(Cast.unchecked(img), title, service); + else + target = wrapFloat( Cast.unchecked( img ), title, service ); + } else if ( t instanceof RealType ) target = wrapFloat( Cast.unchecked( img ), title, service ); else if ( t instanceof ComplexType ) @@ -74,16 +86,6 @@ else if ( t instanceof ComplexType ) target = null; } - // Retrieve and set calibration if we can. ImgPlus has calibration and - // axis types - if ( null != target && img instanceof ImgPlus ) - { - - final ImgPlus< T > imgplus = ( ImgPlus< T > ) img; - CalibrationUtils.copyCalibrationToImagePlus( imgplus, target ); - target.setTitle( imgplus.getName() ); - } - return target; } @@ -94,6 +96,7 @@ else if ( t instanceof ComplexType ) * or ImagePlus.COLOR_RGB) is inferred from the generic type of the input * {@link RandomAccessibleInterval}. * + * @param element type in source image * @param img the {@link RandomAccessibleInterval} to wrap * @param title the name to assign to the wrapped {@link ImagePlus} * @return an {@link ImagePlus} wrapping {@code img} named {@code title} @@ -105,18 +108,36 @@ public static < T extends NumericType< T > > ImagePlus wrap( final RandomAccessi /** * Create a single channel 32-bit float {@link ImagePlus} from a - * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * {@link RandomAccessibleInterval} using a default {@link Converter}. + * + * @param element type in source image + * @param img the data to wrap. + * @param title the title to set on the resulting {@link ImagePlus}. + * @param service an {@link ExecutorService} used for processing. + * @return an RGB {@link ImagePlus} wrapping {@code img} */ public static < T extends RealType< T > > ImagePlus wrapFloat( final RandomAccessibleInterval< T > img, final String title, final ExecutorService service ) { - final ImageJVirtualStackFloat stack = ImageJVirtualStackFloat.wrap( img ); - stack.setExecutorService( service ); - return makeImagePlus( img, stack, title ); + return internalWrap( // + img, // + ImageJVirtualStackFloat::wrap, // + title, // + service // + ); } + /** + * Create a single channel 32-bit float {@link ImagePlus} from a + * {@link RandomAccessibleInterval} using a default {@link Converter}. + * + * @param element type in source image + * @param img the data to wrap. + * @param title the title to set on the resulting {@link ImagePlus}. + * @return an RGB {@link ImagePlus} wrapping {@code img} + */ public static < T extends RealType< T > > ImagePlus wrapFloat( final RandomAccessibleInterval< T > img, final String title ) @@ -127,6 +148,13 @@ public static < T extends RealType< T > > ImagePlus wrapFloat( /** * Create a single channel 32-bit float {@link ImagePlus} from a * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the data to wrap. + * @param converter a {@link Converter} turning {@link T} into a an {@link FloatType} + * @param title the title to set on the resulting {@link ImagePlus}. + * @param service an {@link ExecutorService} used for processing. + * @return an RGB {@link ImagePlus} wrapping {@code img} */ public static < T > ImagePlus wrapFloat( final RandomAccessibleInterval< T > img, @@ -134,10 +162,24 @@ public static < T > ImagePlus wrapFloat( final String title, final ExecutorService service ) { - final ImageJVirtualStackFloat stack = new ImageJVirtualStackFloat( img, converter, service ); - return makeImagePlus( img, stack, title ); + return internalWrap( // + img, // + rai -> new ImageJVirtualStackFloat(rai, converter, service), // + title, // + service // + ); } + /** + * Create a single channel 32-bit float {@link ImagePlus} from a + * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the data to wrap. + * @param converter a {@link Converter} turning {@link T} into a an {@link FloatType} + * @param title the title to set on the resulting {@link ImagePlus}. + * @return an RGB {@link ImagePlus} wrapping {@code img} + */ public static < T > ImagePlus wrapFloat( final RandomAccessibleInterval< T > img, final Converter< T, FloatType > converter, @@ -148,81 +190,116 @@ public static < T > ImagePlus wrapFloat( /** * Create a 24bit RGB {@link ImagePlus} from a - * {@link RandomAccessibleInterval} a using a default (identity) - * {@link Converter}. + * {@link RandomAccessibleInterval} with {@link ARGBType} elements. + * + * @param img the data to wrap. + * @param title the title to set on the resulting {@link ImagePlus}. + * @param service an {@link ExecutorService} used for processing. + * @return an RGB {@link ImagePlus} wrapping {@code img} */ public static ImagePlus wrapRGB( final RandomAccessibleInterval< ARGBType > img, final String title, - final ExecutorService service ) + final ExecutorService service) { - final ImageJVirtualStackARGB stack = ImageJVirtualStackARGB.wrap( img ); - stack.setExecutorService(service); - return makeImagePlus( img, stack, title ); + return internalWrap( // + img, // + ImageJVirtualStackARGB::wrap, // + title, // + service // + ); } - public static ImagePlus wrapRGB( final RandomAccessibleInterval< ARGBType > img, final String title ) - { - return wrapRGB( img, title, null ); + /** + * Create a 24bit RGB {@link ImagePlus} wrapping a + * {@link RandomAccessibleInterval} with {@link ARGBType} elements. + * + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an RGB {@link ImagePlus} wrapping {@code img} + */ + public static ImagePlus wrapRGB( final RandomAccessibleInterval< ARGBType > img, final String title) { + return wrapRGB(img, title, null); } /** * Create a 24bit RGB {@link ImagePlus} from a - * {@link RandomAccessibleInterval} a using a custom {@link Converter}. + * {@link RandomAccessibleInterval} a using a default {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an RGB {@link ImagePlus} wrapping {@code img} */ - public static < T > ImagePlus wrapRGB( final RandomAccessibleInterval< T > img, final Converter< T, ARGBType > converter, final String title, - final ExecutorService service ) + public static < T extends RealType> ImagePlus convertRGB( final RandomAccessibleInterval< T > img, final String title ) { - return wrapRGB( Converters.convert( img, converter, new ARGBType() ), title, service ); - } + // Currently assumes 5d, XYCZT + final boolean hasAlpha = img.dimension(2) != 3; + RandomAccessibleInterval permuted = Views.moveAxis(img, 2, img.numDimensions() - 1); - public static < T > ImagePlus wrapRGB( final RandomAccessibleInterval< T > img, final Converter< T, ARGBType > converter, final String title ) - { - return wrapRGB( img, converter, title, null ); + RandomAccessibleInterval> collapsed = Cast.unchecked(Views.collapse(permuted)); + RandomAccessibleInterval rgbRAI = Converters.convert( + collapsed, // + new RAIToImagePlus.RGBAConverter<>(hasAlpha) // + ); + + final ImagePlus imp = wrapRGB( rgbRAI, title, null ); + + final int c = 1; + final int z = img.numDimensions() > 3 ? (int) img.dimension(3) : 1; + final int t = img.numDimensions() > 4 ? (int) img.dimension(4) : 1; + imp.setDimensions(c, z, t); + + return imp; } /** * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @param service the {@link ExecutorService} used for processing. + * @return an {@link ImagePlus} wrapping {@code img} */ public static < T extends RealType< T > > ImagePlus wrapUnsignedByte( final RandomAccessibleInterval< T > img, final String title, final ExecutorService service ) { - final ImageJVirtualStackUnsignedByte stack = ImageJVirtualStackUnsignedByte.wrap( img ); - stack.setExecutorService( service ); - return makeImagePlus( img, stack, title ); - } - - public static < T extends RealType< T > > ImagePlus wrapUnsignedByte( - final RandomAccessibleInterval< T > img, - final String title ) - { - return wrapUnsignedByte( img, title, null ); + return internalWrap( // + img, // + ImageJVirtualStackUnsignedByte::wrap, // + title, // + service // + ); } /** * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a - * BitType {@link RandomAccessibleInterval} using a custom {@link Converter} - * . + * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an {@link ImagePlus} wrapping {@code img} */ - public static < T extends RealType< T > > ImagePlus wrapBit( - final RandomAccessibleInterval< T > img, - final String title, - final ExecutorService service ) - { - return wrapUnsignedByte( img, new RealUnsignedByteConverter< T >( 0, 1 ), title, service ); - } - - public static < T extends RealType< T > > ImagePlus wrapBit( + public static < T extends RealType< T > > ImagePlus wrapUnsignedByte( final RandomAccessibleInterval< T > img, final String title ) { - return wrapBit( img, title, null ); + return wrapUnsignedByte( img, title, null ); } /** * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param converter a {@link Converter} turning {@link T} into a an {@link UnsignedByteType} + * @param title the name assigned to the resulting {@link ImagePlus} + * @param service the {@link ExecutorService} used for processing. + * @return an {@link ImagePlus} wrapping {@code img} */ public static < T > ImagePlus wrapUnsignedByte( final RandomAccessibleInterval< T > img, @@ -233,6 +310,16 @@ public static < T > ImagePlus wrapUnsignedByte( return wrapUnsignedByte( Converters.convert( img, converter, new UnsignedByteType() ), title, service ); } + /** + * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a + * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param converter a {@link Converter} turning {@link T} into a an {@link UnsignedByteType} + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an {@link ImagePlus} wrapping {@code img} + */ public static < T > ImagePlus wrapUnsignedByte( final RandomAccessibleInterval< T > img, final Converter< T, UnsignedByteType > converter, @@ -245,17 +332,36 @@ public static < T > ImagePlus wrapUnsignedByte( * Create a single channel 16-bit unsigned integer {@link ImagePlus} from a * {@link RandomAccessibleInterval} using a default {@link Converter} (clamp * values to range [0, 65535]). + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @param service the {@link ExecutorService} used for processing. + * @return an {@link ImagePlus} wrapping {@code img} */ public static < T extends RealType< T > > ImagePlus wrapUnsignedShort( final RandomAccessibleInterval< T > img, final String title, final ExecutorService service ) { - final ImageJVirtualStackUnsignedShort stack = ImageJVirtualStackUnsignedShort.wrap( img ); - stack.setExecutorService( service ); - return makeImagePlus( img, stack, title ); + return internalWrap( // + img, // + ImageJVirtualStackUnsignedShort::wrap, // + title, // + service // + ); } + /** + * Create a single channel 16-bit unsigned integer {@link ImagePlus} from a + * {@link RandomAccessibleInterval} using a default {@link Converter} (clamp + * values to range [0, 65535]). + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an {@link ImagePlus} wrapping {@code img} + */ public static < T extends RealType< T > > ImagePlus wrapUnsignedShort( final RandomAccessibleInterval< T > img, final String title ) @@ -266,6 +372,13 @@ public static < T extends RealType< T > > ImagePlus wrapUnsignedShort( /** * Create a single channel 16-bit unsigned integer {@link ImagePlus} from a * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param converter a {@link Converter} turning {@link T} into a an {@link UnsignedShortType} + * @param title the name assigned to the resulting {@link ImagePlus} + * @param service the {@link ExecutorService} used for processing. + * @return an {@link ImagePlus} wrapping {@code img} */ public static < T > ImagePlus wrapUnsignedShort( final RandomAccessibleInterval< T > img, @@ -276,6 +389,16 @@ public static < T > ImagePlus wrapUnsignedShort( return wrapUnsignedShort( Converters.convert( img, converter, new UnsignedShortType() ), title, service ); } + /** + * Create a single channel 16-bit unsigned integer {@link ImagePlus} from a + * {@link RandomAccessibleInterval} using a custom {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param converter a {@link Converter} turning {@link T} into a an {@link UnsignedShortType} + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an {@link ImagePlus} wrapping {@code img} + */ public static < T > ImagePlus wrapUnsignedShort( final RandomAccessibleInterval< T > img, final Converter< T, UnsignedShortType > converter, @@ -284,6 +407,112 @@ public static < T > ImagePlus wrapUnsignedShort( return wrapUnsignedShort( img, converter, title, null ); } + /** + * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a + * {@link BooleanType} {@link RandomAccessibleInterval} using a custom + * {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @param service the {@link ExecutorService} used for processing. + * @return an {@link ImagePlus} wrapping {@code img} + * @see #wrapAndScaleBoolean(RandomAccessibleInterval, String, ExecutorService) for wrapping with scaling. + */ + public static < B extends BooleanType< B >> ImagePlus wrapBoolean( + final RandomAccessibleInterval< B > img, + final String title, + final ExecutorService service ) + { + return internalWrap( // + img, // + ImageJVirtualStackUnsignedByte::wrap, // + title, // + service // + ); + } + + /** + * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a + * {@link BooleanType} {@link RandomAccessibleInterval} using a custom + * {@link Converter}. + * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an {@link ImagePlus} wrapping {@code img} + * @see #wrapAndScaleBoolean(RandomAccessibleInterval, String) for wrapping with scaling. + */ + public static < B extends BooleanType< B >> ImagePlus wrapBoolean( + final RandomAccessibleInterval< B > img, + final String title ) + { + return wrapBoolean( img, title, null ); + } + + /** + * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a + * {@link BooleanType} {@link RandomAccessibleInterval} using a custom + * {@link Converter}. + *

+ * Note that this method scales the result such that {@code true} values in {@code img} are mapped to {@code 255} in the resulting {@code ImagePlus}. + *

+ * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @param service the {@link ExecutorService} used for processing. + * @return an {@link ImagePlus} wrapping {@code img} + * @see #wrapBoolean(RandomAccessibleInterval, String, ExecutorService) for wrapping without scaling. + */ + public static < B extends BooleanType > ImagePlus wrapAndScaleBoolean( + final RandomAccessibleInterval img, + final String title, + final ExecutorService service ) + { + return internalWrap( // + img, // + ImageJVirtualStackUnsignedByte::wrapAndScaleBoolean, // + title, // + service // + ); + } + + /** + * Create a single channel 8-bit unsigned integer {@link ImagePlus} from a + * {@link BooleanType} {@link RandomAccessibleInterval} using a custom + * {@link Converter}. + *

+ * Note that this method scales the result such that {@code true} values in {@code img} are mapped to {@code 255} in the resulting {@code ImagePlus}. + *

+ * + * @param element type in source image + * @param img the {@link RandomAccessibleInterval} to wrap + * @param title the name assigned to the resulting {@link ImagePlus} + * @return an {@link ImagePlus} wrapping {@code img} + * @see #wrapBoolean(RandomAccessibleInterval, String) for wrapping without scaling. + */ + public static < B extends BooleanType > ImagePlus wrapAndScaleBoolean( + final RandomAccessibleInterval img, + final String title ) + { + return wrapAndScaleBoolean( img, title, null ); + } + + private static > ImagePlus internalWrap( + final RandomAccessibleInterval< T > img, + final Function, ImageJVirtualStack> converter, + final String title, + final ExecutorService service ) + { + final ImageJVirtualStack stack = converter.apply(img); + stack.setExecutorService( service ); + final ImagePlus imp = makeImagePlus( img, stack, title ); + // NB: setWritable after the ImagePlus is created. Otherwise a useless stack.setPixels(...) call would be performed. + stack.setWritable( true ); + return imp; + } + private static ImagePlus makeImagePlus( final Dimensions dims, final VirtualStack stack, final String title ) { final ImagePlus imp = new ImagePlus( title, stack ); @@ -310,4 +539,127 @@ private static ImagePlus makeImagePlus( final Dimensions dims, final VirtualStac return imp; } + /** + * Wraps an {@link RandomAccessibleInterval} into an {@link ImagePlus}. The + * image can be {@link RealType} or {@link ARGBType}. The {@link ImagePlus} + * is backed by a special {@link VirtualStack}, which copies an plane + * from the given image, instead of it plane from a file. + *

+ * Only up to five dimensions are supported. Axes can might be arbitrary. The + * image title and calibration are derived from the given image. + * + * @param rai the {@link RandomAccessibleInterval} to convert + * @param title the title to assign to the output + * @return an {@link ImagePlus} wrapping {@code rai} + * @see ArrayImgToImagePlus + */ + public static ImagePlus wrapVirtualStack(final RandomAccessibleInterval< ? > rai, final String title ) + { + return wrapVirtualStack( rai, title, RAIToImagePlus::createVirtualStack ); + } + + /** + * Similar to {@link #wrapVirtualStack(RandomAccessibleInterval, String)}, but works only + * for {@link RandomAccessibleInterval}s of {@link BitType}. The pixel values + * of 0 and 1 are scaled to 0 and 255. + * + * @param rai the {@link RandomAccessibleInterval} to convert. Must contain bits. + * @param title the title to assign to the output + * @return an {@link ImagePlus} wrapping {@code rai} + * @see ArrayImgToImagePlus + */ + public static > ImagePlus wrapVirtualStackAndScaleBooleanType(final RandomAccessibleInterval< B > rai , final String title ) + { + return wrapVirtualStack( rai, title, RAIToImagePlus::createVirtualStackBools); + } + + private static < T > ImagePlus wrapVirtualStack(RandomAccessibleInterval< T > rai, final String title, final Function< RandomAccessibleInterval< T >, ImageJVirtualStack> imageStackWrapper ) + { + final ImageJVirtualStack stack = imageStackWrapper.apply( rai ); + final ImagePlus result = new ImagePlus( title, stack ); + // NB: setWritable after the ImagePlus is created. Otherwise a useless stack.setPixels(...) call would be performed. + stack.setWritable( true ); + return result; + } + + private static > ImageJVirtualStack createVirtualStackBools(final RandomAccessibleInterval< B > sorted ) + { + return ImageJVirtualStackUnsignedByte.wrapAndScaleBoolean( sorted ); + } + + private static ImageJVirtualStack createVirtualStack(final RandomAccessibleInterval< ? > rai ) + { + final Object type = rai.randomAccess().get(); + if ( type instanceof RealType ) + return createVirtualStackRealType( cast( rai ) ); + if ( type instanceof ARGBType ) + return ImageJVirtualStackARGB.wrap( cast( rai ) ); + throw new IllegalArgumentException( "Unsupported type" ); + } + + private static < T > T cast(final Object in ) + { + @SuppressWarnings( "unchecked" ) + final + T out = ( T ) in; + return out; + } + + private static ImageJVirtualStack< ? > createVirtualStackRealType(final RandomAccessibleInterval< ? extends RealType< ? > > rai ) + { + final RealType< ? extends RealType< ? > > type = rai.randomAccess().get(); + final int bitDepth = type.getBitsPerPixel(); + final boolean isSigned = type.getMinValue() < 0; + + if ( bitDepth <= 8 && !isSigned ) + return ImageJVirtualStackUnsignedByte.wrap( rai ); + if ( bitDepth <= 16 && !isSigned ) + return ImageJVirtualStackUnsignedShort.wrap( rai ); + + // other types translated as 32-bit float data + return ImageJVirtualStackFloat.wrap( rai ); + } + + private static class RGBAConverter, C extends Composite> implements SamplerConverter { + + final boolean hasAlpha; + + public RGBAConverter(final boolean hasAlpha) { + this.hasAlpha = hasAlpha; + } + + @Override + public ARGBType convert(Sampler sampler) { + return new ARGBType(new IntAccess() { + @Override + public int getValue(int index) { + Composite in = sampler.get(); + return ARGBType.rgba( // + toInt( in.get( 0 ) ), // + toInt( in.get( 1 ) ), // + toInt( in.get( 2 ) ), // + hasAlpha ? toInt( in.get( 3 ) ) : 255 // + ); + } + + @Override + public void setValue(int index, int value) { + Composite in = sampler.get(); + in.get(0).setReal(ARGBType.red(value)); + in.get(1).setReal(ARGBType.green(value)); + in.get(2).setReal(ARGBType.blue(value)); + if (hasAlpha) { + in.get(3).setReal(ARGBType.alpha(value)); + } + } + }); + } + + private int toInt( final RealType< ? > realType ) + { + return ( int ) realType.getRealFloat(); + } + } + + } diff --git a/src/main/java/net/imglib2/imagej/imageplus/ImagePlusImg.java b/src/main/java/net/imglib2/imagej/imageplus/ImagePlusImg.java index 1ebc4d5..5a9147c 100644 --- a/src/main/java/net/imglib2/imagej/imageplus/ImagePlusImg.java +++ b/src/main/java/net/imglib2/imagej/imageplus/ImagePlusImg.java @@ -162,25 +162,6 @@ public ImagePlus getImagePlus() throws ImgLibException * = ( dimensions.length >= d ) ? dimensions[ d ] : 1; return dim; } */ - /** - * Compute the minimal required number of dimensions for a given - * {@link ImagePlus}, whereas width and height are always first. - * - * E.g. a gray-scale 2d time series would have three dimensions - * [width,height,frames], a gray-scale 3d stack [width,height,depth] and a - * 2d composite image [width,height,channels] as well. A composite 3d stack - * has four dimensions [width,height,channels,depth], as a time series five - * [width,height,channels,depth,frames]. - */ - protected static long[] reduceDimensions( final ImagePlus imp ) - { - final int[] dimensions = imp.getDimensions(); - final long[] impDimensions = new long[ dimensions.length ]; - for ( int d = 0; d < dimensions.length; ++d ) - impDimensions[ d ] = dimensions[ d ]; - return reduceDimensions( impDimensions ); - } - protected static long[] reduceDimensions( final long[] impDimensions ) { /* diff --git a/src/main/java/net/imglib2/imagej/imageplus/ImagePlusImgs.java b/src/main/java/net/imglib2/imagej/imageplus/ImagePlusImgs.java deleted file mode 100644 index 4e1afa6..0000000 --- a/src/main/java/net/imglib2/imagej/imageplus/ImagePlusImgs.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * 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 net.imglib2.imagej.imageplus; - -import ij.process.ByteProcessor; -import ij.process.ColorProcessor; -import ij.process.FloatProcessor; -import ij.process.ShortProcessor; -import net.imglib2.type.Type; -import net.imglib2.type.numeric.ARGBType; -import net.imglib2.type.numeric.complex.ComplexFloatType; -import net.imglib2.type.numeric.integer.*; -import net.imglib2.type.numeric.real.FloatType; - -/** - * Convenience factory methods for creation of {@link ImagePlusImg} instances - * with the most common pixel {@link Type} variants. Keep in mind that this - * cannot be a complete collection since the number of existing pixel - * {@link Type}s may be extended. - * - * For pixel {@link Type}s T not present in this collection, use the generic - * {@link ImagePlusImgFactory#create(long[], net.imglib2.type.NativeType)}, e.g. - * - *

- * img = new ImagePlusImgFactory< MyType >.create( new long[] { 100, 200 }, new MyType() );
- * 
- * - * @author Stephan Saalfeld - */ -final public class ImagePlusImgs -{ - private ImagePlusImgs() - {} - - /** - * Create a {@link ByteImagePlus}{@code <}{@link UnsignedByteType}{@code >}. - * - *

- * (in ImageJ that would be a hyperstack of {@link ByteProcessor}s) - *

- */ - @SuppressWarnings( "unchecked" ) - static public ByteImagePlus< UnsignedByteType > unsignedBytes(final long... dim) - { - return ( ByteImagePlus< UnsignedByteType > ) new ImagePlusImgFactory<>( new UnsignedByteType() ).create( dim ); - } - - /** - * Create a {@link ByteImagePlus}{@code <}{@link ByteType}{@code >}. - * - *

- * (in ImageJ that would be a hyperstack of {@link ByteProcessor}s) - *

- */ - @SuppressWarnings( "unchecked" ) - static public ByteImagePlus< ByteType > bytes(final long... dim) - { - return ( ByteImagePlus< ByteType > ) new ImagePlusImgFactory<>( new ByteType() ).create( dim ); - } - - /** - * Create a - * {@link ShortImagePlus}{@code <}{@link UnsignedShortType}{@code >}. - * - *

- * (in ImageJ that would be a hyperstack of {@link ShortProcessor}s) - *

- */ - @SuppressWarnings( "unchecked" ) - static public ShortImagePlus< UnsignedShortType > unsignedShorts(final long... dim) - { - return ( ShortImagePlus< UnsignedShortType > ) new ImagePlusImgFactory<>( new UnsignedShortType() ).create( dim ); - } - - /** - * Create a {@link ShortImagePlus}{@code <}{@link ShortType}{@code >}. - * - *

- * (in ImageJ that would be a hyperstack of {@link ShortProcessor}s) - *

- */ - @SuppressWarnings( "unchecked" ) - static public ShortImagePlus< ShortType > shorts(final long... dim) - { - return ( ShortImagePlus< ShortType > ) new ImagePlusImgFactory<>( new ShortType() ).create( dim ); - } - - /** - * Create a {@link IntImagePlus}{@code <}{@link UnsignedIntType}{@code >}. - * - *

- * (In ImageJ that would be a hyperstack of {@link ColorProcessor}s. The - * integers, however, would be displayed as ARGB unsigned byte channels and - * thus look weird.) - *

- */ - @SuppressWarnings( "unchecked" ) - static public IntImagePlus< UnsignedIntType > unsignedInts(final long... dim) - { - return ( IntImagePlus< UnsignedIntType > ) new ImagePlusImgFactory<>( new UnsignedIntType() ).create( dim ); - } - - /** - * Create a {@link IntImagePlus}{@code <}{@link IntType}{@code >}. - * - *

- * (In ImageJ that would be a hyperstack of {@link ColorProcessor}s. The - * integers, however, would be displayed as ARGB unsigned byte channels and - * thus look weird.) - *

- */ - @SuppressWarnings( "unchecked" ) - static public IntImagePlus< IntType > ints(final long... dim) - { - return ( IntImagePlus< IntType > ) new ImagePlusImgFactory<>( new IntType() ).create( dim ); - } - - /** - * Create a {@link FloatImagePlus}{@code <}{@link FloatType}{@code >}. - * - *

- * (in ImageJ that would be a hyperstack of {@link FloatProcessor}s) - *

- */ - @SuppressWarnings( "unchecked" ) - static public FloatImagePlus< FloatType > floats(final long... dim) - { - return ( FloatImagePlus< FloatType > ) new ImagePlusImgFactory<>( new FloatType() ).create( dim ); - } - - /** - * Create an {@link IntImagePlus}{@code <}{@link ARGBType}{@code >}. - * - *

- * (in ImageJ that would be a hyperstack of {@link ColorProcessor}s) - *

- */ - @SuppressWarnings( "unchecked" ) - static public IntImagePlus< ARGBType > argbs(final long... dim) - { - return ( IntImagePlus< ARGBType > ) new ImagePlusImgFactory<>( new ARGBType() ).create( dim ); - } - - /** - * Create a - * {@link FloatImagePlus}{@code <}{@link ComplexFloatType}{@code >}. - * - *

- * (In ImageJ that would be a hyperstack of {@link FloatProcessor}s with - * real and imaginary numbers interleaved in the plane. That means it would - * look weird.) - *

- */ - @SuppressWarnings( "unchecked" ) - static public FloatImagePlus< ComplexFloatType > complexFloats( final long... dim ) - { - return ( FloatImagePlus< ComplexFloatType > ) new ImagePlusImgFactory<>( new ComplexFloatType() ).create( dim ); - } -} diff --git a/src/main/java/net/imglib2/imagej/img/AbstractVirtualStack.java b/src/main/java/net/imglib2/imagej/img/AbstractVirtualStack.java index 09124fc..23dfa31 100644 --- a/src/main/java/net/imglib2/imagej/img/AbstractVirtualStack.java +++ b/src/main/java/net/imglib2/imagej/img/AbstractVirtualStack.java @@ -114,7 +114,10 @@ private int toZeroBasedIndex( int n ) return ( n - 1 ) + offset; } - /** If this method return's false the methods {@link #setPixels} and {@link #setVoxels} will have no effect. */ + /** + * If this method return's false the methods {@link #setPixels} and {@link #setVoxels} will have no effect. + * @return true if this {@link VirtualStack} is writable + */ protected boolean isWritable() { return true; } @@ -126,6 +129,7 @@ protected boolean isWritable() { * The type of the array must be byte[], short[], int[] or float[] and must match the type indicated by {@link #getBitDepth()}. * * @param index Zero based index of the plane. (Warning {@link VirtualStack#getPixels(int)} uses one base indices). + * @return a primitive array containing the backing data of this {@link VirtualStack} * @see ImageStack#getBitDepth() */ protected abstract Object getPixelsZeroBasedIndex( int index ); @@ -149,6 +153,7 @@ protected boolean isWritable() { * fot 32-bit images. * * @param index Zero based index of the plane. (Warning {@link VirtualStack#getPixels(int)} uses one base indices). + * @return a 2D {@code RandomAccessibleInterval} */ protected RandomAccessibleInterval< ? > getSliceZeroBasedIndex( int index ) { diff --git a/src/main/java/net/imglib2/imagej/img/ArrayImgToImagePlus.java b/src/main/java/net/imglib2/imagej/img/ArrayImgToImagePlus.java index fa18f1c..1ab6b51 100644 --- a/src/main/java/net/imglib2/imagej/img/ArrayImgToImagePlus.java +++ b/src/main/java/net/imglib2/imagej/img/ArrayImgToImagePlus.java @@ -34,28 +34,15 @@ package net.imglib2.imagej.img; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; -import net.imagej.axis.CalibratedAxis; -import net.imglib2.imagej.ImgPlusToImagePlus; -import net.imglib2.imagej.ImgPlusViews; -import net.imglib2.img.Img; +import ij.ImagePlus; +import ij.process.ImageProcessor; import net.imglib2.img.array.ArrayImg; import net.imglib2.img.basictypeaccess.array.ArrayDataAccess; -import net.imglib2.type.NativeType; import net.imglib2.type.numeric.ARGBType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; -import ij.ImagePlus; -import ij.process.ImageProcessor; - public class ArrayImgToImagePlus { private ArrayImgToImagePlus() @@ -64,62 +51,53 @@ private ArrayImgToImagePlus() } /** - * Indicates if {@link #wrap(ImgPlus)} wrap} supports the image. + * Indicates if {@link #wrap(net.imglib2.img.array.ArrayImg, String)} wrap} supports the image. * + * @param obj an {@link Object} that may be supported by {@code wrap} + * @return {@code true} iff {@code obj} can be converted into an {@link ImagePlus}. * @see PlanarImgToImagePlus - * @see ImgPlusToImagePlus + * @see CellImgToImagePlus */ - public static boolean isSupported( ImgPlus< ? > imgPlus ) + public static boolean isSupported( Object obj ) { - imgPlus = ImgPlusViews.fixAxes( imgPlus ); - return imgPlus.getImg() instanceof ArrayImg && - imgPlus.numDimensions() == 2 && - checkAxis( getAxes( imgPlus ) ) && - ImageProcessorUtils.isSupported( ( NativeType< ? > ) imgPlus.randomAccess().get() ); + if (! (obj instanceof ArrayImg)) + return false; + ArrayImg img = (ArrayImg) obj; + Object storageArray = img.update(null); + if (! (storageArray instanceof ArrayDataAccess)) + return false; + return img.numDimensions() == 2 && + ImageProcessorUtils.isSupported( img.getType() ); } /** - * Takes an {@link ImgPlus} (IJ2) and wraps it into an {@link ImagePlus} - * (IJ1). This only works when {@link ImgPlus} is backed by a two + * Takes an {@link ArrayImg} and wraps it into an {@link ImagePlus} + * (IJ1). This only works when {@link ArrayImg} is backed by a two * dimensional {@link ArrayImg}. Type of the image must be * {@link UnsignedByteType}, {@link UnsignedShortType}, {@link ARGBType} or * {@link FloatType}. *

* The returned {@link ImagePlus} uses the same pixel buffer as the given * image. Changes to the {@link ImagePlus} are therefore correctly reflected - * in the {@link ImgPlus}. The title and calibration are derived from the + * in the {@link ArrayImg}. The title and calibration are derived from the * given image. *

- * Use {@link #isSupported(ImgPlus)} to check if an {@link ImagePlus} is + * Use {@link #isSupported(Object)} to check if an {@link ImagePlus} is * supported. * + * @param img the {@link ArrayImg} to convert + * @param name the {@link String} title to assign to the result + * @return an {@link ImagePlus} wrapping the data in {@code img} * @see PlanarImgToImagePlus - * @see ImgPlusToImagePlus + * @see CellImgToImagePlus */ - public static ImagePlus wrap( ImgPlus< ? > imgPlus ) + public static ImagePlus wrap(ArrayImg< ?, ? extends ArrayDataAccess > img, String name) { - imgPlus = ImgPlusViews.fixAxes( imgPlus ); - final Img< ? > img = imgPlus.getImg(); - if ( !( img instanceof ArrayImg ) ) - throw new IllegalArgumentException( "Expecting ArrayImg" ); - final ArrayImg< ?, ArrayDataAccess< ? > > arrayImg = ( ArrayImg< ?, ArrayDataAccess< ? > > ) img; final int sizeX = ( int ) img.dimension( 0 ); final int sizeY = ( int ) img.dimension( 1 ); - final Object pixels = arrayImg.update( null ).getCurrentStorageArray(); + final Object pixels = img.update( null ).getCurrentStorageArray(); final ImageProcessor processor = ImageProcessorUtils.createImageProcessor( pixels, sizeX, sizeY, null ); - final ImagePlus imagePlus = new ImagePlus( imgPlus.getName(), processor ); - CalibrationUtils.copyCalibrationToImagePlus( imgPlus, imagePlus ); - return imagePlus; - } - - private static boolean checkAxis( final List< AxisType > axes ) - { - return axes.size() == 2 && axes.get( 0 ) == Axes.X && axes.get( 1 ) == Axes.Y; - } - - private static List< AxisType > getAxes( final ImgPlus< ? > img ) - { - return IntStream.range( 0, img.numDimensions() ).mapToObj( img::axis ).map( CalibratedAxis::type ).collect( Collectors.toList() ); + return new ImagePlus( name, processor ); } } diff --git a/src/main/java/net/imglib2/imagej/img/CalibrationUtils.java b/src/main/java/net/imglib2/imagej/img/CalibrationUtils.java deleted file mode 100644 index bc222c0..0000000 --- a/src/main/java/net/imglib2/imagej/img/CalibrationUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/*- - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * 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 net.imglib2.imagej.img; - -import java.util.ArrayList; -import java.util.List; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.CalibratedAxis; -import net.imagej.axis.DefaultLinearAxis; - -import ij.ImagePlus; -import ij.measure.Calibration; - -public class CalibrationUtils -{ - /** - * Sets the {@link Calibration} data on the provided {@link ImagePlus}. - */ - public static void copyCalibrationToImagePlus( final ImgPlus< ? > imgPlus, final ImagePlus imp ) - { - final Calibration calibration = imp.getCalibration(); - final int xIndex = imgPlus.dimensionIndex( Axes.X ); - final int yIndex = imgPlus.dimensionIndex( Axes.Y ); - final int zIndex = imgPlus.dimensionIndex( Axes.Z ); - final int tIndex = imgPlus.dimensionIndex( Axes.TIME ); - - if ( xIndex >= 0 ) - { - calibration.pixelWidth = imgPlus.averageScale( xIndex ); - final CalibratedAxis axis = imgPlus.axis( xIndex ); - calibration.xOrigin = axis.calibratedValue( 0 ); - calibration.setXUnit( axis.unit() ); - } - if ( yIndex >= 0 ) - { - calibration.pixelHeight = imgPlus.averageScale( yIndex ); - final CalibratedAxis axis = imgPlus.axis( yIndex ); - calibration.yOrigin = axis.calibratedValue( 0 ); - calibration.setYUnit( axis.unit() ); - } - if ( zIndex >= 0 ) - { - calibration.pixelDepth = imgPlus.averageScale( zIndex ); - final CalibratedAxis axis = imgPlus.axis( zIndex ); - calibration.zOrigin = axis.calibratedValue( 0 ); - calibration.setZUnit( axis.unit() ); - } - if ( tIndex >= 0 ) - { - calibration.frameInterval = imgPlus.averageScale( tIndex ); - calibration.setTimeUnit( imgPlus.axis( tIndex ).unit() ); - } - } - - public static CalibratedAxis[] getNonTrivialAxes( final ImagePlus image ) - { - final List< CalibratedAxis > result = new ArrayList<>(); - final Calibration calibration = image.getCalibration(); - result.add( new DefaultLinearAxis( Axes.X, calibration.getXUnit(), calibration.pixelWidth, calibration.xOrigin ) ); - result.add( new DefaultLinearAxis( Axes.Y, calibration.getYUnit(), calibration.pixelHeight, calibration.yOrigin ) ); - if ( image.getNChannels() > 1 ) - result.add( new DefaultLinearAxis( Axes.CHANNEL ) ); - if ( image.getNSlices() > 1 ) - result.add( new DefaultLinearAxis( Axes.Z, calibration.getZUnit(), calibration.pixelDepth, calibration.zOrigin ) ); - if ( image.getNFrames() > 1 ) - result.add( new DefaultLinearAxis( Axes.TIME, calibration.getTimeUnit(), calibration.frameInterval ) ); - return result.toArray( new CalibratedAxis[ 0 ] ); - } -} diff --git a/src/main/java/net/imglib2/imagej/img/CellImgToImagePlus.java b/src/main/java/net/imglib2/imagej/img/CellImgToImagePlus.java index de7e473..292ba0c 100644 --- a/src/main/java/net/imglib2/imagej/img/CellImgToImagePlus.java +++ b/src/main/java/net/imglib2/imagej/img/CellImgToImagePlus.java @@ -35,17 +35,17 @@ package net.imglib2.imagej.img; import ij.ImagePlus; -import net.imagej.ImgPlus; import net.imglib2.Dimensions; import net.imglib2.FinalDimensions; import net.imglib2.RandomAccess; import net.imglib2.RandomAccessible; -import net.imglib2.imagej.ImgPlusToImagePlus; import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImg; import net.imglib2.img.basictypeaccess.array.ArrayDataAccess; import net.imglib2.img.cell.AbstractCellImg; import net.imglib2.img.cell.Cell; import net.imglib2.img.cell.CellGrid; +import net.imglib2.img.cell.CellImg; import net.imglib2.img.planar.PlanarImg; import net.imglib2.type.NativeType; import net.imglib2.type.NativeTypeFactory; @@ -62,7 +62,6 @@ * cells contains exactly one image plane), and certain pixel types: * UnsignedByteType, UnsignedShortType, ARGBType and FloatType. * - * @see ImgPlusToImagePlus * @see PlanarImgToImagePlus * @see ArrayImgToImagePlus */ @@ -70,18 +69,25 @@ public class CellImgToImagePlus { /** - * Returns true if {@link #wrap(ImgPlus)} supports the given image. + * Returns true, if {@link #wrap(CellImg, String)} supports the given image. + * @param obj an {@link Object} that may be supported by {@code wrap} + * @return {@code true} iff {@code obj} can be converted into an {@link ImagePlus}. */ - public static boolean isSupported( ImgPlus< ? > imgPlus ) + public static boolean isSupported( Object obj ) { - return isCellImgWithPlanarCells( imgPlus.getImg() ) && - PlanarImgToImagePlus.isSupported( toPlanarImgPlus( imgPlus ) ); + if (!(obj instanceof CellImg)) + return false; + CellImg img = (CellImg) obj; + if (!(img.update(img.cursor()) instanceof ArrayDataAccess)) + return false; + return isCellImgWithPlanarCells( img ) && + PlanarImgToImagePlus.isSupported( toPlanarImgPlus( (CellImg) img ) ); } - private static boolean isCellImgWithPlanarCells( Img< ? > imgPlus ) + private static boolean isCellImgWithPlanarCells( Img< ? > img ) { - return ( imgPlus instanceof AbstractCellImg ) && - areCellsPlanar( ( ( AbstractCellImg ) imgPlus ).getCellGrid() ); + return ( img instanceof AbstractCellImg ) && + areCellsPlanar( ( ( AbstractCellImg ) img ).getCellGrid() ); } private static boolean areCellsPlanar( CellGrid cellGrid ) @@ -100,21 +106,16 @@ private static boolean areCellsPlanar( CellGrid cellGrid ) * by an {@link AbstractCellImg} with planar cells. The pixel type must be * UnsignedByte-, UnsignedShort-, ARGB- or FloatType. First two axes must be * X and Y (or unknown). + * @param img the {@link CellImg} to convert + * @param name the {@link String} title to assign to the result + * @return an {@link ImagePlus} wrapping the data in {@code img} */ - public static ImagePlus wrap( ImgPlus< ? > imgPlus ) + public static , A extends ArrayDataAccess> ImagePlus wrap( CellImg< T, A > img, String name ) { - return PlanarImgToImagePlus.wrap( toPlanarImgPlus( imgPlus ) ); + return PlanarImgToImagePlus.wrap( toPlanarImgPlus( img ), name ); } - private static < T > ImgPlus< T > toPlanarImgPlus( ImgPlus< T > image ) - { - if ( !isCellImgWithPlanarCells( image.getImg() ) ) - throw new IllegalArgumentException( "ERROR: Image must be a CellImg, with planar cells." ); - return new ImgPlus< T >( toPlanar( ( AbstractCellImg ) image.getImg() ), image ); - } - - private static < T extends NativeType< T >, A extends ArrayDataAccess< A > > PlanarImg< ?, ? > - toPlanar( AbstractCellImg< T, A, ?, ? > cellImage ) + private static < T extends NativeType, A extends ArrayDataAccess> PlanarImg< T, A > toPlanarImgPlus(CellImg< T, A > cellImage ) { final long[] dim = Intervals.dimensionsAsLongArray( cellImage ); final T type = cellImage.getType().createVariable(); diff --git a/src/main/java/net/imglib2/imagej/img/ImageJVirtualStack.java b/src/main/java/net/imglib2/imagej/img/ImageJVirtualStack.java index 7e14274..caebfbc 100644 --- a/src/main/java/net/imglib2/imagej/img/ImageJVirtualStack.java +++ b/src/main/java/net/imglib2/imagej/img/ImageJVirtualStack.java @@ -149,6 +149,7 @@ public void setExecutorService( ExecutorService service ) * or {@link #setVoxels} will be copied to the wrapped {@link RandomAccessibleInterval}. * Please note: The {@link ImageProcessor} cannot be used to persistently change the image * content. + * @param writable iff true, changes to this {@link ImageStack} will be reflected in the wrapped image. */ public void setWritable( final boolean writable ) { @@ -222,7 +223,10 @@ protected RandomAccessibleInterval< T > getSliceZeroBasedIndex( int index ) return origin; } - /** Get the underlying ImgLib2 {@link RandomAccessibleInterval}. */ + /** + * Get the underlying ImgLib2 {@link RandomAccessibleInterval}. + * @return the underlying {@link RandomAccessibleInterval} + */ public RandomAccessibleInterval< T > getSource() { return source; diff --git a/src/main/java/net/imglib2/imagej/img/ImageJVirtualStackUnsignedByte.java b/src/main/java/net/imglib2/imagej/img/ImageJVirtualStackUnsignedByte.java index 9923010..fdcbc33 100644 --- a/src/main/java/net/imglib2/imagej/img/ImageJVirtualStackUnsignedByte.java +++ b/src/main/java/net/imglib2/imagej/img/ImageJVirtualStackUnsignedByte.java @@ -41,10 +41,8 @@ import net.imglib2.converter.readwrite.SamplerConverter; import net.imglib2.img.basictypeaccess.ByteAccess; import net.imglib2.type.BooleanType; -import net.imglib2.type.logic.BitType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedByteType; -import net.imglib2.util.Util; import java.util.concurrent.ExecutorService; @@ -66,7 +64,7 @@ public class ImageJVirtualStackUnsignedByte extends ImageJVirtualStack< Unsigned return Converters.convert(source, new ToUnsignedByteSamplerConverter( source.getType() ) ); } - public static ImageJVirtualStackUnsignedByte wrapAndScaleBitType( final RandomAccessibleInterval< BitType > source ) + public static > ImageJVirtualStackUnsignedByte wrapAndScaleBoolean(final RandomAccessibleInterval< B > source ) { return new ImageJVirtualStackUnsignedByte( Converters.convert(source, new ToBitByteSamplerConverter()) ); } diff --git a/src/main/java/net/imglib2/imagej/img/ImageProcessorUtils.java b/src/main/java/net/imglib2/imagej/img/ImageProcessorUtils.java index b7514a0..c6b7181 100644 --- a/src/main/java/net/imglib2/imagej/img/ImageProcessorUtils.java +++ b/src/main/java/net/imglib2/imagej/img/ImageProcessorUtils.java @@ -82,7 +82,9 @@ public static ImageProcessor createImageProcessor( final Object pixels, final in } /** - * Returns true if the given ImgLib2 pixel type is also supported by ImageJ1. + * Returns true if the given ImgLib2 pixel type is also supported by the original ImageJ. + * @param type an ImgLib2 pixel type + * @return true iff that pixel type is also supported in the original ImageJ. */ public static boolean isSupported( final Type< ? > type ) { diff --git a/src/main/java/net/imglib2/imagej/img/PlanarImgToImagePlus.java b/src/main/java/net/imglib2/imagej/img/PlanarImgToImagePlus.java index 640676e..700faa4 100644 --- a/src/main/java/net/imglib2/imagej/img/PlanarImgToImagePlus.java +++ b/src/main/java/net/imglib2/imagej/img/PlanarImgToImagePlus.java @@ -34,40 +34,22 @@ package net.imglib2.imagej.img; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.IntUnaryOperator; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; -import net.imagej.axis.CalibratedAxis; +import ij.ImagePlus; +import ij.VirtualStack; import net.imglib2.Interval; -import net.imglib2.imagej.ImgPlusViews; -import net.imglib2.imagej.ImgPlusToImagePlus; -import net.imglib2.img.Img; import net.imglib2.img.basictypeaccess.PlanarAccess; -import net.imglib2.img.basictypeaccess.array.ArrayDataAccess; -import net.imglib2.img.basictypeaccess.array.ByteArray; -import net.imglib2.img.basictypeaccess.array.FloatArray; -import net.imglib2.img.basictypeaccess.array.IntArray; -import net.imglib2.img.basictypeaccess.array.ShortArray; +import net.imglib2.img.basictypeaccess.array.*; +import net.imglib2.img.cell.CellImg; import net.imglib2.img.planar.PlanarImg; -import net.imglib2.type.NativeType; import net.imglib2.type.Type; import net.imglib2.type.numeric.ARGBType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.util.Cast; -import net.imglib2.util.IntervalIndexer; -import ij.ImagePlus; -import ij.VirtualStack; +import java.util.function.IntUnaryOperator; +import java.util.stream.IntStream; /** * Utility class to convert a {@link PlanarImg} to an {@link ImagePlus} @@ -75,7 +57,6 @@ * UnsignedByteType, UnsignedShortType, ARGBType and FloatType. * * @see ArrayImgToImagePlus - * @see ImgPlusToImagePlus * @see CellImgToImagePlus */ public class PlanarImgToImagePlus extends AbstractVirtualStack @@ -84,56 +65,46 @@ public class PlanarImgToImagePlus extends AbstractVirtualStack // static /** - * Returns true, if {@link #wrap(ImgPlus)} supports the given image. + * Returns true, if {@link #wrap(PlanarImg, String)} supports the given image. + * @param obj an {@link Object} that may be supported by {@code wrap} + * @return {@code true} iff {@code obj} can be converted into an {@link ImagePlus}. */ - public static boolean isSupported( ImgPlus< ? > imgPlus ) + public static boolean isSupported( Object obj ) { - imgPlus = ImgPlusViews.fixAxes( imgPlus ); - return imgPlus.getImg() instanceof PlanarImg && - checkAxisOrder( getAxes( imgPlus ) ) && - ImageProcessorUtils.isSupported( ( NativeType< ? > ) imgPlus.randomAccess().get() ); + if (!(obj instanceof PlanarImg)) + return false; + PlanarImg img = (PlanarImg) obj; + return ImageProcessorUtils.isSupported(img.getType()); } /** - * Wraps an {@link ImgPlus}, that is backed by an {@link PlanarImg} into and - * {@link ImagePlus}. The returned {@link ImagePlus} uses the same pixel - * buffer as the given image. Changes to the {@link ImagePlus} are therefore - * reflected in the {@link ImgPlus}. + * Wraps a {@link PlanarImg} into an {@link ImagePlus}. The returned + * {@link ImagePlus} uses the same pixel buffer as the given image. + * Changes to the {@link ImagePlus} are therefore reflected in the + * {@link PlanarImg}. *

* The image must be {@link UnsignedByteType}, {@link UnsignedShortType}, * {@link ARGBType} or {@link FloatType}. Only up to five dimensions are - * support. Axes oder must start with X, Y axes. Channel, Time and Z axes - * might follow in arbitrary order. The image title and calibration are - * derived from the given image. + * supported. Axes are presumed to start with X, Y. Channel, Z, and Time + * axes are assumed to map to any following dimensions, in that order. *

- * Use {@link #isSupported(ImgPlus)} to check if an {@link ImagePlus} is + * Use {@link #isSupported(Object)} to check if the {@link PlanarImg} is * supported. * - * @see #isSupported(ImgPlus) + * @param img the {@link PlanarImg} to convert + * @param name the {@link String} title to assign to the result + * @return an {@link ImagePlus} wrapping the data in {@code img} + * @see #isSupported(Object) */ - public static ImagePlus wrap( ImgPlus< ? > imgPlus ) + public static ImagePlus wrap(PlanarImg< ?, ? > img, String name ) { - imgPlus = ImgPlusViews.fixAxes( imgPlus ); - final Img< ? > img = imgPlus.getImg(); - if ( !( img instanceof PlanarImg ) ) - throw new IllegalArgumentException( "Image must be a PlanarImg." ); - final IntUnaryOperator indexer = getIndexer( imgPlus ); - final VirtualStack stack = new PlanarImgToImagePlus( ( PlanarImg< ?, ? > ) img, indexer ); - final ImagePlus imagePlus = new ImagePlus( imgPlus.getName(), stack ); - imagePlus.setDimensions( dimension( imgPlus, Axes.CHANNEL ), dimension( imgPlus, Axes.Z ), dimension( imgPlus, Axes.TIME ) ); - CalibrationUtils.copyCalibrationToImagePlus( imgPlus, imagePlus ); - return imagePlus; - } - - private static int dimension( final ImgPlus< ? > imgPlus, final AxisType axisType ) - { - final int index = imgPlus.dimensionIndex( axisType ); - return index < 0 ? 1 : ( int ) imgPlus.dimension( index ); - } - - public static VirtualStack wrap( final PlanarImg< ?, ? > img ) - { - return new PlanarImgToImagePlus( img, x -> x ); + final VirtualStack stack = new PlanarImgToImagePlus( img, x -> x ); + final ImagePlus imp = new ImagePlus(name, stack); + final int c = img.numDimensions() > 2 ? (int) img.dimension(2) : 1; + final int z = img.numDimensions() > 3 ? (int) img.dimension(3) : 1; + final int t = img.numDimensions() > 4 ? (int) img.dimension(4) : 1; + imp.setDimensions(c, z, t); + return imp; } // fields @@ -214,71 +185,4 @@ private static int getBitDepth( final Type< ? > type ) throw new IllegalArgumentException( "unsupported type" ); } - private static IntUnaryOperator getIndexer( final ImgPlus< ? > imgPlus ) - { - final List< AxisType > axes = getAxes( imgPlus ); - if ( !checkAxisOrder( axes ) ) - throw new IllegalArgumentException( "Unsupported axis order, first axis must be X, second axis must be Y, and then optionally, arbitrary ordered: channel, Z and time." ); - if ( inPreferredOrder( axes ) ) - return x -> x; - final int[] stackSizes = { dimension( imgPlus, Axes.CHANNEL ), dimension( imgPlus, Axes.Z ), dimension( imgPlus, Axes.TIME ) }; - final int channelSkip = getSkip( imgPlus, Axes.CHANNEL ); - final int zSkip = getSkip( imgPlus, Axes.Z ); - final int timeSkip = getSkip( imgPlus, Axes.TIME ); - return stackIndex -> { - final int[] stackPosition = new int[ 3 ]; - IntervalIndexer.indexToPosition( stackIndex, stackSizes, stackPosition ); - return channelSkip * stackPosition[ 0 ] + zSkip * stackPosition[ 1 ] + timeSkip * stackPosition[ 2 ]; - }; - } - - private static List< AxisType > getAxes( final ImgPlus< ? > img ) - { - return IntStream.range( 0, img.numDimensions() ).mapToObj( img::axis ).map( CalibratedAxis::type ).collect( Collectors.toList() ); - } - - private static boolean checkAxisOrder( final List< AxisType > axes ) - { - return axes.size() >= 2 && axes.size() <= 5 && testUnique( axes ) && - axes.get( 0 ) == Axes.X && axes.get( 1 ) == Axes.Y && - axes.stream().allMatch( ALLOWED_AXES::contains ); - } - - private static final List< AxisType > ALLOWED_AXES = Arrays.asList( Axes.X, Axes.Y, Axes.CHANNEL, Axes.Z, Axes.TIME ); - - private static boolean inPreferredOrder( final List< AxisType > axes ) - { - for ( int i = 0; i < axes.size() - 1; i++ ) - if ( preferredPosition( axes.get( i ) ) >= preferredPosition( axes.get( i + 1 ) ) ) - return false; - return true; - } - - private static int preferredPosition( final AxisType axisType ) - { - if ( axisType == Axes.X ) - return 0; - if ( axisType == Axes.Y ) - return 1; - if ( axisType == Axes.CHANNEL ) - return 2; - if ( axisType == Axes.Z ) - return 3; - if ( axisType == Axes.TIME ) - return 4; - throw new IllegalArgumentException( "unknown axis" ); - } - - private static int getSkip( final ImgPlus< ? > imgPlus, final AxisType axis ) - { - final int channelIndex = imgPlus.dimensionIndex( axis ); - return IntStream.range( 2, channelIndex ).map( i -> ( int ) imgPlus.dimension( i ) ).reduce( 1, ( a, b ) -> a * b ); - } - - private static < T > boolean testUnique( final List< T > list ) - { - final Set< T > set = new HashSet<>( list ); - return set.size() == list.size(); - } - } diff --git a/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2Benchmark.java b/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2Benchmark.java index b80eac6..ad6e4ec 100644 --- a/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2Benchmark.java +++ b/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2Benchmark.java @@ -33,7 +33,6 @@ */ package net.imglib2.imagej; -import net.imagej.ImgPlus; import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.Img; import net.imglib2.type.numeric.integer.UnsignedByteType; @@ -62,41 +61,41 @@ public class ImagePlusToImgLib2Benchmark @Benchmark public void wrapSmall() { - net.imglib2.imagej.ImagePlusToImgPlus.wrap( small ); + net.imglib2.imagej.ImagePlusToImg.wrapCached( small ); } @Benchmark public void wrapDeep() { - net.imglib2.imagej.ImagePlusToImgPlus.wrap( deep ); + net.imglib2.imagej.ImagePlusToImg.wrapCached( deep ); } @Benchmark public void wrapWide() { - ImagePlusToImgPlus.wrap( wide ); + ImagePlusToImg.wrapCached( wide ); } @Benchmark public void wrapSmallOld() { - net.imglib2.imagej.ImagePlusToImg.wrap( small ); + net.imglib2.imagej.ImagePlusToImg.wrapDirect( small ); } @Benchmark public void wrapDeepOld() { - net.imglib2.imagej.ImagePlusToImg.wrap( deep ); + net.imglib2.imagej.ImagePlusToImg.wrapDirect( deep ); } @Benchmark public void wrapWideOld() { - net.imglib2.imagej.ImagePlusToImg.wrap( wide ); + net.imglib2.imagej.ImagePlusToImg.wrapDirect( wide ); } - private final ImgPlus< UnsignedByteType > wrapped = net.imglib2.imagej.ImagePlusToImgPlus.wrapByte( imageForIteration ); - private final Img< UnsignedByteType > wrappedOld = net.imglib2.imagej.ImagePlusToImg.wrapByte( imageForIteration ); + private final Img< UnsignedByteType > wrapped = net.imglib2.imagej.ImagePlusToImg.wrapByteCached( imageForIteration ); + private final Img< UnsignedByteType > wrappedOld = net.imglib2.imagej.ImagePlusToImg.wrapByteDirect( imageForIteration ); @Benchmark public void iterateWrapped() diff --git a/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2PerformanceTest.java b/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2PerformanceTest.java index 28956a4..56d074d 100644 --- a/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2PerformanceTest.java +++ b/src/test/java/net/imglib2/imagej/ImagePlusToImgLib2PerformanceTest.java @@ -57,7 +57,7 @@ public void testIterateVirtualStack() { final AtomicInteger counter = new AtomicInteger(); final ImagePlus image = countingImagePlus( counter ); - final Img< ? extends RealType< ? > > img = net.imglib2.imagej.ImagePlusToImgPlus.wrapByte( image ); + final Img< ? extends RealType< ? > > img = net.imglib2.imagej.ImagePlusToImg.wrapByteCached( image ); runFlatIteration( img ); assertTrue( counter.get() < 120 ); // don't call getProcessor too often } @@ -67,7 +67,7 @@ public void testIteratePermutedVirtualStack() { final AtomicInteger counter = new AtomicInteger(); final ImagePlus image = countingImagePlus( counter ); - final Img< ? extends RealType< ? > > img = ImagePlusToImgPlus.wrapByte( image ); + final Img< ? extends RealType< ? > > img = ImagePlusToImg.wrapByteCached( image ); runFlatIteration( Views.permute( img, 0, 2 ) ); assertTrue( counter.get() < 120 ); // don't call getProcessor too often } @@ -77,7 +77,7 @@ public void testIterateImagePlusAdapter() { final AtomicInteger counter = new AtomicInteger(); final ImagePlus image = countingImagePlus( counter ); - final Img< ? extends RealType< ? > > img = net.imglib2.imagej.ImagePlusToImg.wrapByte( image ); + final Img< ? extends RealType< ? > > img = net.imglib2.imagej.ImagePlusToImg.wrapByteDirect( image ); runFlatIteration( Views.permute( img, 0, 2 ) ); assertTrue( counter.get() < 120 ); // don't call getProcessor too often } diff --git a/src/test/java/net/imglib2/imagej/ImagePlusToImgPlusTest.java b/src/test/java/net/imglib2/imagej/ImagePlusToImgPlusTest.java deleted file mode 100644 index c58f6f8..0000000 --- a/src/test/java/net/imglib2/imagej/ImagePlusToImgPlusTest.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * 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 net.imglib2.imagej; - -import ij.ImagePlus; -import ij.gui.NewImage; -import ij.measure.Calibration; -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.NumericType; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class ImagePlusToImgPlusTest< T extends NumericType< T > & NativeType< T > > -{ - - /** Which dimensions to test. */ - final int[][] dim = new int[][] { - // nX nY nC nZ nT - { 128, 128, 1, 1, 1 }, // 2D - { 128, 128, 1, 10, 1 }, // 3D - { 128, 128, 5, 10, 1 }, // 3D over 5 channels - { 128, 128, 1, 10, 30 }, // 4D - { 128, 128, 5, 10, 30 }, // 4D over 5 channels - { 128, 128, 1, 1, 30 }, // 2D + T - { 128, 128, 5, 1, 30 } // 2D + T over 5 channels - - }; - - /** Corresponding calibrations. */ - final float[][] calibration = new float[][] { - // X Y C (ignored) Z T - { 0.2f, 0.2f, Float.NaN, 1.5f, 2 }, - { 0.2f, 0.2f, Float.NaN, 1.5f, 2 }, - { 0.2f, 0.2f, Float.NaN, 1.5f, 2 }, - { 0.2f, 0.2f, Float.NaN, 1.5f, 2 }, - { 0.2f, 0.2f, Float.NaN, 1.5f, 2 }, - { 0.2f, 0.2f, Float.NaN, 1.5f, 2 }, - { 0.2f, 0.2f, Float.NaN, 1.5f, 2 } - - }; - - final String[] units = new String[] { "um", "mm", "cm", "minutes" }; - - @Test - public void testDimensionality() - { - for ( int i = 0; i < dim.length; i++ ) - testDimensionality( dim[ i ], calibration[ i ] ); - } - - private void testDimensionality( final int[] dim, final float[] calibration ) - { - final ImagePlus imp = createCalibratedImagePlus( dim, calibration ); - final ImgPlus< ? > img = ImagePlusToImgPlus.wrap( imp ); - // Print stuff -// System.out.println( "got: " + img.getName() ); -// for ( int d = 0; d < img.numDimensions(); d++ ) -// { -// System.out.println( " Axis " + d + "\t - " + img.axis( d ) + ", spacing = " + img.calibration( d ) + ", dimension = " + img.dimension( d ) ); -// } - assertEquals( getExpectedNumDimensions( dim ), img.numDimensions() ); - checkDimensionality( dim, img ); - checkCalibration( dim, calibration, img ); - } - - private ImagePlus createCalibratedImagePlus( final int[] dim, final float[] calibration ) - { - // Create ImagePlus - final int slices = dim[ 2 ] * dim[ 3 ] * dim[ 4 ]; - final ImagePlus imp = NewImage.createByteImage( "Test", dim[ 0 ], dim[ 1 ], slices, NewImage.FILL_BLACK ); - imp.setDimensions( dim[ 2 ], dim[ 3 ], dim[ 4 ] ); - - // Set calibration - final Calibration impCal = imp.getCalibration(); - impCal.pixelWidth = calibration[ 0 ]; - impCal.pixelHeight = calibration[ 1 ]; - // 2 is for channels - impCal.pixelDepth = calibration[ 3 ]; - impCal.frameInterval = calibration[ 4 ]; - impCal.setXUnit( units[ 0 ] ); - impCal.setYUnit( units[ 1 ] ); - impCal.setZUnit( units[ 2 ] ); - impCal.setTimeUnit( units[ 3 ] ); - - // Print stuff -// System.out.println( "\nFor ImagePlus " + imp + " with " + imp.getCalibration() ); - return imp; - } - - private int getExpectedNumDimensions( final int[] dim ) - { - // Are num dimension correct? - int expectedNumDimensions = 0; - for ( int d = 0; d < dim.length; d++ ) - { - if ( dim[ d ] > 1 ) - expectedNumDimensions++; - } - return expectedNumDimensions; - } - - private void checkDimensionality( final int[] dim, final ImgPlus< ? > img ) - { - int skipDim = 0; - for ( int d = 0; d < dim.length; d++ ) - { - if ( dim[ d ] > 1 ) - { - // imglib skips singleton dimensions, so we must test only - // against non-singleton dimension - assertEquals( - String.format( "For dimension %d, expected %d, but got %d.", d, dim[ d ], img.dimension( skipDim ) ), - dim[ d ], img.dimension( skipDim ) ); - skipDim++; - } - } - } - - private void checkCalibration( final int[] dim, final float[] calibration, final ImgPlus< ? > img ) - { - int skipDim = 0; - for ( int d = 0; d < calibration.length; d++ ) - { - if ( dim[ d ] > 1 ) - { - // Is it the channel axis? - if ( d < getExpectedNumDimensions( dim ) && img.axis( d ).type() == Axes.CHANNEL ) - { - - // Then the calibration should be 1, - assertEquals( 1f, img.averageScale( skipDim ), - Float.MIN_VALUE ); - - } - else - { - - // otherwise it should be what we set. - assertEquals( calibration[ d ], img.averageScale( skipDim ), - Float.MIN_VALUE ); - } - skipDim++; - - } - } - } -} diff --git a/src/test/java/net/imglib2/imagej/ImagePlusToImgPlusLazyTest.java b/src/test/java/net/imglib2/imagej/ImagePlusToImgTest.java similarity index 71% rename from src/test/java/net/imglib2/imagej/ImagePlusToImgPlusLazyTest.java rename to src/test/java/net/imglib2/imagej/ImagePlusToImgTest.java index c10f391..f7d580d 100644 --- a/src/test/java/net/imglib2/imagej/ImagePlusToImgPlusLazyTest.java +++ b/src/test/java/net/imglib2/imagej/ImagePlusToImgTest.java @@ -42,54 +42,71 @@ import net.imglib2.type.numeric.ARGBType; import net.imglib2.type.numeric.NumericType; import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.integer.UnsignedIntType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; import org.junit.Test; /** - * Tests the lazy wrapping functions of {@link ImagePlusToImgPlus}. + * Tests the lazy wrapping functions of {@link ImagePlusToImg}. * * @author Matthias Arzt + * @author Gabriel Selzer */ -public class ImagePlusToImgPlusLazyTest +public class ImagePlusToImgTest { private static final long[] DIMENSIONS = { 2, 3, 4, 5, 6 }; - // TODO fix test + /** + * Tests that {@link ImagePlusToImg#wrapByteCached(ImagePlus)} returns the + * same image as {@link ImagePlusToImg#wrapByteDirect(ImagePlus)}. + */ @Test - public void testUnsignedByte() + public void testWrapByteCached() { final ImagePlus image = randomImagePlus( 123, new UnsignedByteType(), DIMENSIONS ); - final Img< UnsignedByteType > actual = ImagePlusToImgPlus.wrapByte( image ); - final Img< UnsignedByteType > expected = ImagePlusToImg.wrapByte( image ); + final Img< UnsignedByteType > actual = ImagePlusToImg.wrapByteCached( image ); + final Img< UnsignedByteType > expected = ImagePlusToImg.wrapByteDirect( image ); ImgLib2Assert.assertImageEquals( expected, actual ); } + /** + * Tests that {@link ImagePlusToImg#wrapShortCached(ImagePlus)} returns the + * same image as {@link ImagePlusToImg#wrapShortDirect(ImagePlus)}. + */ @Test - public void testUnsignedShort() + public void testWrapShortCached() { final ImagePlus image = randomImagePlus( 234, new UnsignedShortType(), DIMENSIONS ); - final Img< UnsignedShortType > actual = ImagePlusToImgPlus.wrapShort( image ); - final Img< UnsignedShortType > expected = ImagePlusToImg.wrapShort( image ); + final Img< UnsignedShortType > actual = ImagePlusToImg.wrapShortCached( image ); + final Img< UnsignedShortType > expected = ImagePlusToImg.wrapShortDirect( image ); ImgLib2Assert.assertImageEquals( expected, actual ); } + /** + * Tests that {@link ImagePlusToImg#wrapRGBACached(ImagePlus)} returns the + * same image as {@link ImagePlusToImg#wrapRGBADirect(ImagePlus)}. + */ @Test public void testRGB() { final ImagePlus image = randomImagePlus( 345, new ARGBType(), DIMENSIONS ); - final Img< ARGBType > actual = ImagePlusToImgPlus.wrapRGBA( image ); - final Img< ARGBType > expected = ImagePlusToImg.wrapRGBA( image ); + final Img< ARGBType > actual = ImagePlusToImg.wrapRGBACached( image ); + final Img< ARGBType > expected = ImagePlusToImg.wrapRGBADirect( image ); ImgLib2Assert.assertImageEquals( expected, actual ); } + /** + * Tests that {@link ImagePlusToImg#wrapFloatCached(ImagePlus)} returns the + * same image as {@link ImagePlusToImg#wrapFloatDirect(ImagePlus)}. + */ @Test public void testFloat() { final ImagePlus image = randomImagePlus( 456, new FloatType(), DIMENSIONS ); - final Img< FloatType > actual = ImagePlusToImgPlus.wrapFloat( image ); - final Img< FloatType > expected = ImagePlusToImg.wrapFloat( image ); + final Img< FloatType > actual = ImagePlusToImg.wrapFloatCached( image ); + final Img< FloatType > expected = ImagePlusToImg.wrapFloatDirect( image ); ImgLib2Assert.assertImageEquals( expected, actual ); } @@ -97,8 +114,8 @@ public void testFloat() public void testLowerNumDimensions() { final ImagePlus image = randomImagePlus( 567, new UnsignedByteType(), 2, 3, 6 ); - final Img< UnsignedByteType > actual = ImagePlusToImgPlus.wrapByte( image ); - final Img< UnsignedByteType > expected = ImagePlusToImg.wrapByte( image ); + final Img< UnsignedByteType > actual = ImagePlusToImg.wrapByteCached( image ); + final Img< UnsignedByteType > expected = ImagePlusToImg.wrapByteDirect( image ); ImgLib2Assert.assertImageEquals( expected, actual ); } @@ -106,8 +123,8 @@ public void testLowerNumDimensions() public void testSingletonDimensions() { final ImagePlus image = randomImagePlus( 678, new UnsignedByteType(), 2, 1, 1, 6 ); - final Img< UnsignedByteType > actual = ImagePlusToImgPlus.wrapByte( image ); - final Img< UnsignedByteType > expected = ImagePlusToImg.wrapByte( image ); + final Img< UnsignedByteType > actual = ImagePlusToImg.wrapByteCached( image ); + final Img< UnsignedByteType > expected = ImagePlusToImg.wrapByteDirect( image ); ImgLib2Assert.assertImageEquals( expected, actual ); } diff --git a/src/test/java/net/imglib2/imagej/ImgPlusViewsTest.java b/src/test/java/net/imglib2/imagej/ImgPlusViewsTest.java deleted file mode 100644 index 6e3f0e6..0000000 --- a/src/test/java/net/imglib2/imagej/ImgPlusViewsTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/*- - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * 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 net.imglib2.imagej; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; -import net.imglib2.img.Img; -import net.imglib2.img.array.ArrayImg; -import net.imglib2.img.array.ArrayImgs; -import net.imglib2.img.basictypeaccess.array.ByteArray; -import net.imglib2.type.numeric.integer.UnsignedByteType; - -import org.junit.Test; - -public class ImgPlusViewsTest -{ - - @Test - public void testHyperSlice() - { - final ArrayImg< UnsignedByteType, ByteArray > img = ArrayImgs.unsignedBytes( 1, 1, 1, 1 ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "image", new AxisType[] { Axes.X, Axes.Y, Axes.Z, Axes.TIME } ); - final ImgPlus< UnsignedByteType > result = ImgPlusViews.hyperSlice( imgPlus, 2, 0 ); - assertEquals( 3, result.numDimensions() ); - assertEquals( Axes.X, result.axis( 0 ).type() ); - assertEquals( Axes.Y, result.axis( 1 ).type() ); - assertEquals( Axes.TIME, result.axis( 2 ).type() ); - } - - @Test - public void testFixAxes() - { - final AxisType[] in = { Axes.X, Axes.unknown(), Axes.Y, Axes.Z, Axes.Y }; - final List< AxisType > expected = Arrays.asList( Axes.X, Axes.CHANNEL, Axes.Y, Axes.Z, Axes.TIME ); - final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( 1, 1, 1, 1, 1 ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "test", in ); - final ImgPlus< UnsignedByteType > result = ImgPlusViews.fixAxes( imgPlus ); - assertEquals( expected, ImgPlusViews.getAxes( result ) ); - } - - @Test - public void testReplaceDuplicates() - { - final Predicate< Integer > isDuplicate = ImgPlusViews.createIsDuplicatePredicate(); - assertFalse( isDuplicate.test( 1 ) ); - assertTrue( isDuplicate.test( 1 ) ); - assertFalse( isDuplicate.test( 2 ) ); - } - - @Test - public void testReplaceNulls() - { - final List< AxisType > in = Arrays.asList( Axes.X, null, Axes.Y, Axes.Z, null ); - final List< AxisType > expected = Arrays.asList( Axes.X, Axes.CHANNEL, Axes.Y, Axes.Z, Axes.TIME ); - final Supplier< AxisType > replacements = Arrays.asList( Axes.CHANNEL, Axes.TIME ).iterator()::next; - final List< AxisType > result = ImgPlusViews.replaceMatches( in, Objects::isNull, replacements ); - assertEquals( expected, result ); - } -} diff --git a/src/test/java/net/imglib2/imagej/ImgPlusToImagePlusTest.java b/src/test/java/net/imglib2/imagej/RAIToImagePlusTest.java similarity index 63% rename from src/test/java/net/imglib2/imagej/ImgPlusToImagePlusTest.java rename to src/test/java/net/imglib2/imagej/RAIToImagePlusTest.java index f286b01..b04049c 100644 --- a/src/test/java/net/imglib2/imagej/ImgPlusToImagePlusTest.java +++ b/src/test/java/net/imglib2/imagej/RAIToImagePlusTest.java @@ -34,106 +34,56 @@ package net.imglib2.imagej; -import java.util.concurrent.atomic.AtomicInteger; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; +import ij.ImagePlus; +import ij.process.ByteProcessor; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; +import ij.process.ShortProcessor; import net.imglib2.FinalInterval; import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.Img; -import net.imglib2.img.ImgView; import net.imglib2.img.array.ArrayImgs; import net.imglib2.type.logic.BitType; import net.imglib2.type.logic.BoolType; import net.imglib2.type.numeric.IntegerType; import net.imglib2.type.numeric.RealType; -import net.imglib2.type.numeric.integer.ByteType; -import net.imglib2.type.numeric.integer.IntType; -import net.imglib2.type.numeric.integer.LongType; -import net.imglib2.type.numeric.integer.ShortType; -import net.imglib2.type.numeric.integer.Unsigned128BitType; -import net.imglib2.type.numeric.integer.Unsigned12BitType; -import net.imglib2.type.numeric.integer.Unsigned2BitType; -import net.imglib2.type.numeric.integer.Unsigned4BitType; -import net.imglib2.type.numeric.integer.UnsignedByteType; -import net.imglib2.type.numeric.integer.UnsignedIntType; -import net.imglib2.type.numeric.integer.UnsignedLongType; -import net.imglib2.type.numeric.integer.UnsignedShortType; +import net.imglib2.type.numeric.integer.*; import net.imglib2.type.numeric.real.DoubleType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.util.ConstantUtils; import net.imglib2.view.Views; - import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; -import ij.ImagePlus; -import ij.process.ByteProcessor; -import ij.process.FloatProcessor; -import ij.process.ImageProcessor; -import ij.process.ShortProcessor; +import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.*; -public class ImgPlusToImagePlusTest +public class RAIToImagePlusTest { @Test public void testAxisOrder() { final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( 1, 1, 2, 3, 4 ); fill( img ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y, Axes.TIME, Axes.CHANNEL, Axes.Z } ); - final ImagePlus imagePlus = ImgPlusToImagePlus.wrap( imgPlus, false ); - assertEquals( 2, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); - assertEquals( 7, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); - assertEquals( 3, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 2, 1, 1 ) ).get( 0, 0 ) ); - } - - @Test - public void test1DStack() - { - final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( 2, 2 ); - fill( img ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.TIME, Axes.X } ); - final ImagePlus imagePlus = ImgPlusToImagePlus.wrap( imgPlus, false ); - assertArrayEquals( new byte[] { 1, 3 }, ( byte[] ) imagePlus.getStack().getPixels( imagePlus.getStackIndex( 1, 1, 1 ) ) ); - assertArrayEquals( new byte[] { 2, 4 }, ( byte[] ) imagePlus.getStack().getPixels( imagePlus.getStackIndex( 1, 1, 2 ) ) ); - } - - @Test - public void testNoXY() - { - final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( 2, 2 ); - fill( img ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.TIME, Axes.CHANNEL } ); - final ImagePlus imagePlus = ImgPlusToImagePlus.wrap( imgPlus, false ); - assertArrayEquals( new byte[] { 2 }, ( byte[] ) imagePlus.getStack().getPixels( imagePlus.getStackIndex( 1, 1, 2 ) ) ); - assertArrayEquals( new byte[] { 3 }, ( byte[] ) imagePlus.getStack().getPixels( imagePlus.getStackIndex( 2, 1, 1 ) ) ); - } - - @Ignore( "not supporting more than five dimensions yet" ) - @Test - public void test6d() - { - final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( 2, 2, 2, 2, 2, 2 ); - fill( img ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y, Axes.Z, Axes.CHANNEL, Axes.TIME, Axes.CHANNEL } ); - final ImagePlus imagePlus = ImgPlusToImagePlus.wrap( imgPlus, true ); - assertArrayEquals( new byte[] { 2 }, ( byte[] ) imagePlus.getStack().getPixels( imagePlus.getStackIndex( 1, 1, 2 ) ) ); - assertArrayEquals( new byte[] { 3 }, ( byte[] ) imagePlus.getStack().getPixels( imagePlus.getStackIndex( 2, 1, 1 ) ) ); + final ImagePlus imagePlus = RAIToImagePlus.wrap( img, "title" ); + assertEquals( 7, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); + assertEquals( 3, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); + assertEquals( 2, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 2, 1, 1 ) ).get( 0, 0 ) ); } @Test public void testColoredAxisOrder() { - final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( 1, 1, 2, 3, 4 ); + final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( 1, 1, 3, 2, 4 ); fill( img ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y, Axes.TIME, Axes.CHANNEL, Axes.Z } ); - final ImagePlus imagePlus = ImgPlusToImagePlus.wrap( imgPlus, true ); - assertEquals( 0xff020406, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); - assertEquals( 0xff07090b, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); + final ImagePlus imagePlus = RAIToImagePlus.convertRGB( img, "title" ); + // img[0, 0, :, 0, 0] is [1, 2, 3] + assertEquals( 0xff010203, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 1 ) ).get( 0, 0 ) ); + // img[0, 0, :, 1, 0] is [4, 5, 6] + assertEquals( 0xff040506, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); + // img[0, 0, :, 0, 1] is [7, 8, 9] + assertEquals( 0xff070809, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); } @Test @@ -235,10 +185,8 @@ public void testDoubleType() private < T extends RealType< T > > void testTypeConversion( final Class< ? extends ImageProcessor > processorClass, final float expected, final T input ) { final RandomAccessibleInterval< T > rai = ConstantUtils.constantRandomAccessibleInterval( input, new FinalInterval( 1, 1 ) ); - final Img< T > image = ImgView.wrap( rai, null ); - final ImgPlus< T > imgPlus = new ImgPlus< T >( image, "title", new AxisType[] { Axes.X, Axes.Y } ); // process - final ImagePlus imagePlus = ImgPlusToImagePlus.wrap( imgPlus, false ); + final ImagePlus imagePlus = RAIToImagePlus.wrap( rai, "title" ); // test final ImageProcessor processor = imagePlus.getProcessor(); Assert.assertTrue( processorClass.isInstance( processor ) ); @@ -256,10 +204,9 @@ public void testUnknownAxes() { final byte[] array = { 1, 2, 3, 4, 5, 6 }; final Img< UnsignedByteType > img = ArrayImgs.unsignedBytes( array, 1, 1, 2, 3 ); - final AxisType[] axes = { Axes.unknown(), Axes.unknown(), Axes.TIME, Axes.unknown() }; - final ImagePlus result = ImgPlusToImagePlus.wrap( new ImgPlus<>( img, "title", axes ), false ); - assertEquals( 3, result.getNChannels() ); - assertEquals( 2, result.getNFrames() ); + final ImagePlus result = RAIToImagePlus.wrap( img, "title" ); + assertEquals( 2, result.getNChannels() ); + assertEquals( 3, result.getNSlices() ); } @Test @@ -267,8 +214,7 @@ public void testPersistence() { // setup final Img< FloatType > img = ArrayImgs.floats( 1, 1 ); - final ImgPlus< FloatType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y } ); - final ImagePlus imagePlus = ImgPlusToImagePlus.wrap( imgPlus, false ); + final ImagePlus imagePlus = RAIToImagePlus.wrap( img, "title" ); final float expected = 42; // process final ImageProcessor processor = imagePlus.getStack().getProcessor( 1 ); @@ -287,8 +233,7 @@ public void testPersistenceBits() { // setup final Img< BitType > img = ArrayImgs.bits( 1, 1 ); - final ImgPlus< BitType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y } ); - final ImagePlus imagePlus = ImgPlusToImagePlus.wrapAndScaleBitType( imgPlus ); + final ImagePlus imagePlus = RAIToImagePlus.wrapAndScaleBoolean( img, "test" ); // process final ImageProcessor processor = imagePlus.getStack().getProcessor( 1 ); processor.setf( 0, 0, 255 ); diff --git a/src/test/java/net/imglib2/imagej/img/ArrayImgToImagePlusTest.java b/src/test/java/net/imglib2/imagej/img/ArrayImgToImagePlusTest.java index 5c0f1fe..7720d1d 100644 --- a/src/test/java/net/imglib2/imagej/img/ArrayImgToImagePlusTest.java +++ b/src/test/java/net/imglib2/imagej/img/ArrayImgToImagePlusTest.java @@ -34,24 +34,18 @@ package net.imglib2.imagej.img; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; +import ij.ImagePlus; import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImg; import net.imglib2.img.array.ArrayImgs; -import net.imglib2.img.cell.CellImg; +import net.imglib2.img.basictypeaccess.array.ByteArray; +import net.imglib2.img.basictypeaccess.array.FloatArray; import net.imglib2.img.cell.CellImgFactory; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.real.FloatType; - import org.junit.Test; -import ij.ImagePlus; +import static org.junit.Assert.*; public class ArrayImgToImagePlusTest { @@ -61,8 +55,8 @@ public void testSharedBuffer() final int width = 2; final int height = 3; final byte[] buffer = new byte[ width * height ]; - final ImgPlus< UnsignedByteType > img = new ImgPlus<>( ArrayImgs.unsignedBytes( buffer, width, height ) ); - final ImagePlus imagePlus = ArrayImgToImagePlus.wrap( img ); + final ArrayImg< UnsignedByteType, ByteArray > img = ArrayImgs.unsignedBytes( buffer, width, height ); + final ImagePlus imagePlus = ArrayImgToImagePlus.wrap( img, "test" ); assertEquals( width, imagePlus.getWidth() ); assertEquals( height, imagePlus.getHeight() ); assertSame( buffer, imagePlus.getProcessor().getPixels() ); @@ -71,13 +65,12 @@ public void testSharedBuffer() @Test public void testIsSupported() { - final ImgPlus< UnsignedByteType > supported = new ImgPlus<>( ArrayImgs.unsignedBytes( 2, 2 ), "image", new AxisType[] { Axes.X, Axes.Y } ); - final ImgPlus< UnsignedByteType > unsupported1 = new ImgPlus<>( ArrayImgs.unsignedBytes( 2, 2, 3 ), "image", new AxisType[] { Axes.X, Axes.Y, Axes.Z } ); - final CellImg< UnsignedByteType, ? > cellImg = new CellImgFactory<>( new UnsignedByteType() ).create( 2, 2 ); - final ImgPlus< UnsignedByteType > unsupported2 = new ImgPlus<>( cellImg, "image", new AxisType[] { Axes.X, Axes.Y } ); + final Img< UnsignedByteType > supported = ArrayImgs.unsignedBytes( 2, 2 ); + final Img< UnsignedByteType > unsupported1 = ArrayImgs.unsignedBytes( 2, 2, 3 ); + final Img< UnsignedByteType > cellImg = new CellImgFactory<>( new UnsignedByteType() ).create( 2, 2 ); assertTrue( ArrayImgToImagePlus.isSupported( supported ) ); assertFalse( ArrayImgToImagePlus.isSupported( unsupported1 ) ); - assertFalse( ArrayImgToImagePlus.isSupported( unsupported2 ) ); + assertFalse( ArrayImgToImagePlus.isSupported( cellImg ) ); } @Test @@ -85,9 +78,8 @@ public void testPersistence() { // setup final float expected = 42.0f; - final Img< FloatType > img = ArrayImgs.floats( 1, 1 ); - final ImgPlus< FloatType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y } ); - final ImagePlus imagePlus = ArrayImgToImagePlus.wrap( imgPlus ); + final ArrayImg img = ArrayImgs.floats( 1, 1 ); + final ImagePlus imagePlus = ArrayImgToImagePlus.wrap( img, "title" ); // process imagePlus.getProcessor().setf( 0, 0, expected ); // test diff --git a/src/test/java/net/imglib2/imagej/img/CellImgToImagePlusTest.java b/src/test/java/net/imglib2/imagej/img/CellImgToImagePlusTest.java index b3ba35a..7406ddb 100644 --- a/src/test/java/net/imglib2/imagej/img/CellImgToImagePlusTest.java +++ b/src/test/java/net/imglib2/imagej/img/CellImgToImagePlusTest.java @@ -35,13 +35,11 @@ package net.imglib2.imagej.img; import ij.ImagePlus; -import ij.ImageStack; -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; import net.imglib2.RandomAccessibleInterval; -import net.imglib2.img.Img; +import net.imglib2.img.basictypeaccess.array.ArrayDataAccess; +import net.imglib2.img.cell.CellImg; import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.type.NativeType; import net.imglib2.type.numeric.IntegerType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.real.DoubleType; @@ -49,10 +47,7 @@ import net.imglib2.view.Views; import org.junit.Test; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Tests {@link CellImgToImagePlus}. @@ -62,84 +57,76 @@ public class CellImgToImagePlusTest @Test public void test3D() { - Img< UnsignedByteType > image = new CellImgFactory<>( new UnsignedByteType(), 3, 1, 1 ).create( 3, 1, 2 ); + CellImg< UnsignedByteType, ? > image = new CellImgFactory<>( new UnsignedByteType(), 3, 1, 1 ).create( 3, 1, 2 ); fill( image ); - ImageStack stack = wrap( image ); - assertArrayEquals( new byte[]{ 1, 2, 3 }, (byte[]) stack.getPixels( 1 ) ); - assertArrayEquals( new byte[]{ 4, 5, 6 }, (byte[]) stack.getPixels( 2 ) ); + ImagePlus imp = wrap( image ); + assertArrayEquals( new byte[]{ 1, 2, 3 }, (byte[]) imp.getStack().getPixels( 1 ) ); + assertArrayEquals( new byte[]{ 4, 5, 6 }, (byte[]) imp.getStack().getPixels( 2 ) ); } - private ImageStack wrap( Img< ? > image ) + private static , A extends ArrayDataAccess> ImagePlus wrap(CellImg< T, ?> image ) { - return CellImgToImagePlus.wrap( ImgPlus.wrap( image ) ).getStack(); + return CellImgToImagePlus.wrap( (CellImg) image, "title" ); } @Test public void test5D() { - Img< UnsignedByteType > image = new CellImgFactory<>( new UnsignedByteType(), 2, 1, 1, 1, 1 ).create( 2, 1, 2, 3, 4 ); + CellImg< UnsignedByteType, ? > image = new CellImgFactory<>( new UnsignedByteType(), 2, 1, 1, 1, 1 ).create( 2, 1, 2, 3, 4 ); fill( image ); - ImageStack stack = wrap( image ); - assertArrayEquals( new byte[]{ 1, 2 }, (byte[]) stack.getPixels( 1 ) ); - assertArrayEquals( new byte[]{ 3, 4 }, (byte[]) stack.getPixels( 2 ) ); - assertArrayEquals( new byte[]{ 5, 6 }, (byte[]) stack.getPixels( 3 ) ); + ImagePlus imp = wrap( image ); + assertArrayEquals( new byte[]{ 1, 2 }, (byte[]) imp.getStack().getPixels( 1 ) ); + assertArrayEquals( new byte[]{ 3, 4 }, (byte[]) imp.getStack().getPixels( 2 ) ); + assertArrayEquals( new byte[]{ 5, 6 }, (byte[]) imp.getStack().getPixels( 3 ) ); } @Test public void testFloatImg() { - Img< FloatType > image = new CellImgFactory<>( new FloatType(), 1, 1, 1 ).create( 1, 1, 1 ); + CellImg< FloatType, ? > image = new CellImgFactory<>( new FloatType(), 1, 1, 1 ).create( 1, 1, 1 ); image.firstElement().set( 42 ); - ImageStack stack = wrap( image ); - assertArrayEquals( new float[]{ 42 }, (float[]) stack.getPixels( 1 ), 0 ); + ImagePlus imp = wrap( image ); + assertArrayEquals( new float[]{ 42 }, (float[]) imp.getStack().getPixels( 1 ), 0 ); } @Test public void testAxisOrder() { - final Img< UnsignedByteType > img = new CellImgFactory<>( new UnsignedByteType(), 1, 1, 1, 1, 1).create( new long[] { 1, 1, 2, 3, 4 } ); + final CellImg< UnsignedByteType, ? > img = new CellImgFactory<>( new UnsignedByteType(), 1, 1, 1, 1, 1).create( new long[] { 1, 1, 2, 3, 4 } ); fill( img ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y, Axes.TIME, Axes.CHANNEL, Axes.Z } ); - final ImagePlus imagePlus = CellImgToImagePlus.wrap( imgPlus ); - assertEquals( 2, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); - assertEquals( 7, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); - assertEquals( 3, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 2, 1, 1 ) ).get( 0, 0 ) ); + final ImagePlus imp = wrap(img); + assertEquals( 7, imp.getStack().getProcessor( imp.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); + assertEquals( 3, imp.getStack().getProcessor( imp.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); + assertEquals( 2, imp.getStack().getProcessor( imp.getStackIndex( 2, 1, 1 ) ).get( 0, 0 ) ); } @Test public void testIsSupported() { - assertTrue( CellImgToImagePlus.isSupported( ImgPlus.wrap( new CellImgFactory<>( new UnsignedByteType(), 2, 2 ).create( 2, 2 ) ) ) ); + assertTrue( CellImgToImagePlus.isSupported( new CellImgFactory<>( new UnsignedByteType(), 2, 2 ).create( 2, 2 ) ) ); } @Test public void testIsSupported_FloatType() { - assertTrue( CellImgToImagePlus.isSupported( ImgPlus.wrap( new CellImgFactory<>( new FloatType(), 2, 2 ).create( 2, 2 ) ) ) ); + assertTrue( CellImgToImagePlus.isSupported( new CellImgFactory<>( new FloatType(), 2, 2 ).create( 2, 2 ) ) ); } @Test public void testIsSupported_UnsupportedType() { - assertFalse( CellImgToImagePlus.isSupported( ImgPlus.wrap( new CellImgFactory<>( new DoubleType(), 2, 2 ).create( 2, 2 ) ) ) ); + assertFalse( CellImgToImagePlus.isSupported( new CellImgFactory<>( new DoubleType(), 2, 2 ).create( 2, 2 ) ) ); } @Test public void testIsSupported_PlanarCells() { - assertTrue( CellImgToImagePlus.isSupported( ImgPlus.wrap( new CellImgFactory<>( new FloatType(), 7, 2 ).create( 2, 2 ) ) ) ); - assertTrue( CellImgToImagePlus.isSupported( ImgPlus.wrap( new CellImgFactory<>( new FloatType(), 2, 2, 3 ).create( 2, 2, 1 ) ) ) ); + assertTrue( CellImgToImagePlus.isSupported( new CellImgFactory<>( new FloatType(), 7, 2 ).create( 2, 2 ) ) ); + assertTrue( CellImgToImagePlus.isSupported( new CellImgFactory<>( new FloatType(), 2, 2, 3 ).create( 2, 2, 1 ) ) ); } @Test public void testIsSupported_NoPlanarCells() { - assertFalse( CellImgToImagePlus.isSupported( ImgPlus.wrap( new CellImgFactory<>( new FloatType(), 1, 2 ).create( 2, 2 ) ) ) ); - assertFalse( CellImgToImagePlus.isSupported( ImgPlus.wrap( new CellImgFactory<>( new FloatType(), 2, 2, 3 ).create( 2, 2, 3 ) ) ) ); - } - - @Test - public void testIsSupported_WrongAxis() { - final Img< FloatType > cellImg = new CellImgFactory<>( new FloatType(), 2, 2, 3 ).create( 2, 2, 1 ); - assertTrue( CellImgToImagePlus.isSupported( new ImgPlus<>( cellImg, "title", new AxisType[]{ Axes.X, Axes.Y, Axes.unknown() } ) ) ); - assertFalse( CellImgToImagePlus.isSupported( new ImgPlus<>( cellImg, "title", new AxisType[]{ Axes.X, Axes.Z, Axes.TIME } ) ) ); + assertFalse( CellImgToImagePlus.isSupported( new CellImgFactory<>( new FloatType(), 1, 2 ).create( 2, 2 ) ) ); + assertFalse( CellImgToImagePlus.isSupported( new CellImgFactory<>( new FloatType(), 2, 2, 3 ).create( 2, 2, 3 ) ) ); } private void fill( RandomAccessibleInterval< ? extends IntegerType< ? > > image ) @@ -153,8 +140,8 @@ private void fill( RandomAccessibleInterval< ? extends IntegerType< ? > > image public void testPersistence() { // setup - final Img< FloatType > img = new CellImgFactory<>( new FloatType() ).create( 1, 1, 1 ); - final ImagePlus imagePlus = CellImgToImagePlus.wrap( ImgPlus.wrap( img ) ); + final CellImg< FloatType, ? > img = new CellImgFactory<>( new FloatType() ).create( 1, 1, 1 ); + final ImagePlus imagePlus = wrap( img ); final float expected = 42.0f; // process imagePlus.getProcessor().setf( 0, 0, expected ); @@ -165,8 +152,8 @@ public void testPersistence() @Test public void testSetPixels() { // setup - final Img< FloatType > img = new CellImgFactory<>( new FloatType() ).create( 1, 1, 1 ); - final ImagePlus imagePlus = CellImgToImagePlus.wrap( new ImgPlus<>( img, "title" ) ); + final CellImg< FloatType, ? > img = new CellImgFactory<>( new FloatType() ).create( 1, 1, 1 ); + final ImagePlus imagePlus = wrap( img ); final float expected = 42.0f; // process imagePlus.getStack().setPixels( new float[] { expected }, 1 ); diff --git a/src/test/java/net/imglib2/imagej/img/ImageJFunctionBehavior.java b/src/test/java/net/imglib2/imagej/img/ImageJFunctionBehavior.java index 3c2c0d7..5ed9842 100644 --- a/src/test/java/net/imglib2/imagej/img/ImageJFunctionBehavior.java +++ b/src/test/java/net/imglib2/imagej/img/ImageJFunctionBehavior.java @@ -69,36 +69,36 @@ static public final void main( final String[] arg ) // 1. Test ImagePlus -> Img, specific wrappers IJ.run( imp, "8-bit", "" ); - final Img< UnsignedByteType > imgb = ImagePlusToImg.wrapByte( imp ); + final Img< UnsignedByteType > imgb = ImagePlusToImg.wrapByteDirect( imp ); print( imgb ); IJ.run( imp, "16-bit", "" ); - final Img< UnsignedShortType > imgs = ImagePlusToImg.wrapShort( imp ); + final Img< UnsignedShortType > imgs = ImagePlusToImg.wrapShortDirect( imp ); print( imgs ); IJ.run( imp, "32-bit", "" ); - final Img< FloatType > imgf = ImagePlusToImg.wrapFloat( imp ); + final Img< FloatType > imgf = ImagePlusToImg.wrapFloatDirect( imp ); print( imgf ); IJ.run( imp, "RGB Color", "" ); - final Img< ARGBType > imgRGB = ImagePlusToImg.wrapRGBA( imp ); + final Img< ARGBType > imgRGB = ImagePlusToImg.wrapRGBADirect( imp ); print( imgRGB ); // 2. Test ImagePlus -> Img, generic wrapper IJ.run( imp, "8-bit", "" ); - final Img< UnsignedByteType > g_imgb = (Img) ImagePlusToImg.wrap( imp ); + final Img< UnsignedByteType > g_imgb = (Img) ImagePlusToImg.wrapDirect( imp ); print( g_imgb ); IJ.run( imp, "16-bit", "" ); - final Img< UnsignedShortType > g_imgs = (Img) ImagePlusToImg.wrap( imp ); + final Img< UnsignedShortType > g_imgs = (Img) ImagePlusToImg.wrapDirect( imp ); print( g_imgs ); IJ.run( imp, "32-bit", "" ); - final Img< FloatType > g_imgf = (Img) ImagePlusToImg.wrap( imp ); + final Img< FloatType > g_imgf = (Img) ImagePlusToImg.wrapDirect( imp ); print( g_imgf ); IJ.run( imp, "RGB Color", "" ); - final Img< ARGBType > g_imgRGB = (Img) ImagePlusToImg.wrap( imp ); + final Img< ARGBType > g_imgRGB = (Img) ImagePlusToImg.wrapDirect( imp ); print( g_imgRGB ); // 3. Test Img -> ImagePlus, specific wrappers diff --git a/src/test/java/net/imglib2/imagej/img/ImageJVirtualStackGetProcessorBenchmark.java b/src/test/java/net/imglib2/imagej/img/ImageJVirtualStackGetProcessorBenchmark.java index 085a6df..35da0f4 100644 --- a/src/test/java/net/imglib2/imagej/img/ImageJVirtualStackGetProcessorBenchmark.java +++ b/src/test/java/net/imglib2/imagej/img/ImageJVirtualStackGetProcessorBenchmark.java @@ -34,8 +34,7 @@ package net.imglib2.imagej.img; import ij.ImageStack; -import net.imagej.ImgPlus; -import net.imglib2.imagej.ImgPlusToImagePlus; +import net.imglib2.imagej.RAIToImagePlus; import net.imglib2.img.Img; import net.imglib2.img.array.ArrayImgFactory; import net.imglib2.type.NativeType; @@ -107,7 +106,7 @@ public void all() { private < T extends NativeType< T > > void test( T type ) { Img< T > image = getImage( type ); - ImageStack stack = ImgPlusToImagePlus.wrap( new ImgPlus<>( image ) ).getStack(); + ImageStack stack = RAIToImagePlus.wrapVirtualStack( image, "test" ).getStack(); for ( int i = 0; i < stack.getSize(); i++ ) { stack.getProcessor( i + 1 ); diff --git a/src/test/java/net/imglib2/imagej/img/ImgLib2ToVirtualStackBenchmark.java b/src/test/java/net/imglib2/imagej/img/ImgLib2ToVirtualStackBenchmark.java index dfd6c5a..cc60d41 100644 --- a/src/test/java/net/imglib2/imagej/img/ImgLib2ToVirtualStackBenchmark.java +++ b/src/test/java/net/imglib2/imagej/img/ImgLib2ToVirtualStackBenchmark.java @@ -33,13 +33,13 @@ */ package net.imglib2.imagej.img; -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; -import net.imglib2.imagej.ImgPlusToImagePlus; -import net.imglib2.img.Img; +import net.imglib2.imagej.RAIToImagePlus; +import net.imglib2.img.array.ArrayImg; import net.imglib2.img.array.ArrayImgs; +import net.imglib2.img.basictypeaccess.array.ByteArray; +import net.imglib2.img.cell.CellImg; import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.img.planar.PlanarImg; import net.imglib2.img.planar.PlanarImgs; import net.imglib2.type.numeric.integer.UnsignedByteType; @@ -60,72 +60,62 @@ public class ImgLib2ToVirtualStackBenchmark long[] deepDims = { 10, 10, 1000000 }; long[] cubicDims = { 1000, 1000, 1000 }; - private final ImgPlus< UnsignedByteType > smallCellImage = makeImgPlus( createCellImg( deepDims ) ); - private final ImgPlus< UnsignedByteType > deepCellImage = makeImgPlus( createCellImg( deepDims ) ); - private final ImgPlus< UnsignedByteType > cubicCellImage = makeImgPlus( createCellImg( cubicDims ) ); - private final ImgPlus< UnsignedByteType > smallPlanarImg = makeImgPlus( PlanarImgs.unsignedBytes( smallDims ) ); - private final ImgPlus< UnsignedByteType > cubicPlanarImg = makeImgPlus( PlanarImgs.unsignedBytes( cubicDims ) ); - private final ImgPlus< UnsignedByteType > deepPlanarImg = makeImgPlus( PlanarImgs.unsignedBytes( deepDims ) ); - private final ImgPlus< UnsignedByteType > small2dArrayImg = makeImgPlus( ArrayImgs.unsignedBytes( 10, 10 ) ); - private final ImgPlus< UnsignedByteType > big2dArrayImg = makeImgPlus( ArrayImgs.unsignedBytes( 10000, 10000 ) ); + private final CellImgFactory fac = new CellImgFactory<>( new UnsignedByteType() ); + private final CellImg< UnsignedByteType, ? > smallCellImage = fac.create( smallDims ); + private final CellImg< UnsignedByteType, ? > deepCellImage = fac.create( deepDims ); + private final CellImg< UnsignedByteType, ? > cubicCellImage = fac.create( cubicDims ); + private final PlanarImg< UnsignedByteType, ByteArray > smallPlanarImg = PlanarImgs.unsignedBytes( smallDims ); + private final PlanarImg< UnsignedByteType, ByteArray > cubicPlanarImg = PlanarImgs.unsignedBytes( cubicDims ); + private final PlanarImg< UnsignedByteType, ByteArray > deepPlanarImg = PlanarImgs.unsignedBytes( deepDims ); + private final ArrayImg< UnsignedByteType, ByteArray > small2dArrayImg = ArrayImgs.unsignedBytes( 10, 10 ); + private final ArrayImg< UnsignedByteType, ByteArray> big2dArrayImg = ArrayImgs.unsignedBytes( 10000, 10000 ); @Benchmark public void testSmallCellImg() { - ImgPlusToImagePlus.wrap( smallCellImage ); + RAIToImagePlus.wrap( smallCellImage, "test" ); } @Benchmark public void testDeepCellImg() { - ImgPlusToImagePlus.wrap( deepCellImage ); + RAIToImagePlus.wrap(deepCellImage, "test" ); } @Benchmark public void testCubicCellImg() { - ImgPlusToImagePlus.wrap( cubicCellImage ); + RAIToImagePlus.wrap(cubicCellImage, "test" ); } @Benchmark public void testSmallPlanarImg() { - PlanarImgToImagePlus.wrap( smallPlanarImg ); + PlanarImgToImagePlus.wrap( smallPlanarImg, "test" ); } @Benchmark public void testCubicPlanarImg() { - PlanarImgToImagePlus.wrap( cubicPlanarImg ); + PlanarImgToImagePlus.wrap( cubicPlanarImg, "test" ); } @Benchmark public void testDeepPlanarImg() { - PlanarImgToImagePlus.wrap( deepPlanarImg ); + PlanarImgToImagePlus.wrap( deepPlanarImg, "test" ); } @Benchmark public void testSmall2dArrayImg() { - ArrayImgToImagePlus.wrap( small2dArrayImg ); + ArrayImgToImagePlus.wrap( small2dArrayImg, "test" ); } @Benchmark public void testLarge2dArrayImg() { - ArrayImgToImagePlus.wrap( big2dArrayImg ); - } - - private ImgPlus< UnsignedByteType > makeImgPlus( final Img< UnsignedByteType > deepPlanarImg ) - { - final AxisType[] axes = { Axes.X, Axes.Y, Axes.Z }; - return new ImgPlus<>( deepPlanarImg, "title", axes ); - } - - private Img< UnsignedByteType > createCellImg( final long... dim ) - { - return new CellImgFactory<>( new UnsignedByteType() ).create( dim ); + ArrayImgToImagePlus.wrap( big2dArrayImg, "test" ); } public static void main( final String... args ) throws RunnerException diff --git a/src/test/java/net/imglib2/imagej/img/PlanarImgToImagePlusTest.java b/src/test/java/net/imglib2/imagej/img/PlanarImgToImagePlusTest.java index 0d47996..fb9e921 100644 --- a/src/test/java/net/imglib2/imagej/img/PlanarImgToImagePlusTest.java +++ b/src/test/java/net/imglib2/imagej/img/PlanarImgToImagePlusTest.java @@ -34,17 +34,11 @@ package net.imglib2.imagej.img; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; - -import java.util.concurrent.atomic.AtomicInteger; - -import net.imagej.ImgPlus; -import net.imagej.axis.Axes; -import net.imagej.axis.AxisType; +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; import net.imglib2.RandomAccessibleInterval; -import net.imglib2.imagej.ImagePlusToImgPlus; +import net.imglib2.imagej.ImagePlusToImg; import net.imglib2.img.basictypeaccess.array.FloatArray; import net.imglib2.img.basictypeaccess.array.IntArray; import net.imglib2.img.planar.PlanarImg; @@ -55,30 +49,42 @@ import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.view.Views; - import org.junit.Test; -import ij.IJ; -import ij.ImagePlus; -import ij.VirtualStack; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; public class PlanarImgToImagePlusTest { + private void fill( final RandomAccessibleInterval< ? extends IntegerType> img ) + { + final AtomicInteger i = new AtomicInteger(); + Views.flatIterable( img ).forEach( pixel -> pixel.setInteger( i.incrementAndGet() ) ); + } + + private PlanarImg< UnsignedByteType, ? > example() + { + final PlanarImg< UnsignedByteType, ? > img = new PlanarImgFactory<>(new UnsignedByteType()).create(3, 1, 2); + fill( img ); + return img; + } + @Test public void testStorageArray() { final PlanarImg< UnsignedByteType, ? > img = example(); - final VirtualStack stack = PlanarImgToImagePlus.wrap( img ); - assertSame( img.getPlane( 1 ).getCurrentStorageArray(), stack.getPixels( 2 ) ); + final ImagePlus imp = PlanarImgToImagePlus.wrap( img, "test"); + assertSame( img.getPlane( 1 ).getCurrentStorageArray(), imp.getStack().getPixels( 2 ) ); } @Test public void testPixelValues() { final PlanarImg< UnsignedByteType, ? > img = example(); - final VirtualStack stack = PlanarImgToImagePlus.wrap( img ); - assertArrayEquals( new byte[] { 1, 2, 3 }, ( byte[] ) stack.getPixels( 1 ) ); - assertArrayEquals( new byte[] { 4, 5, 6 }, ( byte[] ) stack.getPixels( 2 ) ); + final ImagePlus imp = PlanarImgToImagePlus.wrap( img, "test" ); + assertArrayEquals( new byte[] { 1, 2, 3 }, ( byte[] ) imp.getStack().getPixels( 1 ) ); + assertArrayEquals( new byte[] { 4, 5, 6 }, ( byte[] ) imp.getStack().getPixels( 2 ) ); } @Test @@ -87,50 +93,35 @@ public void testImgPlus() // setup final PlanarImg< UnsignedByteType, ? > img = example(); final String title = "test image"; - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, title, new AxisType[] { Axes.X, Axes.Y, Axes.TIME } ); // process - final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( imgPlus ); + final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( img, title ); // test assertEquals( title, imagePlus.getTitle() ); assertEquals( 3, imagePlus.getWidth() ); assertEquals( 1, imagePlus.getHeight() ); - assertEquals( 1, imagePlus.getNChannels() ); + assertEquals( 2, imagePlus.getNChannels() ); assertEquals( 1, imagePlus.getNSlices() ); - assertEquals( 2, imagePlus.getNFrames() ); + assertEquals( 1, imagePlus.getNFrames() ); } @Test public void testAxisOrder() { - final PlanarImg< UnsignedByteType, ? > img = new PlanarImgFactory< UnsignedByteType >().create( new long[] { 1, 1, 2, 3, 4 }, new UnsignedByteType() ); - fill( img ); - final ImgPlus< UnsignedByteType > imgPlus = new ImgPlus<>( img, "title", new AxisType[] { Axes.X, Axes.Y, Axes.TIME, Axes.CHANNEL, Axes.Z } ); - final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( imgPlus ); - assertEquals( 2, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); - assertEquals( 7, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); - assertEquals( 3, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 2, 1, 1 ) ).get( 0, 0 ) ); - } - - private void fill( final RandomAccessibleInterval< ? extends IntegerType > img ) - { - final AtomicInteger i = new AtomicInteger(); - Views.flatIterable( img ).forEach( pixel -> pixel.setInteger( i.incrementAndGet() ) ); - } - - private PlanarImg< UnsignedByteType, ? > example() - { - final PlanarImg< UnsignedByteType, ? > img = new PlanarImgFactory< UnsignedByteType >().create( new long[] { 3, 1, 2 }, new UnsignedByteType() ); + final PlanarImg< UnsignedByteType, ? > img = new PlanarImgFactory<>(new UnsignedByteType()).create(1, 1, 2, 3, 4); fill( img ); - return img; + final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( img, "title" ); + assertEquals( 7, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 1, 2 ) ).get( 0, 0 ) ); + assertEquals( 3, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 1, 2, 1 ) ).get( 0, 0 ) ); + assertEquals( 2, imagePlus.getStack().getProcessor( imagePlus.getStackIndex( 2, 1, 1 ) ).get( 0, 0 ) ); } @Test public void testPersistence() { // setup - final PlanarImg< FloatType, FloatArray > img = PlanarImgs.floats( 1, 1 ); - final ImgPlus< FloatType > imgPlus = new ImgPlus< FloatType >( img, "title", new AxisType[] { Axes.X, Axes.Y } ); - final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( imgPlus ); + final PlanarImg img = PlanarImgs.floats( 1, 1 ); +// final ImgPlus< FloatType > imgPlus = new ImgPlus< FloatType >( img, "title", new AxisType[] { Axes.X, Axes.Y } ); + final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( img,"title" ); final float expected = 42.0f; // process imagePlus.getProcessor().setf( 0, 0, expected ); @@ -142,7 +133,7 @@ public void testPersistence() public void testSetPixels() { // setup final PlanarImg< FloatType, FloatArray > img = PlanarImgs.floats( 1, 1 ); - final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( new ImgPlus<>( img, "title" ) ); + final ImagePlus imagePlus = PlanarImgToImagePlus.wrap( img, "title" ); final float expected = 42.0f; // process imagePlus.getStack().setPixels( new float[] { expected }, 1 ); @@ -156,7 +147,7 @@ public void testGetProcessorForColorProcessor() { // To achieve this min and max must not be set for ColorProcessor. final PlanarImg< ARGBType, IntArray > argbs = PlanarImgs.argbs( 1, 1, 1 ); argbs.randomAccess().get().set( 0xff010203 ); - final VirtualStack stack = PlanarImgToImagePlus.wrap( argbs ); + final ImageStack stack = PlanarImgToImagePlus.wrap( argbs, "title" ).getImageStack(); assertArrayEquals( new int[] { 0xff010203 }, (int[]) stack.getPixels( 1 ) ); stack.getProcessor( 1 ); assertArrayEquals( new int[] { 0xff010203 }, (int[]) stack.getPixels( 1 ) ); @@ -165,8 +156,8 @@ public void testGetProcessorForColorProcessor() { @Test public void testConvertingBackAndForth() { ImagePlus imagePlus = IJ.createImage( "test", "8-bit ramp", 3, 3, 3 ); - ImgPlus< UnsignedByteType > convertedImg = ImagePlusToImgPlus.wrapByte( imagePlus ); - ImagePlus twiceConvertedImagePlus = PlanarImgToImagePlus.wrap( convertedImg ); + PlanarImg convertedImg = ImagePlusToImg.wrapByteDirect( imagePlus ); + ImagePlus twiceConvertedImagePlus = PlanarImgToImagePlus.wrap( convertedImg, "title" ); twiceConvertedImagePlus.getStack().getProcessor( 1 ).set( 0, 0, 5 ); assertEquals( 5, imagePlus.getStack().getProcessor( 1 ).get( 0, 0 ) ); }