Skip to content

Commit b4fcd44

Browse files
authored
Merge pull request #219 from maarzt/add-unit-test-utils
Asserts And Radomized Images for JUnit Tests with ImgLib2
2 parents 43f4b1a + 0eefb73 commit b4fcd44

File tree

6 files changed

+726
-6
lines changed

6 files changed

+726
-6
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* #%L
3+
* ImgLib2: a general-purpose, multidimensional image processing library.
4+
* %%
5+
* Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
6+
* John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
7+
* Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
8+
* Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
9+
* Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
10+
* Jean-Yves Tinevez and Michael Zinsmaier.
11+
* %%
12+
* Redistribution and use in source and binary forms, with or without
13+
* modification, are permitted provided that the following conditions are met:
14+
*
15+
* 1. Redistributions of source code must retain the above copyright notice,
16+
* this list of conditions and the following disclaimer.
17+
* 2. Redistributions in binary form must reproduce the above copyright notice,
18+
* this list of conditions and the following disclaimer in the documentation
19+
* and/or other materials provided with the distribution.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
25+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
* #L%
33+
*/
34+
35+
package net.imglib2.test;
36+
37+
import net.imglib2.Cursor;
38+
import net.imglib2.Interval;
39+
import net.imglib2.Localizable;
40+
import net.imglib2.RandomAccessibleInterval;
41+
import net.imglib2.type.numeric.IntegerType;
42+
import net.imglib2.type.numeric.RealType;
43+
import net.imglib2.type.operators.ValueEquals;
44+
import net.imglib2.util.Intervals;
45+
import net.imglib2.util.Pair;
46+
import net.imglib2.view.IntervalView;
47+
import net.imglib2.view.Views;
48+
49+
import java.util.Arrays;
50+
import java.util.StringJoiner;
51+
import java.util.function.BiPredicate;
52+
53+
public class ImgLib2Assert
54+
{
55+
private ImgLib2Assert()
56+
{
57+
// prevent from instantiation
58+
}
59+
60+
/**
61+
* Throws an AssertionError, if the content or intervals of the two images differ.
62+
* Comparision is done pixel wise using {@link ValueEquals#valueEquals(Object)}.
63+
*/
64+
public static < A extends ValueEquals< B >, B >
65+
void assertImageEquals( final RandomAccessibleInterval< ? extends A > actual, final RandomAccessibleInterval< ? extends B > expected )
66+
{
67+
assertImageEquals( actual, expected, ValueEquals::valueEquals );
68+
}
69+
70+
/**
71+
* Throws an AssertionError, if the content or intervals of the two images differ.
72+
* Comparision is done pixel wise. Two pixels are considered equal, if the values
73+
* returned by {@link RealType#getRealDouble()} differ by less than "tolerance".
74+
*/
75+
public static void assertImageEqualsRealType( final RandomAccessibleInterval< ? extends RealType< ? > > actual, final RandomAccessibleInterval< ? extends RealType< ? > > expected, double tolerance )
76+
{
77+
assertImageEquals( actual, expected, ( a, e ) -> Math.abs( a.getRealDouble() - e.getRealDouble() ) <= tolerance );
78+
}
79+
80+
/**
81+
* Throws an AssertionError, if the content or intervals of the two images differ.
82+
* Comparision is done pixel wise. Two pixels are considered equal, if the values
83+
* returned by {@link IntegerType#getIntegerLong()} are equal.
84+
*/
85+
public static void assertImageEqualsIntegerType( final RandomAccessibleInterval< ? extends IntegerType< ? > > actual, final RandomAccessibleInterval< ? extends IntegerType< ? > > expected )
86+
{
87+
assertImageEquals( actual, expected, ( a, e ) -> a.getIntegerLong() == e.getIntegerLong() );
88+
}
89+
90+
/**
91+
* Throws an AssertionError, if the content or intervals of the two images differ.
92+
* Comparision is done pixel wise. Two pixels are considered equal, if the give
93+
* predicate returns true.
94+
*/
95+
public static < A, B >
96+
void assertImageEquals( final RandomAccessibleInterval< ? extends A > a, final RandomAccessibleInterval< ? extends B > b, BiPredicate< A, B > equals )
97+
{
98+
assertIntervalEquals( a, b );
99+
IntervalView< ? extends Pair< ? extends A, ? extends B > > pairs = Views.interval( Views.pair( a, b ), b );
100+
Cursor< ? extends Pair< ? extends A, ? extends B > > cursor = pairs.cursor();
101+
while ( cursor.hasNext() )
102+
{
103+
Pair< ? extends A, ? extends B > p = cursor.next();
104+
if ( !equals.test( p.getA(), p.getB() ) )
105+
fail( "Pixel values differ on coordinate " +
106+
positionToString( cursor ) + ", expected: "
107+
+ p.getA() + " actual: " + p.getB() );
108+
}
109+
}
110+
111+
/**
112+
* Throws an AssertionError, if the two Intervals differ.
113+
*/
114+
public static void assertIntervalEquals( Interval a, Interval b )
115+
{
116+
if ( !Intervals.equals( a, b ) )
117+
fail( "Intervals are different, expected: " + intervalToString( a ) + ", actual: " + intervalToString( b ) );
118+
}
119+
120+
// -- Helper methods --
121+
122+
private static String positionToString( Localizable localizable )
123+
{
124+
StringJoiner joiner = new StringJoiner( ", " );
125+
for ( int i = 0, n = localizable.numDimensions(); i < n; i++ )
126+
joiner.add( String.valueOf( localizable.getIntPosition( i ) ) );
127+
return "(" + joiner + ")";
128+
}
129+
130+
static String intervalToString( Interval a )
131+
{
132+
return "{min=" + Arrays.toString( Intervals.minAsLongArray( a ) ) +
133+
", max=" + Arrays.toString( Intervals.maxAsLongArray( a ) ) + "}";
134+
}
135+
136+
private static void fail( String message )
137+
{
138+
throw new AssertionError( message );
139+
}
140+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* #%L
3+
* ImgLib2: a general-purpose, multidimensional image processing library.
4+
* %%
5+
* Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
6+
* John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
7+
* Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
8+
* Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
9+
* Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
10+
* Jean-Yves Tinevez and Michael Zinsmaier.
11+
* %%
12+
* Redistribution and use in source and binary forms, with or without
13+
* modification, are permitted provided that the following conditions are met:
14+
*
15+
* 1. Redistributions of source code must retain the above copyright notice,
16+
* this list of conditions and the following disclaimer.
17+
* 2. Redistributions in binary form must reproduce the above copyright notice,
18+
* this list of conditions and the following disclaimer in the documentation
19+
* and/or other materials provided with the distribution.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
25+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
* #L%
33+
*/
34+
35+
package net.imglib2.test;
36+
37+
import net.imglib2.Interval;
38+
import net.imglib2.RandomAccessibleInterval;
39+
import net.imglib2.img.Img;
40+
import net.imglib2.img.array.ArrayImgFactory;
41+
import net.imglib2.type.NativeType;
42+
import net.imglib2.type.numeric.ARGBType;
43+
import net.imglib2.type.numeric.IntegerType;
44+
import net.imglib2.type.numeric.integer.AbstractIntegerBitType;
45+
import net.imglib2.type.numeric.integer.ByteType;
46+
import net.imglib2.type.numeric.integer.IntType;
47+
import net.imglib2.type.numeric.integer.LongType;
48+
import net.imglib2.type.numeric.integer.ShortType;
49+
import net.imglib2.type.numeric.integer.UnsignedByteType;
50+
import net.imglib2.type.numeric.integer.UnsignedIntType;
51+
import net.imglib2.type.numeric.integer.UnsignedLongType;
52+
import net.imglib2.type.numeric.integer.UnsignedShortType;
53+
import net.imglib2.type.numeric.integer.UnsignedVariableBitLengthType;
54+
import net.imglib2.type.numeric.real.DoubleType;
55+
import net.imglib2.type.numeric.real.FloatType;
56+
import net.imglib2.util.Intervals;
57+
import net.imglib2.util.Util;
58+
import net.imglib2.view.Views;
59+
60+
import java.util.Random;
61+
import java.util.function.Consumer;
62+
63+
public class RandomImgs
64+
{
65+
66+
private final Random random;
67+
68+
public static RandomImgs seed( int seed )
69+
{
70+
return new RandomImgs( seed );
71+
}
72+
73+
private RandomImgs( int seed )
74+
{
75+
this.random = new Random( seed );
76+
}
77+
78+
/**
79+
* Creates an image with randomized content.
80+
* @param type Pixel type
81+
* @param interval Interval
82+
*/
83+
public < T extends NativeType< T > > RandomAccessibleInterval< T > nextImage( final T type, Interval interval )
84+
{
85+
long[] sizes = Intervals.dimensionsAsLongArray( interval );
86+
long[] min = Intervals.minAsLongArray( interval );
87+
return Views.translate( nextImage( type, sizes ), min );
88+
}
89+
90+
/**
91+
* Creates an image with randomized content
92+
* @param type Pixel type
93+
* @param dims Dimensions
94+
*/
95+
public < T extends NativeType< T > > Img< T > nextImage( final T type, final long... dims )
96+
{
97+
final Img< T > result = new ArrayImgFactory<>( type ).create( dims );
98+
return randomize( result );
99+
}
100+
101+
/**
102+
* Randomizes the content of the given image.
103+
* @return Reference to the given image
104+
*/
105+
public < I extends RandomAccessibleInterval< T >, T >
106+
I randomize( final I image )
107+
{
108+
final T type = Util.getTypeFromInterval( image );
109+
Views.iterable( image ).forEach( randomSetter( type ) );
110+
return image;
111+
}
112+
113+
private < T > Consumer< T > randomSetter( final T type )
114+
{
115+
if ( ( type instanceof UnsignedByteType )
116+
|| ( type instanceof ByteType )
117+
|| ( type instanceof ShortType )
118+
|| ( type instanceof UnsignedShortType )
119+
|| ( type instanceof IntType ) )
120+
return b -> ( ( IntegerType< ? > ) b ).setInteger( random.nextInt() );
121+
if ( ( type instanceof UnsignedLongType )
122+
|| ( type instanceof UnsignedIntType )
123+
|| ( type instanceof AbstractIntegerBitType )
124+
|| ( type instanceof UnsignedVariableBitLengthType )
125+
|| ( type instanceof LongType ) )
126+
return b -> ( ( IntegerType< ? > ) b ).setInteger( random.nextLong() );
127+
if ( type instanceof ARGBType )
128+
return b -> ( ( ARGBType ) b ).set( random.nextInt() );
129+
if ( type instanceof FloatType )
130+
return b -> ( ( FloatType ) b ).setReal( random.nextFloat() );
131+
if ( type instanceof DoubleType )
132+
return b -> ( ( DoubleType ) b ).setReal( random.nextDouble() );
133+
throw new UnsupportedOperationException( "Randomization of type: " + type.getClass() + " is not supported." );
134+
}
135+
}

src/main/java/net/imglib2/util/Util.java

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434

3535
package net.imglib2.util;
3636

37-
import java.util.List;
38-
3937
import net.imglib2.Dimensions;
4038
import net.imglib2.Interval;
4139
import net.imglib2.IterableInterval;
@@ -47,13 +45,21 @@
4745
import net.imglib2.RealLocalizable;
4846
import net.imglib2.RealRandomAccess;
4947
import net.imglib2.RealRandomAccessible;
48+
import net.imglib2.img.Img;
5049
import net.imglib2.img.ImgFactory;
5150
import net.imglib2.img.array.ArrayImg;
5251
import net.imglib2.img.array.ArrayImgFactory;
5352
import net.imglib2.img.cell.CellImgFactory;
5453
import net.imglib2.img.list.ListImgFactory;
5554
import net.imglib2.type.NativeType;
5655
import net.imglib2.type.Type;
56+
import net.imglib2.type.numeric.RealType;
57+
import net.imglib2.type.operators.ValueEquals;
58+
import net.imglib2.view.Views;
59+
60+
import java.util.List;
61+
import java.util.function.BiPredicate;
62+
import java.util.stream.StreamSupport;
5763

5864
/**
5965
* A collection of general-purpose utility methods for working with ImgLib2 data
@@ -953,6 +959,28 @@ public static boolean locationsEqual( final RealLocalizable l1, final RealLocali
953959
return true;
954960
}
955961

962+
/**
963+
* Checks if both images have equal intervals and content.
964+
*/
965+
public static < T extends ValueEquals< U >, U > boolean imagesEqual( final RandomAccessibleInterval< ? extends T > a, final RandomAccessibleInterval< ? extends U > b )
966+
{
967+
return imagesEqual( a, b, ValueEquals::valueEquals );
968+
}
969+
970+
/**
971+
* Checks if both images have equal intervals and content.
972+
* A predicate must be given to check if two pixels are equal.
973+
*/
974+
public static < T, U > boolean imagesEqual( final RandomAccessibleInterval< ? extends T > a, final RandomAccessibleInterval< ? extends U > b, BiPredicate< T, U > pixelEquals )
975+
{
976+
if ( !Intervals.equals( a, b ) )
977+
return false;
978+
for ( Pair< ? extends T, ? extends U > pair : Views.interval( Views.pair( a, b ), b ) )
979+
if ( !pixelEquals.test( pair.getA(), pair.getB() ) )
980+
return false;
981+
return true;
982+
}
983+
956984
/**
957985
* Writes min(a,b) into a
958986
*
@@ -978,4 +1006,30 @@ final static public void max( final double[] a, final double[] b )
9781006
if ( b[ i ] > a[ i ] )
9791007
a[ i ] = b[ i ];
9801008
}
1009+
1010+
/**
1011+
* Returns the content of {@code Iterable<RealType>} as array of doubles.
1012+
*/
1013+
public static double[] asDoubleArray( Iterable< ? extends RealType< ? > > iterable )
1014+
{
1015+
return StreamSupport.stream( iterable.spliterator(), false ).mapToDouble( RealType::getRealDouble ).toArray();
1016+
}
1017+
1018+
/**
1019+
* Returns the pixels of an RandomAccessibleInterval of RealType as array of doubles.
1020+
* The pixels are sorted in flat iteration order.
1021+
*/
1022+
public static double[] asDoubleArray( RandomAccessibleInterval< ? extends RealType< ? > > rai )
1023+
{
1024+
return asDoubleArray( Views.flatIterable( rai ) );
1025+
}
1026+
1027+
/**
1028+
* Returns the pixels of an image of RealType as array of doubles.
1029+
* The pixels are sorted in flat iteration order.
1030+
*/
1031+
public static double[] asDoubleArray( Img< ? extends RealType< ? > > image )
1032+
{
1033+
return asDoubleArray( ( RandomAccessibleInterval< ? extends RealType< ? > > ) image );
1034+
}
9811035
}

0 commit comments

Comments
 (0)