diff --git a/src/main/java/net/imglib2/view/Views.java b/src/main/java/net/imglib2/view/Views.java index 9b1ac8bc0..ff1e3aaa4 100644 --- a/src/main/java/net/imglib2/view/Views.java +++ b/src/main/java/net/imglib2/view/Views.java @@ -82,6 +82,7 @@ import net.imglib2.view.composite.InterleaveView; import net.imglib2.view.composite.NumericComposite; import net.imglib2.view.composite.RealComposite; +import net.imglib2.view.composite.RealStackCompositeView; /** * Create light-weight views into {@link RandomAccessible RandomAccessibles}. @@ -1137,6 +1138,36 @@ public static < T > RandomAccessibleInterval< T > stack( final StackAccessMode s return new StackView<>( Arrays.asList( hyperslices ), stackAccessMode ); } + /** + * Form a {@link RealRandomAccessible} of {@link Composite}s from n + * {@link RealRandomAccessible}s. + * + * @param stackAccessMode + * describes how a {@link RealRandomAccess} on the output + * {@link RealRandonAccessible} maps position changes into + * position changes of the underlying {@link RealRandomAccess}es. + * @param components + * a list of n-dimensional {@link RealRandomAccessible}s + * @return + */ + public static < T > RealRandomAccessible> realComposite( final StackAccessMode stackAccessMode, final RealRandomAccessible< T >... components) + { + return new RealStackCompositeView( components, stackAccessMode ); + } + + /** + * Form a {@link RealRandomAccessible} of {@link Composite}s from n + * {@link RealRandomAccessible}s. + * + * @param components + * a list of n-dimensional {@link RealRandomAccessible}s + * @return + */ + public static < T > RealRandomAccessible> realComposite( final RealRandomAccessible< T >... components) + { + return new RealStackCompositeView( components ); + } + /** * Positive shear transform of a RandomAccessible using * {@link ShearTransform}, i.e. c[ shearDimension ] = c[ shearDimension ] + diff --git a/src/main/java/net/imglib2/view/composite/RealStackCompositeView.java b/src/main/java/net/imglib2/view/composite/RealStackCompositeView.java new file mode 100644 index 000000000..7a9daddc3 --- /dev/null +++ b/src/main/java/net/imglib2/view/composite/RealStackCompositeView.java @@ -0,0 +1,483 @@ +package net.imglib2.view.composite; + +import net.imglib2.Localizable; +import net.imglib2.RealInterval; +import net.imglib2.RealLocalizable; +import net.imglib2.RealRandomAccess; +import net.imglib2.RealRandomAccessible; +import net.imglib2.View; +import net.imglib2.view.StackView.StackAccessMode; + +/** + * A {@link RealStackCompositeView} converts an array of {@link RealRandomAccessible}s + * of T into a RealRandomAccessible of {@link Composite} of T. + *

+ * This view has the same dimensionality as each source RealRandomAccessible, and + * at every position, the i-th value of the composite is the value for the i-th input RealRandomAccessible. + * + * @author John Bogovic + */ +public class RealStackCompositeView< T > implements RealRandomAccessible>, View +{ + protected final int n, nd; + + protected final RealRandomAccessible< T >[] sources; + + private final StackAccessMode stackAccessMode; + + /** + * Creates a RealStackCompositeView. Every input {@link RealRandomAccessible} must + * have the same dimensionality, and be of the same type. + * + * @param sources the list of RealRandomAccessibles + * @param stackAccessMode the mode + */ + public RealStackCompositeView( final RealRandomAccessible< T >[] sources, StackAccessMode stackAccessMode ) + { + assert( sources.length > 0 ); + this.sources = sources; + n = sources.length; + nd = sources[0].numDimensions(); + this.stackAccessMode = stackAccessMode; + } + + /** + * Creates a RealStackCompositeView. Every input {@link RealRandomAccessible} must + * have the same dimensionality, and be of the same type. Uses the DEFAULT + * {@link StackAccessMode}. + * + * @param sources the list of RealRandomAccessibles + */ + public RealStackCompositeView( final RealRandomAccessible< T >[] sources ) + { + this( sources, StackAccessMode.DEFAULT ); + } + + @Override + public int numDimensions() + { + return nd; + } + + @Override + public RealRandomAccess< Composite > realRandomAccess() + { + return stackAccessMode == StackAccessMode.MOVE_ALL_SLICE_ACCESSES ? + new CompositeRealRandomAccessMoveAll() : + new CompositeRealRandomAccessDefault(); + } + + @Override + public RealRandomAccess< Composite > realRandomAccess( RealInterval interval ) + { + return realRandomAccess(); + } + + public class CompositeRealRandomAccessMoveAll implements RealRandomAccess< Composite >, Composite + { + final protected RealRandomAccess< T >[] sourceAccesses; + + @SuppressWarnings( "unchecked" ) + public CompositeRealRandomAccessMoveAll() + { + sourceAccesses = new RealRandomAccess[ n ]; + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ] = sources[ i ].realRandomAccess(); + } + + @SuppressWarnings( "unchecked" ) + protected CompositeRealRandomAccessMoveAll( final CompositeRealRandomAccessMoveAll other ) + { + sourceAccesses = new RealRandomAccess[ n ]; + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ] = other.sourceAccesses[i].copy(); + } + + @Override + public int numDimensions() + { + return n; + } + + @Override + public Composite get() + { + return this; + } + + @Override + public CompositeRealRandomAccessMoveAll copy() + { + return new CompositeRealRandomAccessMoveAll( this ); + } + + @Override + public double getDoublePosition( int d ) + { + return sourceAccesses[0].getDoublePosition( d ); + } + + @Override + public void move( float distance, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance, d ); + } + + @Override + public void move( double distance, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance, d ); + } + + @Override + public void move( RealLocalizable distance ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance ); + } + + @Override + public void move( float[] distance ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance ); + } + + @Override + public void move( double[] distance ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance ); + } + + @Override + public void setPosition( RealLocalizable position ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position ); + } + + @Override + public void setPosition( float[] position ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position ); + } + + @Override + public void setPosition( double[] position ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position ); + } + + @Override + public void setPosition( float position, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position, d ); + } + + @Override + public void setPosition( double position, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position, d ); + } + + @Override + public void fwd( int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].fwd( d ); + } + + @Override + public void bck( int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].bck( d ); + } + + @Override + public void move( int distance, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance, d ); + } + + @Override + public void move( long distance, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance, d ); + } + + @Override + public void move( Localizable distance ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance ); + } + + @Override + public void move( int[] distance ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance ); + } + + @Override + public void move( long[] distance ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].move( distance ); + } + + @Override + public void setPosition( Localizable position ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position ); + } + + @Override + public void setPosition( int[] position ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position ); + } + + @Override + public void setPosition( long[] position ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position ); + } + + @Override + public void setPosition( int position, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position, d ); + } + + @Override + public void setPosition( long position, int d ) + { + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ].setPosition( position, d ); + } + + @Override + public T get( long i ) + { + return sourceAccesses[ (int)i ].get(); + } + } + + public class CompositeRealRandomAccessDefault implements RealRandomAccess< Composite >, Composite + { + final protected RealRandomAccess< T >[] sourceAccesses; + + protected int currentIndex = 0; + + protected RealRandomAccess< T > currentAccess; + + @SuppressWarnings( "unchecked" ) + public CompositeRealRandomAccessDefault() + { + sourceAccesses = new RealRandomAccess[ n ]; + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ] = sources[ i ].realRandomAccess(); + + currentIndex = 0; + currentAccess = sourceAccesses[ currentIndex ]; + } + + @SuppressWarnings( "unchecked" ) + protected CompositeRealRandomAccessDefault( final CompositeRealRandomAccessDefault other ) + { + sourceAccesses = new RealRandomAccess[ n ]; + for ( int i = 0; i < n; i++ ) + sourceAccesses[ i ] = other.sourceAccesses[i].copy(); + + currentIndex = other.currentIndex; + currentAccess = sourceAccesses[currentIndex]; + } + + @Override + public int numDimensions() + { + return n; + } + + @Override + public Composite get() + { + return this; + } + + @Override + public CompositeRealRandomAccessDefault copy() + { + return new CompositeRealRandomAccessDefault( this ); + } + + @Override + public double getDoublePosition( int d ) + { + return sourceAccesses[0].getDoublePosition( d ); + } + + @Override + public void move( float distance, int d ) + { + currentAccess.move( distance, d ); + } + + @Override + public void move( double distance, int d ) + { + currentAccess.move( distance, d ); + } + + @Override + public void move( RealLocalizable distance ) + { + currentAccess.move( distance ); + } + + @Override + public void move( float[] distance ) + { + for ( int i = 0; i < n; i++ ) + currentAccess.move( distance ); + } + + @Override + public void move( double[] distance ) + { + currentAccess.move( distance ); + } + + @Override + public void setPosition( RealLocalizable position ) + { + currentAccess.setPosition( position ); + } + + @Override + public void setPosition( float[] position ) + { + currentAccess.setPosition( position ); + } + + @Override + public void setPosition( double[] position ) + { + currentAccess.setPosition( position ); + } + + @Override + public void setPosition( float position, int d ) + { + currentAccess.setPosition( position, d ); + } + + @Override + public void setPosition( double position, int d ) + { + currentAccess.setPosition( position, d ); + } + + @Override + public void fwd( int d ) + { + currentAccess.fwd( d ); + } + + @Override + public void bck( int d ) + { + currentAccess.bck( d ); + } + + @Override + public void move( int distance, int d ) + { + currentAccess.move( distance, d ); + } + + @Override + public void move( long distance, int d ) + { + currentAccess.move( distance, d ); + } + + @Override + public void move( Localizable distance ) + { + currentAccess.move( distance ); + } + + @Override + public void move( int[] distance ) + { + currentAccess.move( distance ); + } + + @Override + public void move( long[] distance ) + { + currentAccess.move( distance ); + } + + @Override + public void setPosition( Localizable position ) + { + currentAccess.setPosition( position ); + } + + @Override + public void setPosition( int[] position ) + { + currentAccess.setPosition( position ); + } + + @Override + public void setPosition( long[] position ) + { + currentAccess.setPosition( position ); + } + + @Override + public void setPosition( int position, int d ) + { + currentAccess.setPosition( position, d ); + } + + @Override + public void setPosition( long position, int d ) + { + currentAccess.setPosition( position, d ); + } + + @Override + public T get( long i ) + { + if( i == currentIndex ) + return currentAccess.get(); + else + { + sourceAccesses[(int)i].setPosition(currentAccess); + currentAccess = sourceAccesses[(int)i]; + currentIndex = (int)i; + return currentAccess.get(); + } + } + } + +} diff --git a/src/test/java/net/imglib2/view/composite/RealStackCompositeTest.java b/src/test/java/net/imglib2/view/composite/RealStackCompositeTest.java new file mode 100644 index 000000000..76325c96e --- /dev/null +++ b/src/test/java/net/imglib2/view/composite/RealStackCompositeTest.java @@ -0,0 +1,139 @@ +package net.imglib2.view.composite; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import net.imglib2.RealRandomAccess; +import net.imglib2.RealRandomAccessible; +import net.imglib2.position.FunctionRealRandomAccessible; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.view.StackView.StackAccessMode; + +public class RealStackCompositeTest +{ + private static final double EPS = 1e-9; + + private int N = 5; + private RealRandomAccessible[] sources; + private RealRandomAccessible[] sources2; + + @SuppressWarnings( "unchecked" ) + @Before + public void before() throws Exception + { + // 1d + sources = new RealRandomAccessible[ N ]; + for( int i = 0; i < N; i++ ) + { + final int j = i; + sources[i] = new FunctionRealRandomAccessible<>( 1, + ( x, v ) -> { v.set( j * x.getDoublePosition( 0 ) ); }, + DoubleType::new ); + } + + // 2d + sources2 = new RealRandomAccessible[ N ]; + for( int i = 0; i < N; i++ ) + { + final int j = i; + sources2[i] = new FunctionRealRandomAccessible<>( 2, + ( x, v ) -> { v.set( j * x.getDoublePosition( 0 ) + x.getDoublePosition( 1 )); }, + DoubleType::new ); + } + } + + @Test + public final void test1d() + { + test1dHelper("default", new RealStackCompositeView<>(sources)); + test1dHelper("moveAll", new RealStackCompositeView<>(sources, StackAccessMode.MOVE_ALL_SLICE_ACCESSES)); + } + + protected final void test1dHelper( String accessMode, RealStackCompositeView rimg ) + { + assertEquals( "ndims", 1, rimg.numDimensions() ); + + final RealRandomAccess> access = rimg.realRandomAccess(); + for( int i = 0; i < N; i++ ) + assertEquals( accessMode, 0, access.get().get( i ).get(), EPS ); + + // test setPosition + double p = 10; + access.setPosition( p, 0 ); + for( int i = 0; i < N; i++ ) + assertEquals( accessMode, i * p, access.get().get( i ).get(), EPS ); + + // test setPosition 2 + p = 4; + access.setPosition( p, 0 ); + for( int i = 0; i < N; i++ ) + assertEquals( accessMode, i * p, access.get().get( i ).get(), EPS ); + + // test fwd + p++; + access.fwd( 0 ); + for( int i = 0; i < N; i++ ) + assertEquals( accessMode, i * p, access.get().get( i ).get(), EPS ); + + // test move + p -= 2; + access.move( -2, 0 ); + for( int i = 0; i < N; i++ ) + assertEquals( accessMode, i * p, access.get().get( i ).get(), EPS ); + + } + + @Test + public final void test2d() + { + final RealStackCompositeView rimg = new RealStackCompositeView<>( sources2 ); + assertEquals( "ndims", 2, rimg.numDimensions() ); + + final RealRandomAccess> access = rimg.realRandomAccess(); + for( int i = 0; i < N; i++ ) + assertEquals( 0, access.get().get( i ).get(), EPS ); + + double x = 10; + double y = 2; + final double[] pa = new double[]{x,y}; + + // test setPosition + access.setPosition( x, 0 ); + access.setPosition( y, 1 ); + for( int i = 0; i < N; i++ ) + assertEquals( i * x + y, access.get().get( i ).get(), EPS ); + + // test setPosition + x = 11; pa[0] = x; + y = 3; pa[1] = y; + access.setPosition( pa ); + for( int i = 0; i < N; i++ ) + assertEquals( i * x + y, access.get().get( i ).get(), EPS ); + + // test fwd + x++; + access.fwd( 0 ); + for( int i = 0; i < N; i++ ) + assertEquals( i * x + y, access.get().get( i ).get(), EPS ); + + y++; + access.fwd( 1 ); + for( int i = 0; i < N; i++ ) + assertEquals( i * x + y, access.get().get( i ).get(), EPS ); + + // test move + x -= 2.5; + access.move( -2.5, 0 ); + for( int i = 0; i < N; i++ ) + assertEquals( i * x + y, access.get().get( i ).get(), EPS ); + + // test move + y -= 0.7; + access.move( -0.7, 1 ); + for( int i = 0; i < N; i++ ) + assertEquals( i * x + y, access.get().get( i ).get(), EPS ); + + } +}