diff --git a/pom.xml b/pom.xml index 8288285..74e51d7 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ sc.fiji pom-indago - 2.2.4 + 2.2.10 @@ -81,6 +81,18 @@ junit test + + org.openjdk.jmh + jmh-core + 1.19 + test + + + org.openjdk.jmh + jmh-generator-annprocess + 1.19 + test + diff --git a/src/main/java/com/indago/io/DataMover.java b/src/main/java/com/indago/io/DataMover.java index a6c5cf7..d6c79e1 100644 --- a/src/main/java/com/indago/io/DataMover.java +++ b/src/main/java/com/indago/io/DataMover.java @@ -3,6 +3,7 @@ */ package com.indago.io; +import java.util.Arrays; import java.util.List; import io.scif.img.ImgIOException; @@ -15,12 +16,13 @@ import net.imglib2.exception.IncompatibleTypeException; import net.imglib2.img.Img; import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.loops.ClassCopyProvider; import net.imglib2.type.NativeType; import net.imglib2.type.Type; import net.imglib2.type.numeric.ARGBType; +import net.imglib2.type.numeric.IntegerType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.IntType; -import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.DoubleType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.view.Views; @@ -31,6 +33,9 @@ */ public class DataMover { + private static final ClassCopyProvider copyLoopFactory = + new ClassCopyProvider<>(CopyLoopClass.class, CopyLoopInterface.class); + /** * FROM: imglib Example 2c :) * Copy from a source that is just @@ -48,44 +53,23 @@ public class DataMover { * - an IterableInterval as target */ public static < T extends Type< T >> void copy( final RandomAccessible< T > source, final IterableInterval< T > target ) { - // create a cursor that automatically localizes itself on every move - final Cursor< T > targetCursor = target.localizingCursor(); - final RandomAccess< T > sourceRandomAccess = source.randomAccess(); - - // iterate over the input cursor - while ( targetCursor.hasNext() ) { - // move input cursor forward - targetCursor.fwd(); - - // set the output cursor to the position of the input cursor - sourceRandomAccess.setPosition( targetCursor ); - - // set the value of this pixel of the output image, every Type - // supports T.set( T type ) - targetCursor.get().set( sourceRandomAccess.get() ); - } + copy(source, target, (in, out) -> out.set(in)); } - public static < T1 extends Type< T1 >, T2 extends Type< T2 >> void copy( final RandomAccessible< T1 > source, final IterableInterval< T2 > target, final Converter< T1, T2 > converter ) { - // create a cursor that automatically localizes itself on every move - final Cursor< T2 > targetCursor = target.localizingCursor(); - final RandomAccess< T1 > sourceRandomAccess = source.randomAccess(); - - // iterate over the input cursor - while ( targetCursor.hasNext() ) { - // move input cursor forward - targetCursor.fwd(); - - // set the output cursor to the position of the input cursor - sourceRandomAccess.setPosition( targetCursor ); + public static < T extends Type< T >> void copy( final RandomAccessible< T > source, final RandomAccessibleInterval< T > target ) { + copy( source, Views.iterable( target ) ); + } - // set converted value - converter.convert( sourceRandomAccess.get(), targetCursor.get() ); - } + public static < T extends Type< T >> void copy( final RandomAccessible< T > source, final Img< T > target ) { + copy( source, (IterableInterval) target ); } - public static < T extends Type< T >> void copy( final RandomAccessible< T > source, final RandomAccessibleInterval< T > target ) { - copy( source, Views.iterable( target ) ); + public static < S, T > void copy( final RandomAccessible< ? extends S > source, final IterableInterval< ? extends T > target, final Converter< S, T > converter ) { + RandomAccess< ? extends S > sourceRandomAccess = source.randomAccess(); + Cursor< ? extends T > targetCursor = target.localizingCursor(); + Object key = Arrays.asList(sourceRandomAccess.getClass(), sourceRandomAccess + .get().getClass(), targetCursor.getClass(), targetCursor.get().getClass(), converter.getClass()); + copyLoopFactory.newInstanceForKey(key).copy(sourceRandomAccess, targetCursor, converter); } public static < T extends NativeType< T >> Img< T > createEmptyArrayImgLike( final RandomAccessibleInterval< ? > blueprint, final T type ) { @@ -112,22 +96,7 @@ public static < T extends NativeType< T >> Img< T > createEmptyArrayImgLike( fin public static < T extends RealType< T > > void add( final RandomAccessible< T > source, final IterableInterval< T > target ) { - // create a cursor that automatically localizes itself on every move - final Cursor< T > targetCursor = target.localizingCursor(); - final RandomAccess< T > sourceRandomAccess = source.randomAccess(); - - // iterate over the input cursor - while ( targetCursor.hasNext() ) { - // move input cursor forward - targetCursor.fwd(); - - // set the output cursor to the position of the input cursor - sourceRandomAccess.setPosition( targetCursor ); - - // add the value of this pixel of the output image, every Type - // supports T.set( T type ) - targetCursor.get().add( sourceRandomAccess.get() ); - } + copy(source, target, (in, out) -> out.add(in)); } public static < T extends RealType< T > > void add( @@ -136,6 +105,12 @@ public static < T extends RealType< T > > void add( add( source, Views.iterable( target ) ); } + public static < T extends RealType< T > > void add( + final RandomAccessible< T > source, + final Img< T > target ) { + add( source, (IterableInterval< T >) target ); + } + /** * Util function that copies one image into another. * I cases where the native pixel types match, convertAndCopy @@ -163,255 +138,58 @@ public static < T extends RealType< T > > void add( * * @param source * @param target - * @throws Exception */ -// public static , TT extends NativeType> void convertAndCopy(final Img source, final Img target) throws Exception { -// convertAndCopy( source, Views.iterable(target) ); -// } -// -// public static , TT extends NativeType> void convertAndCopy(final RandomAccessible source, final RandomAccessibleInterval target) throws Exception { -// convertAndCopy( source, Views.iterable(target) ); -// } @SuppressWarnings( "unchecked" ) public static < ST extends RealType< ST >, TT extends NativeType< TT > > void convertAndCopy( final RandomAccessible< ST > source, - final IterableInterval< TT > target ) throws Exception { + final IterableInterval< TT > target ) { final ST sourceType = source.randomAccess().get(); final TT targetType = target.firstElement(); - // if source and target are of same type -> use copy since convert is not needed... - if ( sourceType.getClass().isInstance( targetType ) ) { - DataMover.copy( source, ( IterableInterval< ST > ) target ); - return; - } + Converter, NativeType> converter = getConverter(sourceType, targetType); - // implemented conversion cases follow here... + DataMover.copy( source, target, converter ); + } - boolean throwException = false; - if ( sourceType instanceof FloatType ) { + private static < ST extends RealType< ST >, TT extends NativeType< TT > > Converter,NativeType> getConverter(ST sourceType, TT targetType) { - if ( targetType instanceof DoubleType ) { // FloatType --> DoubleType - final Cursor< TT > targetCursor = target.localizingCursor(); - final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); - final int v; - while ( targetCursor.hasNext() ) { - targetCursor.fwd(); - sourceRandomAccess.setPosition( targetCursor ); + if ( sourceType.getClass().isInstance( targetType ) ) + return (in, out) -> ((Type) out).set(in); - ( ( DoubleType ) targetCursor.get() ).set( ( ( FloatType ) sourceRandomAccess.get() ).getRealDouble() ); - } - } else // FloatType --> IntType - if ( targetType instanceof IntType ) { - final Cursor< TT > targetCursor = target.localizingCursor(); - final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); - final int v; - while ( targetCursor.hasNext() ) { - targetCursor.fwd(); - sourceRandomAccess.setPosition( targetCursor ); - - ( ( IntType ) targetCursor.get() ).set( Math.round( ( ( FloatType ) sourceRandomAccess.get() ).getRealFloat() ) ); - } - } else // FloatType --> ARGBType - if ( targetType instanceof ARGBType ) { - final Cursor< TT > targetCursor = target.localizingCursor(); - final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); - int v; - while ( targetCursor.hasNext() ) { - targetCursor.fwd(); - sourceRandomAccess.setPosition( targetCursor ); - try { - v = Math.round( ( ( FloatType ) sourceRandomAccess.get() ).get() * 255 ); - } catch ( final ArrayIndexOutOfBoundsException e ) { - v = 255; // If image-sizes do not match we pad with white pixels... - } - if ( v > 255 ) { throw new Exception( "TODO: in this case (source in not within [0,1]) I did not finish the code!!! Now would likely be a good time... ;)" ); } - ( ( ARGBType ) targetCursor.get() ).set( ARGBType.rgba( v, v, v, 255 ) ); - } - } else { - throwException = true; - } + if ( targetType instanceof DoubleType ) + return (in, out) -> ((DoubleType) out).set( in.getRealDouble() ); - } else if ( sourceType instanceof UnsignedShortType ) { + if ( targetType instanceof FloatType ) + return (in, out) -> ((FloatType) out).set(in.getRealFloat()); - // UnsignedShortType --> FloatType - if ( targetType instanceof FloatType ) { - final Cursor< TT > targetCursor = target.localizingCursor(); - final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); - final int v; - while ( targetCursor.hasNext() ) { - targetCursor.fwd(); - sourceRandomAccess.setPosition( targetCursor ); + if ( targetType instanceof IntType ) { + if ( sourceType instanceof IntegerType ) + return (in, out) -> ((IntType) out).set( ((IntegerType) in).getInteger()); + else + return (in, out) -> ((IntType) out).set( Math.round(in.getRealFloat())); + } - ( ( FloatType ) targetCursor.get() ).set( ( ( UnsignedShortType ) sourceRandomAccess.get() ).getRealFloat() ); + if ( sourceType instanceof FloatType && targetType instanceof ARGBType ) { + return (in, out) -> { + int v; + try { + v = Math.round(((FloatType) in).get() * 255); } - } else - if ( targetType instanceof DoubleType ) { - final Cursor< TT > targetCursor = target.localizingCursor(); - final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); - final int v; - while ( targetCursor.hasNext() ) { - targetCursor.fwd(); - sourceRandomAccess.setPosition( targetCursor ); - - ( ( DoubleType ) targetCursor.get() ).set( ( ( UnsignedShortType ) sourceRandomAccess.get() ).getRealDouble() ); + catch (final ArrayIndexOutOfBoundsException e) { + v = 255; // If image-sizes do not match we pad with white pixels... } - } else // UnsignedShortType --> IntType - if ( targetType instanceof IntType ) { - final Cursor< TT > targetCursor = target.localizingCursor(); - final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); - final int v; - while ( targetCursor.hasNext() ) { - targetCursor.fwd(); - sourceRandomAccess.setPosition( targetCursor ); - - ( ( IntType ) targetCursor.get() ).set( ( ( UnsignedShortType ) sourceRandomAccess.get() ).get() ); + if (v > 255) { + v = 255; + } else if (v < 0) { + v = 0; } -// } else -// if ( targetType instanceof ARGBType ) { -// final Cursor< TT > targetCursor = target.localizingCursor(); -// final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); -// int v; -// while ( targetCursor.hasNext() ) { -// targetCursor.fwd(); -// sourceRandomAccess.setPosition( targetCursor ); -// try { -// v = -// Math.round( ( ( UnsignedShortType ) sourceRandomAccess.get() ).getRealFloat() * 255 ); -// } catch ( final ArrayIndexOutOfBoundsException e ) { -// v = 255; // If image-sizes do not match we pad with white pixels... -// } -// if ( v > 255 ) { throw new Exception( "TODO: in this case (source in not within [0,1]) I did not finish the code!!! Now would likely be a good time... ;)" ); } -// ( ( ARGBType ) targetCursor.get() ).set( ARGBType.rgba( v, v, v, 255 ) ); -// } -// } else { -// throwException = true; - } -// } else if ( sourceType instanceof ARGBType ) { -// -// // ARGBType --> FloatType -// if ( targetType instanceof ARGBType ) { -// final Cursor< TT > targetCursor = target.localizingCursor(); -// final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); -// double v; -// int intRGB; -// while ( targetCursor.hasNext() ) { -// targetCursor.fwd(); -// sourceRandomAccess.setPosition( targetCursor ); -// intRGB = ( ( ARGBType ) sourceRandomAccess.get() ).get(); -// v = -// 0.2989 * ARGBType.red( intRGB ) + 0.5870 * ARGBType.green( intRGB ) + 0.1140 * ARGBType.blue( intRGB ); -// v /= 255; -// ( ( ARGBType ) targetCursor.get() ).set( ARGBType.rgba( v, v, v, 255 ) ); -// } -// } else { -// throwException = true; -// } - } else { - throwException = true; + ((ARGBType) out).set(ARGBType.rgba(v, v, v, 255)); + }; } - if ( throwException ) - throw new Exception( "Convertion from " + sourceType.getClass().toString() + " to " + targetType.getClass() + " not implemented!" ); + throw new RuntimeException( "Convertion from " + sourceType.getClass().toString() + " to " + targetType.getClass() + " not implemented!" ); } -// @SuppressWarnings( "unchecked" ) -// public static < ST extends NativeType< ST >, TT extends NativeType< TT > > void convertAndCopy( -// final RandomAccessible< ST > source, -// final IterableInterval< TT > target ) throws Exception { -// final ST sourceType = source.randomAccess().get(); -// final TT targetType = target.firstElement(); -// -// // if source and target are of same type -> use copy since convert is not needed... -// if ( sourceType.getClass().isInstance( targetType ) ) { -// DataMover.copy( source, ( IterableInterval< ST > ) target ); -// } -// -// // implemented conversion cases follow here... -// -// boolean throwException = false; -// if ( sourceType instanceof FloatType ) { -// -// // FloatType --> ARGBType -// if ( targetType instanceof ARGBType ) { -// final Cursor< TT > targetCursor = target.localizingCursor(); -// final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); -// int v; -// while ( targetCursor.hasNext() ) { -// targetCursor.fwd(); -// sourceRandomAccess.setPosition( targetCursor ); -// try { -// v = Math.round( ( ( FloatType ) sourceRandomAccess.get() ).get() * 255 ); -// } catch ( final ArrayIndexOutOfBoundsException e ) { -// v = 255; // If image-sizes do not match we pad with white pixels... -// } -// if ( v > 255 ) { throw new Exception( "TODO: in this case (source in not within [0,1]) I did not finish the code!!! Now would likely be a good time... ;)" ); } -// ( ( ARGBType ) targetCursor.get() ).set( ARGBType.rgba( v, v, v, 255 ) ); -// } -// } else { -// throwException = true; -// } -// -// } else if ( sourceType instanceof UnsignedShortType ) { -// -// // RealType --> FloatType -// if ( targetType instanceof FloatType ) { -// final Cursor< TT > targetCursor = target.localizingCursor(); -// final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); -// final int v; -// while ( targetCursor.hasNext() ) { -// targetCursor.fwd(); -// sourceRandomAccess.setPosition( targetCursor ); -// -// ( ( FloatType ) targetCursor.get() ).set( ( ( UnsignedShortType ) sourceRandomAccess.get() ).getRealFloat() ); -// } -// } else -// // RealType --> ARGBType -// if ( targetType instanceof ARGBType ) { -// final Cursor< TT > targetCursor = target.localizingCursor(); -// final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); -// int v; -// while ( targetCursor.hasNext() ) { -// targetCursor.fwd(); -// sourceRandomAccess.setPosition( targetCursor ); -// try { -// v = -// Math.round( ( ( UnsignedShortType ) sourceRandomAccess.get() ).getRealFloat() * 255 ); -// } catch ( final ArrayIndexOutOfBoundsException e ) { -// v = 255; // If image-sizes do not match we pad with white pixels... -// } -// if ( v > 255 ) { throw new Exception( "TODO: in this case (source in not within [0,1]) I did not finish the code!!! Now would likely be a good time... ;)" ); } -// ( ( ARGBType ) targetCursor.get() ).set( ARGBType.rgba( v, v, v, 255 ) ); -// } -// } else { -// throwException = true; -// } -// } else if ( sourceType instanceof ARGBType ) { -// -// // ARGBType --> FloatType -// if ( targetType instanceof ARGBType ) { -// final Cursor< TT > targetCursor = target.localizingCursor(); -// final RandomAccess< ST > sourceRandomAccess = source.randomAccess(); -// double v; -// int intRGB; -// while ( targetCursor.hasNext() ) { -// targetCursor.fwd(); -// sourceRandomAccess.setPosition( targetCursor ); -// intRGB = ( ( ARGBType ) sourceRandomAccess.get() ).get(); -// v = -// 0.2989 * ARGBType.red( intRGB ) + 0.5870 * ARGBType.green( intRGB ) + 0.1140 * ARGBType.blue( intRGB ); -// v /= 255; -// ( ( ARGBType ) targetCursor.get() ).set( ARGBType.rgba( v, v, v, 255 ) ); -// } -// } else { -// throwException = true; -// } -// } else { -// throwException = true; -// } -// -// if ( throwException ) -// throw new Exception( "Convertion between the given NativeTypes not implemented!" ); -// } public static < T extends RealType< T > & NativeType< T > > Img< T > stackThemAsFrames( final List< Img< T > > imageList ) throws ImgIOException, IncompatibleTypeException, Exception { @@ -446,4 +224,30 @@ public static < T extends RealType< T > & NativeType< T > > Img< T > stackThemAs return stack; } + + // -- Helper classes -- + + public static class CopyLoopClass implements CopyLoopInterface { + // NB: This class needs to be public for ClassCopyProvider to work + + @Override + public < S, T > void copy( + RandomAccess< ? extends S > source, Cursor< ? extends T > target, + Converter< S, T > converter) + { + while ( target.hasNext() ) { + target.fwd(); + source.setPosition(target); + converter.convert( source.get(), target.get() ); + } + } + } + + public interface CopyLoopInterface { + // NB: This class needs to be public for ClassCopyProvider to work + + < S, T > void copy( + RandomAccess< ? extends S > source, Cursor< ? extends T > target, + Converter< S, T > converter); + } } diff --git a/src/test/java/com/indago/io/DataMoverBenchmark.java b/src/test/java/com/indago/io/DataMoverBenchmark.java new file mode 100644 index 0000000..f821b34 --- /dev/null +++ b/src/test/java/com/indago/io/DataMoverBenchmark.java @@ -0,0 +1,57 @@ +package com.indago.io; + +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedShortType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import org.junit.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +@State(Scope.Benchmark) +public class DataMoverBenchmark { + + private final long[] dimensions = {1000, 1000}; + + Img floats = ArrayImgs.floats(dimensions); + Img floats2 = ArrayImgs.floats(dimensions); + Img doubles = ArrayImgs.doubles(dimensions); + Img doubles2 = ArrayImgs.doubles(dimensions); + Img shorts = ArrayImgs.unsignedShorts(dimensions); + Img shorts2 = ArrayImgs.unsignedShorts(dimensions); + Img ints = ArrayImgs.ints(dimensions); + + @Benchmark + public void benchmarkConvertAndCopy() throws Exception { + DataMover.convertAndCopy(floats, doubles); + DataMover.convertAndCopy(shorts, ints); + DataMover.convertAndCopy(shorts, floats); + } + + @Benchmark + public void benchmarkCopy() { + DataMover.copy(floats, floats2); + DataMover.copy(doubles, doubles2); + DataMover.copy(shorts, shorts2); + } + public static void main( final String... args ) throws RunnerException + { + final Options opt = new OptionsBuilder() + .include( DataMover.class.getSimpleName() ) + .forks( 0 ) + .warmupIterations( 20 ) + .measurementIterations( 20 ) + .warmupTime( TimeValue.milliseconds( 100 ) ) + .measurementTime( TimeValue.milliseconds( 100 ) ) + .build(); + new Runner( opt ).run(); + } +} diff --git a/src/test/java/com/indago/io/DataMoverTest.java b/src/test/java/com/indago/io/DataMoverTest.java new file mode 100644 index 0000000..ce51b9e --- /dev/null +++ b/src/test/java/com/indago/io/DataMoverTest.java @@ -0,0 +1,111 @@ +package com.indago.io; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.img.list.ListImg; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.ARGBType; +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.UnsignedShortType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DataMoverTest { + + @Test + public void testCopy() { + Img in = ArrayImgs.ints(new int[]{42}, 1); + Img out = ArrayImgs.ints(1); + DataMover.copy(in, (RandomAccessibleInterval) out); + assertEquals(42, out.firstElement().get()); + } + + @Test + public void testCopyWithConverter() { + Img in = ArrayImgs.ints(new int[]{42}, 1); + Img out = ArrayImgs.floats(1); + DataMover.copy(in, (IterableInterval) out, (i,o) -> o.set(i.get())); + assertEquals(42, out.firstElement().get(), 0.0f); + } + + @Test + public void testAdd() { + Img in = ArrayImgs.ints(new int[]{42}, 1); + Img out = ArrayImgs.ints(new int[]{4}, 1); + DataMover.add(in, (RandomAccessibleInterval) out); + assertEquals(46, out.firstElement().get()); + } + + @Test + public void testConvertAndCopyIntTypeToIntType() { + testConvertAndCopy(new IntType(42), new IntType(42)); + } + + @Test + public void testConvertAndCopyFloatTypeToDoubleType() { + testConvertAndCopy(new FloatType(42), new DoubleType(42)); + } + + @Test + public void testConvertAndCopyIntegerTypeToIntType() { + testConvertAndCopy(new ByteType((byte) 42), new IntType(42)); + } + + @Test + public void testConvertAndCopyFloatTypeToIntType() { + testConvertAndCopy(new FloatType(42), new IntType(42)); + } + + @Test + public void testConvertAndCopyRealTypeToFloatType() { + testConvertAndCopy(new IntType(42), new FloatType(42)); + } + + @Test + public void testConvertAndCopyRealTypeToDoubleType() { + testConvertAndCopy(new IntType(42), new DoubleType(42)); + } + + @Test + public void testConvertAndCopyFloatTypeToARGBType() { + testConvertAndCopy(new FloatType(0.5f), new ARGBType(ARGBType.rgba(128, 128, 128, 255))); + } + + @Test + public void testConvertAndCopyUnsignedShortTypeToFloatType() { + testConvertAndCopy(new UnsignedShortType(42), new FloatType(42)); + } + + @Test + public void testConvertAndCopyUnsignedShortTypeToIntType() { + testConvertAndCopy(new UnsignedShortType(42), new IntType(42)); + } + + @Test + public void testConvertAndCopyUnsignedShortTypeToDoubleType() { + testConvertAndCopy(new UnsignedShortType(42), new DoubleType(42)); + } + + private , T extends NativeType> void testConvertAndCopy(S input, T expected) { + Img in = new ListImg<>(Collections.singleton(input), 1); + Img out = new ArrayImgFactory<>(expected.createVariable()).create(1); + try { + DataMover.convertAndCopy(in, out); + } + catch (Exception e) { + throw new RuntimeException(e); + } + assertTrue(expected.valueEquals(out.randomAccess().get())); + } +}