diff --git a/src/main/java/net/imglib2/converter/RealTypeConverterInternals.java b/src/main/java/net/imglib2/converter/RealTypeConverterInternals.java new file mode 100644 index 0000000000..c4be8459c4 --- /dev/null +++ b/src/main/java/net/imglib2/converter/RealTypeConverterInternals.java @@ -0,0 +1,214 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 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.converter; + +import net.imglib2.loops.ClassCopyProvider; +import net.imglib2.type.BooleanType; +import net.imglib2.type.numeric.IntegerType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.GenericByteType; +import net.imglib2.type.numeric.integer.GenericIntType; +import net.imglib2.type.numeric.integer.GenericShortType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.type.numeric.real.FloatType; + +import java.util.Arrays; + +/** + * Implementation of {@link RealTypeConverters#getConverter(RealType, RealType)}. + */ +class RealTypeConverterInternals +{ + static < S extends RealType< ? >, T extends RealType< ? > > Converter< S, T > getConverter( S inputType, T outputType ) + { + final RealTypeConverterInternals.ConverterFactory factory = RealTypeConverterInternals.getConverterFactory( inputType, outputType ); + return factory.create( inputType, outputType ); + } + + private static ConverterFactory TYPE_IDENTITY = new ConverterFactory( TypeIdentity.class ); + + private static ConverterFactory DOUBLE = new ConverterFactory( DoubleConverter.class ); + + private static ConverterFactory FLOAT = new ConverterFactory( FloatConverter.class ); + + private static ConverterFactory INTEGER = new ConverterFactory( IntegerConverter.class ); + + private static ConverterFactory LONG = new ConverterFactory( LongConverter.class ); + + private static ConverterFactory BYTE = new ConverterFactory( ByteConverter.class ); + + private static ConverterFactory SHORT = new ConverterFactory( ShortConverter.class ); + + private static ConverterFactory BOOLEAN = new ConverterFactory( BooleanConverter.class ); + + private static ConverterFactory getConverterFactory( RealType< ? > inputType, RealType< ? > outputType ) + { + if ( inputType.getClass().equals( outputType.getClass() ) ) + return TYPE_IDENTITY; + if ( ( inputType instanceof IntegerType ) && ( outputType instanceof IntegerType ) ) + return integerConverterFactory( inputType, outputType ); + return floatingPointConverterFactory( inputType, outputType ); + } + + private static ConverterFactory floatingPointConverterFactory( RealType< ? > inputType, RealType< ? > outputType ) + { + if ( ( inputType instanceof FloatType ) || ( outputType instanceof FloatType ) ) + return FLOAT; + return DOUBLE; + } + + private static ConverterFactory integerConverterFactory( RealType< ? > inputType, RealType< ? > outputType ) + { + if ( inputType instanceof LongType || outputType instanceof LongType ) + return LONG; + if ( inputType instanceof IntType || outputType instanceof IntType ) + return INTEGER; + if ( inputType instanceof GenericShortType && outputType instanceof GenericShortType ) + return SHORT; + if ( inputType instanceof GenericByteType && outputType instanceof GenericByteType ) + return BYTE; + if ( inputType instanceof BooleanType && outputType instanceof BooleanType ) + return BOOLEAN; + if ( inputType instanceof GenericIntType && smallerThanInt( outputType ) || + smallerThanInt( inputType ) && outputType instanceof GenericIntType ) + return INTEGER; + return LONG; + } + + static boolean smallerThanInt( RealType< ? > variable ) + { + // This method is package-private to allow testing. + final boolean lowerBound = variable.getMinValue() >= Integer.MIN_VALUE; + final boolean upperBound = variable.getMaxValue() <= Integer.MAX_VALUE; + return lowerBound && upperBound; + } + + /** + * The class {@link ConverterFactory} encapsulates the {@link ClassCopyProvider} + * that is used to create a {@link Converter}. + */ + private static class ConverterFactory + { + private final ClassCopyProvider< Converter > provider; + + private ConverterFactory( Class< ? extends Converter > converterClass ) + { + this.provider = new ClassCopyProvider<>( converterClass, Converter.class ); + } + + public Converter create( RealType< ? > in, RealType< ? > out ) + { + return provider.newInstanceForKey( Arrays.asList( in.getClass(), out.getClass() ) ); + } + } + + /** Converts via {@link RealType#getRealDouble()}. */ + public static class DoubleConverter implements Converter< RealType< ? >, RealType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( RealType< ? > input, RealType< ? > output ) + { + output.setReal( input.getRealDouble() ); + } + } + + /** Converts via {@link RealType#getRealFloat()}. */ + public static class FloatConverter implements Converter< RealType< ? >, RealType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( RealType< ? > input, RealType< ? > output ) + { + output.setReal( input.getRealFloat() ); + } + } + + /** Converts via {@link IntegerType#getInteger()}. */ + public static class IntegerConverter implements Converter< IntegerType< ? >, IntegerType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( IntegerType< ? > input, IntegerType< ? > output ) + { + output.setInteger( input.getInteger() ); + } + } + + /** Converts via {@link IntegerType#getInteger()}. */ + public static class LongConverter implements Converter< IntegerType< ? >, IntegerType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( IntegerType< ? > input, IntegerType< ? > output ) + { + output.setInteger( input.getIntegerLong() ); + } + } + + /** Converts via {@link GenericByteType#getByte()}. */ + public static class ByteConverter implements Converter< GenericByteType< ? >, GenericByteType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( GenericByteType< ? > input, GenericByteType< ? > output ) + { + output.setByte( input.getByte() ); + } + } + + /** Converts void {@link GenericShortType#getShort()}. */ + public static class ShortConverter implements Converter< GenericShortType< ? >, GenericShortType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( GenericShortType< ? > input, GenericShortType< ? > output ) + { + output.setShort( input.getShort() ); + } + } + + /** Converts via {@link BooleanType#get()}. */ + public static class BooleanConverter implements Converter< BooleanType< ? >, BooleanType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( BooleanType< ? > input, BooleanType< ? > output ) + { + output.set( input.get() ); + } + } +} diff --git a/src/main/java/net/imglib2/converter/RealTypeConverters.java b/src/main/java/net/imglib2/converter/RealTypeConverters.java new file mode 100644 index 0000000000..10ffeb2780 --- /dev/null +++ b/src/main/java/net/imglib2/converter/RealTypeConverters.java @@ -0,0 +1,130 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 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.converter; + +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.loops.LoopBuilder; +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.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +public final class RealTypeConverters +{ + + private RealTypeConverters() + { + // prevent from instantiation + } + + /** + * Copy the image content from a source image to a destination image. + * The area to be copied is defined by the destination. + *
+ * Both images need to be of type {@link RealType}. So for example + * {@link UnsignedByteType}, {@link IntType}, {@link FloatType} + * and many more types are supported. A type conversion is done + * if needed. + * + * @param source Image that is source of the copy operation. + * @param destination Image that is destination of the copy operation. + */ + public static void copyFromTo( + RandomAccessible< ? extends RealType< ? > > source, + RandomAccessibleInterval< ? extends RealType< ? > > destination + ) + { + IntervalView< ? extends RealType< ? > > sourceInterval = Views.interval( source, destination ); + RealType< ? > s = Util.getTypeFromInterval( sourceInterval ); + RealType< ? > d = Util.getTypeFromInterval( destination ); + Converter< RealType< ? >, RealType< ? > > copy = getConverter( s, d ); + LoopBuilder.setImages( sourceInterval, destination ).forEachPixel( copy::convert ); + } + + /** + * Convert the pixel type of the given image to a given output pixel type. + *
+ * Example, convert and image from {@link UnsignedByteType} to {@link IntType}: + *
+ * {@code + * + * RandomAccessibleInterval+ * + * The conversion is done on-the-fly when a pixel value is red. + * @param image image to convert + * @param pixelType pixel type of the result image + */ + public staticimage = ...; + * RandomAccessibleInterval intImage = + * RealTypeConverters.convert( image, new IntType() ); + * } + *
+ * To get a converter from {@link UnsignedByteType} to {@link LongType} use: + *
+ * {@code + * Converter< UnsignedByteType, LongType > converter = + * RealConverters.getConverter(new UnsignedByteType(), new LongType()); + * } + *
+ * The functionality is equivalent to {@link RealDoubleConverter}.
+ * But the returned converter is faster and has higher numerical precision
+ * than {@link RealDoubleConverter}.
+ * This is because it's optimal for the given pair of types.
+ * A converter from {@link UnsignedByteType} to {@link ByteType}
+ * will for example copy the byte directly and never convert to double.
+ */
+ public static < S extends RealType< ? >, T extends RealType< ? > > Converter< S, T > getConverter( S inputType, T outputType )
+ {
+ return RealTypeConverterInternals.getConverter( inputType, outputType );
+ }
+
+}
diff --git a/src/main/java/net/imglib2/loops/ClassCopyProvider.java b/src/main/java/net/imglib2/loops/ClassCopyProvider.java
index 4af3a2fb82..52ce411ac7 100644
--- a/src/main/java/net/imglib2/loops/ClassCopyProvider.java
+++ b/src/main/java/net/imglib2/loops/ClassCopyProvider.java
@@ -79,7 +79,7 @@ public ClassCopyProvider( final Class< ? extends T > clazz, final Class< T > int
if ( constructors.length == 1 )
return constructors[ 0 ].getParameterTypes();
if ( constructors.length == 0 )
- throw new IllegalArgumentException( "ClassCopyProvider: Public constructor in class (" + clazz.getName() + ") needed." );
+ throw new IllegalArgumentException( "ClassCopyProvider: Class and it's constructor need to be public (" + clazz.getName() + ")." );
throw new IllegalArgumentException( "ClassCopyProvider: Please specify constructor signature." );
}
diff --git a/src/main/java/net/imglib2/type/numeric/integer/LongType.java b/src/main/java/net/imglib2/type/numeric/integer/LongType.java
index 15b8abae96..d81edf4900 100644
--- a/src/main/java/net/imglib2/type/numeric/integer/LongType.java
+++ b/src/main/java/net/imglib2/type/numeric/integer/LongType.java
@@ -39,6 +39,7 @@
import net.imglib2.img.NativeImg;
import net.imglib2.img.basictypeaccess.LongAccess;
import net.imglib2.type.NativeTypeFactory;
+import net.imglib2.util.Util;
/**
* TODO
@@ -133,11 +134,17 @@ public void setBigInteger( final BigInteger b )
set( b.longValue() );
}
+ @Override
+ public void setReal( float real )
+ {
+ set( Util.roundToLong( real ) );
+ }
+
@Override
public double getMaxValue()
{
return Long.MAX_VALUE;
- }
+ } // imprecise
@Override
public double getMinValue()
diff --git a/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java b/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java
index c158f9ddc1..c4bc3d5764 100644
--- a/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java
+++ b/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java
@@ -205,6 +205,22 @@ public BigInteger getBigInteger()
return get();
}
+ @Override
+ public float getRealFloat()
+ {
+ return (float) getRealDouble();
+ }
+
+ @Override
+ public double getRealDouble()
+ {
+ final int k = i * 2;
+ final long lower = dataAccess.getValue( k );
+ final long upper = dataAccess.getValue( k + 1 );
+ return UnsignedLongType.unsignedLongToDouble( lower ) +
+ UnsignedLongType.unsignedLongToDouble( upper ) * Math.pow( 2, 64 );
+ }
+
@Override
public void setInteger( final int value )
{
@@ -227,6 +243,23 @@ public void setBigInteger( final BigInteger b )
set( b );
}
+ @Override
+ public void setReal( float real )
+ {
+ setReal( (double) real );
+ }
+
+ @Override
+ public void setReal( double real )
+ {
+ real = Math.floor( real + 0.5 );
+ final double base = Math.pow( 2, 64 );
+ double upper = Math.floor(real / base );
+ double lower = real - base * upper;
+ set( UnsignedLongType.doubleToUnsignedLong( lower ),
+ UnsignedLongType.doubleToUnsignedLong( upper ));
+ }
+
/**
* The maximum value that can be stored is {@code Math.pow(2, 128) -1},
* which cannot be represented with precision using a double
@@ -463,4 +496,10 @@ public int hashCode()
final int hash2 = Long.hashCode( dataAccess.getValue( k ) );
return Util.combineHash( hash1, hash2 );
}
+
+ @Override
+ public String toString()
+ {
+ return getBigInteger().toString();
+ }
}
diff --git a/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java b/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java
index 1ed47678cf..9026e6026b 100644
--- a/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java
+++ b/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java
@@ -221,6 +221,12 @@ public void setBigInteger( final BigInteger b )
set( b.longValue() );
}
+ @Override
+ public void setReal( float real )
+ {
+ set( Util.roundToLong( real ) );
+ }
+
@Override
public double getMaxValue()
{
diff --git a/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java b/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java
index 547fbdc33d..fb479742ae 100644
--- a/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java
+++ b/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java
@@ -55,6 +55,8 @@ public class UnsignedLongType extends GenericLongType< UnsignedLongType >
private static final double MAX_VALUE_PLUS_ONE = Math.pow( 2, 64 ); // not precise, because double is not sufficient
+ private static final double MAX_LONG_PLUS_ONE = Math.pow( 2, 63 ); // not precise, because double is not sufficient
+
// this is the constructor if you want it to read from an array
public UnsignedLongType( final NativeImg< ?, ? extends LongAccess > img )
{
@@ -207,7 +209,7 @@ public void dec()
@Override
public String toString()
{
- return "" + get();
+ return getBigInteger().toString();
}
/**
@@ -265,6 +267,26 @@ public void setBigInteger( final BigInteger b )
set( b.longValue() );
}
+ @Override
+ public void setReal( double real )
+ {
+ set( doubleToUnsignedLong( real ) );
+ }
+
+ static long doubleToUnsignedLong( double real )
+ {
+ double value = real < MAX_LONG_PLUS_ONE ?
+ Math.max(0, real) :
+ Math.min(-1, real - MAX_VALUE_PLUS_ONE);
+ return Util.round( value );
+ }
+
+ @Override
+ public void setReal( float real )
+ {
+ setReal( (double) real );
+ }
+
public void set( final BigInteger bi )
{
set( bi.longValue() );
@@ -336,7 +358,11 @@ public float getRealFloat()
@Override
public double getRealDouble()
{
- long l = get();
+ return unsignedLongToDouble( get() );
+ }
+
+ static double unsignedLongToDouble( long l )
+ {
return l >= 0 ? l : (MAX_VALUE_PLUS_ONE + l);
}
@@ -345,4 +371,5 @@ public int compareTo( final UnsignedLongType other )
{
return Long.compareUnsigned( get(), other.get() );
}
+
}
diff --git a/src/main/java/net/imglib2/util/Util.java b/src/main/java/net/imglib2/util/Util.java
index 312f731e15..4d5cdbaa89 100644
--- a/src/main/java/net/imglib2/util/Util.java
+++ b/src/main/java/net/imglib2/util/Util.java
@@ -500,14 +500,35 @@ else if ( a + threshold > b && a - threshold < b )
public static int round( final float value )
{
- return ( int ) ( value + ( 0.5f * Math.signum( value ) ) );
+ return roundToInt( value );
}
public static long round( final double value )
+ {
+ return roundToLong( value );
+ }
+
+ public static int roundToInt( final float value )
+ {
+ return ( int ) ( value + ( 0.5f * Math.signum( value ) ) );
+ }
+
+ public static int roundToInt( final double value )
+ {
+ return ( int ) ( value + ( 0.5d * Math.signum( value ) ) );
+ }
+
+ public static long roundToLong( final float value )
+ {
+ return ( long ) ( value + ( 0.5f * Math.signum( value ) ) );
+ }
+
+ public static long roundToLong( final double value )
{
return ( long ) ( value + ( 0.5d * Math.signum( value ) ) );
}
+
/**
* This method creates a gaussian kernel
*
diff --git a/src/test/java/net/imglib2/converter/RealTypeConverterInternalsTest.java b/src/test/java/net/imglib2/converter/RealTypeConverterInternalsTest.java
new file mode 100644
index 0000000000..5b3e6f9603
--- /dev/null
+++ b/src/test/java/net/imglib2/converter/RealTypeConverterInternalsTest.java
@@ -0,0 +1,204 @@
+/*
+ * #%L
+ * ImgLib2: a general-purpose, multidimensional image processing library.
+ * %%
+ * Copyright (C) 2009 - 2018 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.converter;
+
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.array.ArrayImgs;
+import net.imglib2.test.ImgLib2Assert;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.logic.BoolType;
+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.real.DoubleType;
+import net.imglib2.type.numeric.real.FloatType;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test {@link RealTypeConverterInternals}.
+ *
+ * @author Matthias Arzt
+ */
+public class RealTypeConverterInternalsTest
+{
+ public static final List< RealType< ? > > TYPES = Arrays.asList(
+ new BitType(),
+ new BoolType(),
+ new ByteType(),
+ new ShortType(),
+ new IntType(),
+ new LongType(),
+ new Unsigned2BitType(),
+ new Unsigned4BitType(),
+ new Unsigned12BitType(),
+ new UnsignedByteType(),
+ new UnsignedShortType(),
+ new UnsignedIntType(),
+ new UnsignedLongType(),
+ new Unsigned128BitType(),
+ new FloatType(),
+ new DoubleType()
+ );
+
+ private static final float HIGHEST_INTEGER_CORRECTLY_REPRESENTED_BY_FLOAT = ( float ) Math.pow( 2, 24 );
+
+ private static final float FLOAT_PRECISION = ( float ) Math.pow( 2.0, -24 );
+
+ @Test
+ public void testConvertUnsignedByteToFloatType()
+ {
+ UnsignedByteType in = new UnsignedByteType( 42 );
+ FloatType out = new FloatType();
+ Converter< UnsignedByteType, FloatType > converter = RealTypeConverters.getConverter( in, out );
+ converter.convert( in, out );
+ assertEquals( 42, out.getRealFloat(), 0 );
+ }
+
+ @Test
+ public void testConvertImportantValuesBetweenAllTypes()
+ {
+ for ( RealType< ? > in : TYPES )
+ for ( RealType< ? > out : TYPES )
+ {
+ Converter< RealType< ? >, RealType< ? > > converter = RealTypeConverters.getConverter( in, out );
+ testMinValueConversion( converter, in.createVariable(), out.createVariable() );
+ testMaxValueConversion( converter, in.createVariable(), out.createVariable() );
+ testValueConversion( 0.0, converter, in, out );
+ testValueConversion( 1.0, converter, in, out );
+ }
+ }
+
+ private void testMinValueConversion( Converter< RealType< ? >, RealType< ? > > converter, RealType< ? > in, RealType< ? > out )
+ {
+ double value = Math.max( in.getMinValue(), out.getMinValue() );
+ testValueConversion( value, converter, in, out );
+ }
+
+ private void testMaxValueConversion( Converter< RealType< ? >, RealType< ? > > converter, RealType< ? > in, RealType< ? > out )
+ {
+ final double inMaxValue = in.getMaxValue();
+ final double outMaxValue = out.getMaxValue();
+ double value = decreaseMaxValue( Math.min( inMaxValue, outMaxValue ) );
+ testValueConversion( value, converter, in, out );
+ }
+
+ private double decreaseMaxValue( double value )
+ {
+ // NB: If an integer value is two high it cannot be represented
+ // correctly by float. This tends to round up Integer.MAX_VALUE
+ // Long.MAX_VALUE ... etc. The consequence is an integer overflow.
+ // The value is decreased by the float precision, to compensate
+ // for the rounding up.
+ if ( value <= HIGHEST_INTEGER_CORRECTLY_REPRESENTED_BY_FLOAT )
+ return value;
+ return value * ( 1.0 - FLOAT_PRECISION );
+ }
+
+ private void testValueConversion( double value, Converter< RealType< ? >, RealType< ? > > converter, RealType< ? > in, RealType< ? > out )
+ {
+ in.setReal( value );
+ converter.convert( in, out );
+ double delta = value * FLOAT_PRECISION;
+ assertEquals( "Conversion of value: " + value +
+ " from: " + in.getClass().getSimpleName() + " (" + in + ")" +
+ " to: " + out.getClass().getSimpleName() + " (" + out + ")",
+ in.getRealDouble(), out.getRealDouble(), delta );
+ }
+
+ @Test
+ public void testSmallerThanInt()
+ {
+ assertTrue( RealTypeConverterInternals.smallerThanInt( new IntType() ) );
+ assertFalse( RealTypeConverterInternals.smallerThanInt( new UnsignedIntType() ) );
+ assertFalse( RealTypeConverterInternals.smallerThanInt( new UnsignedLongType() ) );
+ assertFalse( RealTypeConverterInternals.smallerThanInt( new LongType() ) );
+ assertFalse( RealTypeConverterInternals.smallerThanInt( new Unsigned128BitType() ) );
+ }
+
+ @Test
+ public void testTypeIdentityConverters()
+ {
+ for ( RealType< ? > type : TYPES )
+ testConverterType( type.createVariable(), type.createVariable(), TypeIdentity.class );
+ }
+
+ @Test
+ public void testOptimalConverterTypes()
+ {
+ testConverterType( new BoolType(), new BitType(), RealTypeConverterInternals.BooleanConverter.class );
+ testConverterType( new UnsignedByteType(), new ByteType(), RealTypeConverterInternals.ByteConverter.class );
+ testConverterType( new UnsignedShortType(), new ShortType(), RealTypeConverterInternals.ShortConverter.class );
+ testConverterType( new UnsignedIntType(), new IntType(), RealTypeConverterInternals.IntegerConverter.class );
+ testConverterType( new UnsignedLongType(), new LongType(), RealTypeConverterInternals.LongConverter.class );
+ testConverterType( new Unsigned12BitType(), new LongType(), RealTypeConverterInternals.LongConverter.class );
+ testConverterType( new Unsigned12BitType(), new UnsignedLongType(), RealTypeConverterInternals.LongConverter.class );
+ testConverterType( new Unsigned12BitType(), new IntType(), RealTypeConverterInternals.IntegerConverter.class );
+ testConverterType( new Unsigned12BitType(), new UnsignedIntType(), RealTypeConverterInternals.IntegerConverter.class );
+ testConverterType( new Unsigned12BitType(), new ShortType(), RealTypeConverterInternals.LongConverter.class );
+ testConverterType( new BoolType(), new FloatType(), RealTypeConverterInternals.FloatConverter.class );
+ testConverterType( new BoolType(), new IntType(), RealTypeConverterInternals.IntegerConverter.class );
+ }
+
+ @Test
+ public void testConvertRandomAccessibleInterval() {
+ RandomAccessibleInterval
+ * The measurement is done for all combinations of the types
+ * listed in {@link #TYPES}.
+ */
+@State( Scope.Benchmark )
+public class RealTypeConvertersBenchmark
+{
+
+ private static final Map< String, RealType< ? > > TYPES = Stream.of(
+ new DoubleType(),
+ new FloatType(),
+ new IntType(),
+ new UnsignedLongType()
+ ).collect( Collectors.toMap( x -> x.getClass().getSimpleName(), v -> v ) );
+
+ private RealType< ? > in;
+
+ private RealType< ? > out;
+
+ private Converter< RealType< ? >, RealType< ? > > converter;
+
+ @Param( { "DoubleType" } )
+ public String srcType;
+
+ @Param( { "DoubleType" } )
+ public String targetType;
+
+ @Setup( Level.Trial )
+ public void setup()
+ {
+ in = TYPES.get( srcType );
+ out = TYPES.get( targetType );
+ converter = RealTypeConverters.getConverter( in, out );
+ }
+
+ @Benchmark
+ public void converter()
+ {
+ converter.convert( in, out );
+ }
+
+ public static void main( final String... args ) throws RunnerException
+ {
+ final String[] values = TYPES.keySet().toArray( new String[ 0 ] );
+ final Options opt = new OptionsBuilder()
+ .include( RealTypeConvertersBenchmark.class.getSimpleName() )
+ .forks( 1 )
+ .warmupIterations( 4 )
+ .measurementIterations( 8 )
+ .warmupTime( TimeValue.milliseconds( 100 ) )
+ .measurementTime( TimeValue.milliseconds( 100 ) )
+ .param( "srcType", values )
+ .param( "targetType", values )
+ .build();
+ new Runner( opt ).run();
+ }
+}
diff --git a/src/test/java/net/imglib2/converter/RealTypeConvertersTest.java b/src/test/java/net/imglib2/converter/RealTypeConvertersTest.java
new file mode 100644
index 0000000000..a2760b7237
--- /dev/null
+++ b/src/test/java/net/imglib2/converter/RealTypeConvertersTest.java
@@ -0,0 +1,89 @@
+/*
+ * #%L
+ * ImgLib2: a general-purpose, multidimensional image processing library.
+ * %%
+ * Copyright (C) 2009 - 2018 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.converter;
+
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.Img;
+import net.imglib2.img.array.ArrayImgs;
+import net.imglib2.test.ImgLib2Assert;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import net.imglib2.type.numeric.real.DoubleType;
+import net.imglib2.type.numeric.real.FloatType;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link RealTypeConverters}.
+ *
+ * @author Matthias Arzt
+ */
+public class RealTypeConvertersTest
+{
+
+ @Test
+ public void testGetConverter() {
+ UnsignedByteType input = new UnsignedByteType( 42 );
+ DoubleType output = new DoubleType();
+ Converter
+ * It's a parameterized test, see Junit4
+ * Parameterized Test. The test is executed for every type in the list
+ * {@link IntegerTypeTest#types}.
+ *
+ * @param