diff --git a/src/main/java/net/imglib2/RandomAccessible.java b/src/main/java/net/imglib2/RandomAccessible.java index e42768a23..aa3097da7 100644 --- a/src/main/java/net/imglib2/RandomAccessible.java +++ b/src/main/java/net/imglib2/RandomAccessible.java @@ -34,6 +34,7 @@ package net.imglib2; +import net.imglib2.view.fluent.RandomAccessibleView; import net.imglib2.view.Views; /** @@ -135,10 +136,10 @@ public interface RandomAccessible< T > extends EuclideanSpace, Typed< T > * loops, or called many times!!! Use {@link #randomAccess()} when efficiency * is important. *

- * This method is a short cut for {@code randomAccess().setPositionAndGet( position );} + * This method is a short-cut for {@code randomAccess().setPositionAndGet( position );} * * @param position, length must be ≥ {@link #numDimensions()} - * @return value of the the {@link RandomAccessible} at {@code position}. + * @return value of the {@link RandomAccessible} at {@code position}. */ default T getAt( final long... position ) { @@ -153,10 +154,10 @@ default T getAt( final long... position ) * loops, or called many times!!! Use {@link #randomAccess()} when efficiency * is important. *

- * This method is a short cut for {@code randomAccess().setPositionAndGet( position );} + * This method is a short-cut for {@code randomAccess().setPositionAndGet( position );} * * @param position, length must be ≥ {@link #numDimensions()} - * @return value of the the {@link RandomAccessible} at {@code position}. + * @return value of the {@link RandomAccessible} at {@code position}. */ default T getAt( final int... position ) { @@ -171,16 +172,38 @@ default T getAt( final int... position ) * loops, or called many times!!! Use {@link #randomAccess()} when efficiency * is important. *

- * This method is a short cut for {@code randomAccess().setPositionAndGet( position );} + * This method is a short-cut for {@code randomAccess().setPositionAndGet( position );} * * @param position, {@link Localizable#numDimensions()} must be ≥ {@link #numDimensions()} - * @return value of the the {@link RandomAccessible} at {@code position}. + * @return value of the {@link RandomAccessible} at {@code position}. */ default T getAt( final Localizable position ) { return randomAccess().setPositionAndGet( position ); } + /** + * Provides a gateway for creating light-weight {@link net.imglib2.view.Views + * views} into this {@code RandomAccessible}. + *

+ * A view is itself a {@code RandomAccessible} or {@code + * RandomAccessibleInterval} whose accessors transform coordinates and/or + * values on-the-fly without copying the underlying data. Consecutive + * transformations are concatenated and simplified to provide optimally + * efficient accessors. + *

+ * Note, that accessors provided by a view are read/write. Changing pixels + * in a view changes the underlying image data. (Value converters are an + * exception.) + * + * @return gateway for creating light-weight {@link net.imglib2.view.Views + * views} into this {@code RandomAccessible}. + */ + default RandomAccessibleView< T, ? > view() + { + return RandomAccessibleView.wrap( this ); + } + /* * NB: We cannot have a default implementation here because of * https://bugs.openjdk.org/browse/JDK-7120669 diff --git a/src/main/java/net/imglib2/RandomAccessibleInterval.java b/src/main/java/net/imglib2/RandomAccessibleInterval.java index e3f367e9e..9d73dabff 100644 --- a/src/main/java/net/imglib2/RandomAccessibleInterval.java +++ b/src/main/java/net/imglib2/RandomAccessibleInterval.java @@ -36,6 +36,7 @@ import net.imglib2.util.Intervals; import net.imglib2.view.RandomAccessibleIntervalCursor; +import net.imglib2.view.fluent.RandomAccessibleIntervalView; /** *

@@ -98,4 +99,27 @@ default Object iterationOrder() { return new FlatIterationOrder( this ); } + + /** + * Provides a gateway for creating light-weight {@link net.imglib2.view.Views + * views} into this {@code RandomAccessibleInterval}. + *

+ * A view is itself a {@code RandomAccessibleInterval} or {@code + * RandomAccessible} whose accessors transform coordinates and/or values + * on-the-fly without copying the underlying data. Consecutive + * transformations are concatenated and simplified to provide optimally + * efficient accessors. + *

+ * Note, that accessors provided by a view are read/write. Changing pixels + * in a view changes the underlying image data. (Value converters are an + * exception.) + * + * @return gateway for creating light-weight {@link net.imglib2.view.Views + * views} into this {@code RandomAccessibleInterval}. + */ + @Override + default RandomAccessibleIntervalView< T > view() + { + return RandomAccessibleIntervalView.wrap( this ); + } } diff --git a/src/main/java/net/imglib2/RealRandomAccessible.java b/src/main/java/net/imglib2/RealRandomAccessible.java index 0825ea62f..86477fa80 100644 --- a/src/main/java/net/imglib2/RealRandomAccessible.java +++ b/src/main/java/net/imglib2/RealRandomAccessible.java @@ -34,6 +34,8 @@ package net.imglib2; +import net.imglib2.view.fluent.RealRandomAccessibleView; + /** *

* f:Rn→T @@ -120,6 +122,28 @@ default T getAt( final RealLocalizable position ) return realRandomAccess().setPositionAndGet( position ); } + /** + * Provides a gateway for creating light-weight {@link net.imglib2.view.Views + * views} into this {@code RealRandomAccessible}. + *

+ * A view is itself a {@code RealRandomAccessible} or {@code + * RandomAccessible} whose accessors transform coordinates and/or + * values on-the-fly without copying the underlying data. Consecutive + * transformations are concatenated and simplified to provide optimally + * efficient accessors. + *

+ * Note, that accessors provided by a view are read/write. Changing pixels + * in a view changes the underlying image data. (Value converters are an + * exception.) + * + * @return gateway for creating light-weight views into this {@code + * RealRandomAccessible}. + */ + default RealRandomAccessibleView< T > realView() + { + return RealRandomAccessibleView.wrap( this ); + } + /* * NB: We cannot have a default implementation here because of * https://bugs.openjdk.org/browse/JDK-7120669 diff --git a/src/main/java/net/imglib2/blocks/Extension.java b/src/main/java/net/imglib2/blocks/Extension.java index 4c008c23c..7ead383b2 100644 --- a/src/main/java/net/imglib2/blocks/Extension.java +++ b/src/main/java/net/imglib2/blocks/Extension.java @@ -33,12 +33,16 @@ */ package net.imglib2.blocks; +import static net.imglib2.outofbounds.OutOfBoundsMirrorFactory.Boundary.SINGLE; + +import net.imglib2.blocks.ViewNode.ExtensionViewNode; import net.imglib2.outofbounds.OutOfBoundsBorderFactory; import net.imglib2.outofbounds.OutOfBoundsConstantValueFactory; import net.imglib2.outofbounds.OutOfBoundsFactory; import net.imglib2.outofbounds.OutOfBoundsMirrorFactory; - -import static net.imglib2.outofbounds.OutOfBoundsMirrorFactory.Boundary.SINGLE; +import net.imglib2.outofbounds.OutOfBoundsZeroFactory; +import net.imglib2.type.operators.SetZero; +import net.imglib2.util.Cast; interface Extension { @@ -112,4 +116,17 @@ else if ( oobFactory instanceof OutOfBoundsConstantValueFactory ) return new ExtensionImpl.UnknownExtension<>( oobFactory ); } } + + static Extension of( ExtensionViewNode node ) + { + OutOfBoundsFactory< ?, ? > oobFactory = node.getOutOfBoundsFactory(); + if ( oobFactory instanceof OutOfBoundsZeroFactory ) + { + final net.imglib2.type.Type< ? > type = Cast.unchecked( node.view().getType() ); + final SetZero zero = Cast.unchecked( type.createVariable() ); + zero.setZero(); + oobFactory = new OutOfBoundsConstantValueFactory<>( zero ); + } + return of( oobFactory ); + } } diff --git a/src/main/java/net/imglib2/blocks/ViewAnalyzer.java b/src/main/java/net/imglib2/blocks/ViewAnalyzer.java index 802a47aba..9a992b89f 100644 --- a/src/main/java/net/imglib2/blocks/ViewAnalyzer.java +++ b/src/main/java/net/imglib2/blocks/ViewAnalyzer.java @@ -50,6 +50,7 @@ import net.imglib2.img.array.ArrayImg; import net.imglib2.img.cell.AbstractCellImg; import net.imglib2.img.planar.PlanarImg; +import net.imglib2.view.fluent.RandomAccessibleView; import net.imglib2.transform.integer.BoundingBox; import net.imglib2.transform.integer.MixedTransform; import net.imglib2.type.NativeType; @@ -136,6 +137,12 @@ else if ( source instanceof ImgView ) nodes.add( new DefaultViewNode( ViewNode.ViewType.IDENTITY, view ) ); source = view.getSource(); } + else if ( source instanceof RandomAccessibleView ) + { + final RandomAccessibleView< ?, ? > view = ( RandomAccessibleView< ?, ? > ) source; + nodes.add( new DefaultViewNode( ViewNode.ViewType.IDENTITY, view ) ); + source = view.delegate(); + } // INTERVAL, else if ( source instanceof IntervalView ) { diff --git a/src/main/java/net/imglib2/interpolation/Interpolant.java b/src/main/java/net/imglib2/interpolation/Interpolant.java index e2b6eb3ee..450ebb25a 100644 --- a/src/main/java/net/imglib2/interpolation/Interpolant.java +++ b/src/main/java/net/imglib2/interpolation/Interpolant.java @@ -53,7 +53,7 @@ final public class Interpolant< T, F > implements RealRandomAccessible< T >, Vie protected final int n; - final InterpolatorFactory< T, F > factory; + final InterpolatorFactory< T, ? super F > factory; /** * @@ -79,7 +79,7 @@ public Interpolant( final EuclideanSpace source, final InterpolatorFactory< T, F * @param factory * @param n */ - public Interpolant( final F source, final InterpolatorFactory< T, F > factory, final int n ) + public Interpolant( final F source, final InterpolatorFactory< T, ? super F > factory, final int n ) { this.source = source; this.factory = factory; @@ -118,7 +118,7 @@ public T getType() /** * @return {@link InterpolatorFactory} used for interpolation */ - public InterpolatorFactory< T, F > getInterpolatorFactory() + public InterpolatorFactory< T, ? super F > getInterpolatorFactory() { return factory; } diff --git a/src/main/java/net/imglib2/outofbounds/OutOfBoundsZeroFactory.java b/src/main/java/net/imglib2/outofbounds/OutOfBoundsZeroFactory.java new file mode 100644 index 000000000..9a187e963 --- /dev/null +++ b/src/main/java/net/imglib2/outofbounds/OutOfBoundsZeroFactory.java @@ -0,0 +1,59 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2024 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.outofbounds; + +import net.imglib2.Interval; +import net.imglib2.RandomAccessible; +import net.imglib2.type.Type; +import net.imglib2.type.operators.SetZero; + +/** + * Return the zero value of {@code T} when out of bounds. + * + * @param + * pixel type + * + * @author Tobias Pietzsch + */ +public class OutOfBoundsZeroFactory< T extends Type< T > & SetZero, F extends Interval & RandomAccessible< T > > + implements OutOfBoundsFactory< T, F > +{ + @Override + public OutOfBoundsConstantValue< T > create( final F f ) + { + final T zero = f.getType().createVariable(); + zero.setZero(); + return new OutOfBoundsConstantValue<>( f, zero ); + } +} diff --git a/src/main/java/net/imglib2/view/IntervalView.java b/src/main/java/net/imglib2/view/IntervalView.java index 804839a57..e0b8494aa 100644 --- a/src/main/java/net/imglib2/view/IntervalView.java +++ b/src/main/java/net/imglib2/view/IntervalView.java @@ -66,7 +66,7 @@ public class IntervalView< T > extends AbstractInterval implements RandomAccessi * TODO Javadoc */ protected RandomAccessible< T > fullViewRandomAccessible; - + /** * TODO Javadoc */ @@ -76,7 +76,7 @@ public class IntervalView< T > extends AbstractInterval implements RandomAccessi * Create a view that defines an interval on a source. It is the callers * responsibility to ensure that the source is defined in the specified * interval. - * + * * @see Views#interval(RandomAccessible, Interval) */ public IntervalView( final RandomAccessible< T > source, final Interval interval ) @@ -92,9 +92,9 @@ public IntervalView( final RandomAccessible< T > source, final Interval interval * Create a view that defines an interval on a source. It is the callers * responsibility to ensure that the source is defined in the specified * interval. - * + * * @see Views#interval(RandomAccessible, Interval) - * + * * @param min * minimum coordinate of the interval. * @param max @@ -107,7 +107,7 @@ public IntervalView( final RandomAccessible< T > source, final long[] min, final /** * Gets the underlying source {@link RandomAccessible}. - * + * * @return the source {@link RandomAccessible}. */ public RandomAccessible< T > getSource() @@ -128,7 +128,7 @@ public RandomAccess< T > randomAccess() fullViewRandomAccessible = TransformBuilder.getEfficientRandomAccessible( this, this ); return fullViewRandomAccessible.randomAccess(); } - + protected IterableInterval< T > getFullViewIterableInterval() { if ( fullViewIterableInterval == null ) diff --git a/src/main/java/net/imglib2/view/Views.java b/src/main/java/net/imglib2/view/Views.java index dad0c8fb0..f026953b9 100644 --- a/src/main/java/net/imglib2/view/Views.java +++ b/src/main/java/net/imglib2/view/Views.java @@ -36,7 +36,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import net.imglib2.EuclideanSpace; @@ -85,7 +84,7 @@ /** * Create light-weight views into {@link RandomAccessible RandomAccessibles}. - * + *

* A view is itself a {@link RandomAccessible} or * {@link RandomAccessibleInterval} that provides {@link RandomAccess accessors} * that transform coordinates on-the-fly without copying the underlying data. @@ -109,7 +108,7 @@ public class Views * source * @return */ - public static < T, F extends EuclideanSpace > RealRandomAccessible< T > interpolate( final F source, final InterpolatorFactory< T, F > factory ) + public static < T, F extends EuclideanSpace > RealRandomAccessible< T > interpolate( final F source, final InterpolatorFactory< T, ? super F > factory ) { return new Interpolant<>( source, factory, source.numDimensions() ); } diff --git a/src/main/java/net/imglib2/view/fluent/RandomAccessibleIntervalView.java b/src/main/java/net/imglib2/view/fluent/RandomAccessibleIntervalView.java new file mode 100644 index 000000000..436a0cf15 --- /dev/null +++ b/src/main/java/net/imglib2/view/fluent/RandomAccessibleIntervalView.java @@ -0,0 +1,599 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2024 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.view.fluent; + +import java.util.function.Function; +import java.util.function.Supplier; + +import net.imglib2.Cursor; +import net.imglib2.FlatIterationOrder; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.Converter; +import net.imglib2.converter.Converters; +import net.imglib2.outofbounds.OutOfBoundsBorderFactory; +import net.imglib2.outofbounds.OutOfBoundsConstantValueFactory; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory; +import net.imglib2.outofbounds.OutOfBoundsPeriodicFactory; +import net.imglib2.outofbounds.OutOfBoundsZeroFactory; +import net.imglib2.stream.LocalizableSpliterator; +import net.imglib2.type.Type; +import net.imglib2.type.operators.SetZero; +import net.imglib2.util.Util; +import net.imglib2.view.IterableRandomAccessibleInterval; +import net.imglib2.view.Views; + +/** + * Gateway for creating light-weight views on a {@code RandomAccessibleInterval}. + *

+ * A view is itself a {@code RandomAccessible} or {@code + * RandomAccessibleInterval} whose accessors transform coordinates and/or + * values on-the-fly without copying the underlying data. Consecutive + * transformations are concatenated and simplified to provide optimally + * efficient accessors. + *

+ * The {@code RandomAccessibleIntervalView} gateway implements {@code + * RandomAccessibleInterval}, forwarding all methods to its {@link #delegate}. + * Additionally, it provides methods analogous to the {@code static} {@link + * Views} methods that operate on its {@link #delegate} and return {@code + * RandomAccessibleIntervalView}, {@code RandomAccessibleView}, or {@code + * RealRandomAccessibleView} wrappers. + *

+ * This provides a fluent API for conveniently chaining {@code Views} methods. + * For example + *

+ * {@code RandomAccessibleInterval< IntType > view =
+ *                img.view()
+ *                   .permute( 0, 1 )
+ *                   .expand( Extension.border(), 5 )
+ *                   .zeroMin();
+ * }
+ * 
+ * + * @author Tobias Pietzsch + * @author Michael Innerberger + * @see Views + */ +public interface RandomAccessibleIntervalView< T > extends RandomAccessibleView< T, RandomAccessibleIntervalView< T > >, RandomAccessibleInterval< T > +{ + @Override + RandomAccessibleInterval< T > delegate(); + + static < T > RandomAccessibleIntervalView< T > wrap( final RandomAccessibleInterval< T > delegate ) + { + return () -> delegate; + } + + // -- Views methods ------------------------------------------------------- + + /** + * Enforce {@link FlatIterationOrder} order for this {@code + * RandomAccessibleInterval}. + *

+ * If this passed {@code RandomAccessibleInterval} already has flat + * iteration order then it is returned directly. If not, then it is wrapped + * in a {@link IterableRandomAccessibleInterval}. + * + * @return a view with flat iteration order + */ + default RandomAccessibleIntervalView< T > flatIterable() + { + return wrap( Views.flatIterable( delegate() ) ); + } + + /** + * Take a (n-1)-dimensional slice of this n-dimensional + * {@code RandomAccessible}, by fixing the {@code d} dimension of + * coordinates to {@code pos}. + * + * @param d + * coordinate dimension to fix + * @param pos + * coordinate value to fix {@code d}th dimension to + * + * @return a view on the given slice + */ + @Override + default RandomAccessibleIntervalView< T > slice( int d, long pos ) + { + return wrap( Views.hyperSlice( delegate(), d, pos ) ); + } + + /** + * Create a (n+1)-dimensional view of this n-dimensional + * {@code RandomAccessibleInterval}, by replicating values along the added + * axis. + *

+ * The additional dimension is the last dimension. For example, an XYZ view + * is created for an XY source. When accessing an XYZ sample in the view, + * the final coordinate is discarded and the source XY sample is accessed. + * + * @param minOfNewDim + * interval min in the added dimension. + * @param maxOfNewDim + * interval max in the added dimension. + * + * @return a view with an additional dimension + */ + default RandomAccessibleIntervalView< T > addDimension( long minOfNewDim, long maxOfNewDim ) + { + return wrap( Views.addDimension( delegate(), minOfNewDim, maxOfNewDim ) ); + } + + /** + * Create a view that is translated by the given {@code translation} vector. + *

+ * The pixel at coordinates x in this {@code RandomAccessible} has + * coordinates (x + translation) in the resulting view. + * + * @param translation + * translation vector + * + * @return a translated view + */ + @Override + default RandomAccessibleIntervalView< T > translate( long... translation ) + { + return wrap( Views.translate( delegate(), translation ) ); + } + + /** + * Create a view that is translated by the inverse of the given {@code + * translation} vector. + *

+ * The pixel at coordinates x in this {@code RandomAccessible} has + * coordinates (x - translation) in the resulting view. + * + * @param translation + * translation vector + * + * @return an inverse-translated view + */ + @Override + default RandomAccessibleIntervalView< T > translateInverse( long... translation ) + { + return wrap( Views.translateInverse( delegate(), translation ) ); + } + + /** + * Create a translated view such that the min (upper left) corner is at the + * origin. + * + * @return a view that is translated to the origin + */ + default RandomAccessibleIntervalView< T > zeroMin() + { + return wrap( Views.zeroMin( delegate() ) ); + } + + /** + * Sample only every stepdth value of a + * source {@link RandomAccessible}. This is effectively an integer scaling + * transformation. + *

+ * The provided {@code steps} vector is expanded or truncated to the + * dimensionality of this {@code RandomAccessible}. When expanding ({@code + * steps.length < this.numDimensions()}), the last element is repeated. + * + * @param steps + * the subsampling step sizes + * + * @return a subsampled view + */ + @Override + default RandomAccessibleIntervalView< T > subsample( final long... steps ) + { + return wrap( Views.subsample( delegate(), Util.expandArray( steps, numDimensions() ) ) ); + } + + /** + * Create a view rotated 90 degrees, mapping {@code fromAxis} to {@code + * toAxis}. + *

+ * For example, {@code fromAxis=0, toAxis=1} means that the {@code X} axis + * of this {@code RandomAccessibleInterval} is mapped to the {@code Y} axis + * of the rotated view. Correspondingly, the {@code Y} axis is mapped to + * {@code -X}. All other axes remain unchanged. This corresponds to a 90 + * degree clock-wise rotation of this {@code RandomAccessibleInterval} in + * the {@code XY} plane. + *

+ * Note that if this {@code RandomAccessibleInterval} has its min coordinate + * at the origin, the min coordinate of the rotated view will not be at the + * origin. To align the min coordinate of the rotated view with the origin, + * use {@link #zeroMin()}. + * + * @param fromAxis + * axis index + * @param toAxis + * axis index that {@code fromAxis} should be rotated to + * + * @return a view rotated 90 degrees + */ + @Override + default RandomAccessibleIntervalView< T > rotate( int fromAxis, int toAxis ) + { + return wrap( Views.rotate( delegate(), fromAxis, toAxis ) ); + } + + /** + * Create a view with permuted axes where the specified {@code fromAxis} to + * {@code toAxis} are swapped (while all other axes remain unchanged). + * + * @param fromAxis + * axis index + * @param toAxis + * axis index that {@code fromAxis} should be swapped with + * + * @return a view with permuted axes + */ + @Override + default RandomAccessibleIntervalView< T > permute( int fromAxis, int toAxis ) + { + return wrap( Views.permute( delegate(), fromAxis, toAxis ) ); + } + + /** + * Create view with permuted axes where the specified {@code fromAxis} is + * moved to index {@code toAxis} while the order of other axes is preserved. + *

+ * For example, if {@code fromAxis=2, toAxis=4} and the axis order of this + * {@code RandomAccessibleInterval} is {@code XYCZT}, the resulting view + * will have the axis order {@code XYZTC}. + * + * @param fromAxis + * axis index + * @param toAxis + * axis index that {@code fromAxis} should be moved to + * + * @return a view with permuted axes + */ + @Override + default RandomAccessibleIntervalView< T > moveAxis( int fromAxis, int toAxis ) + { + return wrap( Views.moveAxis( delegate(), fromAxis, toAxis ) ); + } + + /** + * Invert the {@code axis} with the given index. + *

+ * For example, if {@code axis=1}, then coordinate {@code (x,y)} in the + * resulting view corresponds to coordinate {@code (x,-y)} in this {@code + * RandomAccessibleInterval}. + *

+ * Note that the interval boundaries of the view are modified accordingly. + * If this {@code RandomAccessibleInterval} is a {@code 10x10} image with + * interval {@code (0,0)..(9,9)}, the interval of the view is {@code + * (0,-9)..(9,0)} + * + * @param axis + * the axis to invert + * + * @return a view with {@code axis} inverted + */ + @Override + default RandomAccessibleIntervalView< T > invertAxis( int axis ) + { + return wrap( Views.invertAxis( delegate(), axis ) ); + } + + /** + * Extension method to use with {@link #extend} and {@link #expand}. {@code + * Extension} instances can be created using static methods {@link #border}, + * {@link #value}, {@link #mirrorSingle}, etc. + *

+ * Usage example: + *

+	 * {@code
+	 * RandomAccessible extended =
+	 *                img.view()
+	 *                   .extend(Extension.border());
+	 * }
+	 * 
+ * + * @param + * pixel type ot the {@code RandomAccessible} to be extended + */ + class Extension< T > + { + final OutOfBoundsFactory< T, RandomAccessibleIntervalView< T > > factory; + + private Extension( OutOfBoundsFactory< T, RandomAccessibleIntervalView< T > > factory ) + { + this.factory = factory; + } + + /** + * Create {@code Extension} using {@link OutOfBoundsBorderFactory}. + *

+ * Out-of-bounds pixels are created by repeating border pixels. + */ + public static < T > Extension< T > border() + { + return new Extension<>(new OutOfBoundsBorderFactory<>() ); + } + + /** + * Create {@code Extension} using {@link OutOfBoundsZeroFactory}. + *

+ * All out-of-bounds pixels have value zero. + */ + public static < T extends Type< T > & SetZero > Extension< T > zero() + { + return new Extension<>( new OutOfBoundsZeroFactory< T, RandomAccessibleIntervalView< T > >() ); + } + + /** + * Create {@code Extension} using {@link OutOfBoundsConstantValueFactory}. + *

+ * All out-of-bounds pixels have the provided {@code value}. + */ + public static < T > Extension< T > value( T value ) + { + return new Extension<>(new OutOfBoundsConstantValueFactory<>( value ) ); + } + + /** + * Create {@code Extension} using {@link OutOfBoundsMirrorFactory}. + *

+ * Out-of-bounds pixels are created by mirroring, where boundary pixels + * are not repeated. Note that this requires that all dimensions of the + * source must be > 1. + */ + public static < T > Extension< T > mirrorSingle() + { + return new Extension<>(new OutOfBoundsMirrorFactory<>( OutOfBoundsMirrorFactory.Boundary.SINGLE ) ); + } + + /** + * Create {@code Extension} using {@link OutOfBoundsMirrorFactory}. + *

+ * Out-of-bounds pixels are created by mirroring, where boundary pixels + * are repeated. + */ + public static < T > Extension< T > mirrorDouble() + { + return new Extension<>(new OutOfBoundsMirrorFactory<>( OutOfBoundsMirrorFactory.Boundary.DOUBLE ) ); + } + + /** + * Create {@code Extension} using {@link OutOfBoundsPeriodicFactory}. + *

+ * Out-of-bounds pixels are created by periodically repeating the source + * image. + */ + public static < T > Extension< T > periodic() + { + return new Extension<>(new OutOfBoundsPeriodicFactory<>() ); + } + } + + /** + * Create an unbounded {@link RandomAccessible} view of this {@code + * RandomAccessibleInterval} using out-of-bounds extension with the given + * {@code extension} method. + *

+ * {@link Extension} can be created by one of its static factory methods. + * For example + *

+	 * {@code
+	 * RandomAccessible extended =
+	 *                img.view()
+	 *                   .extend(Extension.border());
+	 * }
+	 * 
+ * + * @param extension + * the out-of-bounds strategy to use + * + * @return an extended (unbounded) view + */ + default RandomAccessibleView< T, ? > extend( Extension< T > extension ) + { + return RandomAccessibleView.wrap( Views.extend( this, extension.factory ) ); + } + + /** + * Create an expanded view of this {@code RandomAccessibleInterval} that + * using out-of-bounds extension with the given {@code extension} method. + *

+ * The provided {@code border} vector is expanded or truncated to the + * dimensionality of this {@code RandomAccessibleInterval}. When expanding + * ({@code border.length < this.numDimensions()}), the last element is + * repeated. + *

+ * For example + *

+	 * {@code
+	 * // expand by 10 pixels in every dimension
+	 * RandomAccessibleInterval expanded =
+	 *                img.view()
+	 *                   .expand(Extension.border(), 5);
+	 *
+     * // expand by 5 pixels in X and Y, don't expand higher dimensions
+	 * RandomAccessibleInterval expanded =
+	 *                img.view()
+	 *                   .expand(Extension.border(), 5, 5, 0);
+	 * }
+	 * 
+ * + * @param extension + * the out-of-bounds strategy to use + * @param border + * the border to add to the image + * + * @return an expanded view + */ + default RandomAccessibleIntervalView< T > expand( Extension< T > extension, long... border ) + { + return RandomAccessibleIntervalView.wrap( Views.expand( this, extension.factory, Util.expandArray( border, numDimensions() ) ) ); + } + + /** + * Create a view of this {@code RandomAccessibleInterval} converted to pixel + * type {@code U}. + *

+ * Pixel values {@code T} are converted to {@code U} using the given {@code + * converter}. A {@code Converter} is equivalent to a {@code BiConsumer} that reads a value from its first argument and writes a converted + * value to its second argument. + * + * @param + * target pixel type + * @param targetSupplier + * creates instances of {@code U} for storing converted values + * @param converter + * converts pixel values from {@code T} to {@code U} + * + * @return a converted view + */ + @Override + default < U > RandomAccessibleIntervalView< U > convert( + final Supplier< U > targetSupplier, final Converter< ? super T, ? super U > converter ) + { + return wrap( Converters.convert2( delegate(), converter, targetSupplier ) ); + } + + /** + * Create a view of this {@code RandomAccessibleInterval} converted to pixel + * type {@code U}. + *

+ * Pixel values {@code T} are converted to {@code U} using {@code + * Converter}s created by the given {@code converterSupplier}. A {@code + * Converter} is equivalent to a {@code BiConsumer} that reads a value + * from its first argument and writes a converted value to its second + * argument. + * + * @param + * target pixel type + * @param targetSupplier + * creates instances of {@code U} for storing converted values + * @param converterSupplier + * converts pixel values from {@code T} to {@code U} + * + * @return a converted view + */ + @Override + default < U > RandomAccessibleIntervalView< U > convert( + final Supplier< U > targetSupplier, final Supplier< Converter< ? super T, ? super U > > converterSupplier ) + { + return wrap( Converters.convert2( delegate(), converterSupplier, targetSupplier ) ); + } + + /** + * Apply the specified {@code function} to this {@code + * RandomAccessibleInterval} and return the result. + * + * @param function + * function to evaluate on this {@code RandomAccessibleInterval} + * @param + * the type of the result of the function + * + * @return {@code function.apply(this)} + */ + @Override + default < U > U use( Function< ? super RandomAccessibleIntervalView< T >, U > function ) + { + return function.apply( this ); + } + + + // -- RandomAccessibleInterval -------------------------------------------- + + @Override + default RandomAccessibleIntervalView< T > view() + { + return this; + } + + @Override + default T getType() + { + return delegate().getType(); + } + + @Override + default long min( final int d ) + { + return delegate().min( d ); + } + + @Override + default long max( final int d ) + { + return delegate().max( d ); + } + + @Override + default long dimension( final int d ) + { + return delegate().dimension( d ); + } + + @Override + default Cursor< T > cursor() + { + return delegate().cursor(); + } + + @Override + default Cursor< T > localizingCursor() + { + return delegate().localizingCursor(); + } + + @Override + default LocalizableSpliterator< T > spliterator() + { + return delegate().spliterator(); + } + + @Override + default LocalizableSpliterator< T > localizingSpliterator() + { + return delegate().localizingSpliterator(); + } + + @Override + default long size() + { + return delegate().size(); + } + + @Override + default Object iterationOrder() + { + return delegate().iterationOrder(); + } +} diff --git a/src/main/java/net/imglib2/view/fluent/RandomAccessibleView.java b/src/main/java/net/imglib2/view/fluent/RandomAccessibleView.java new file mode 100644 index 000000000..84353f812 --- /dev/null +++ b/src/main/java/net/imglib2/view/fluent/RandomAccessibleView.java @@ -0,0 +1,461 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2024 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.view.fluent; + +import java.util.function.Function; +import java.util.function.Supplier; + +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RealRandomAccessible; +import net.imglib2.converter.Converter; +import net.imglib2.converter.Converters; +import net.imglib2.interpolation.InterpolatorFactory; +import net.imglib2.interpolation.randomaccess.ClampingNLinearInterpolatorFactory; +import net.imglib2.interpolation.randomaccess.LanczosInterpolatorFactory; +import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory; +import net.imglib2.interpolation.randomaccess.NearestNeighborInterpolatorFactory; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Intervals; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + +/** + * Gateway for creating light-weight views on a {@code RandomAccessible}. + *

+ * A view is itself a {@code RandomAccessible} or {@code + * RandomAccessibleInterval} whose accessors transform coordinates and/or values + * on-the-fly without copying the underlying data. Consecutive transformations + * are concatenated and simplified to provide optimally efficient accessors. + *

+ * The {@code RandomAccessibleView} gateway implements {@code RandomAccessible}, + * forwarding all methods to its {@link #delegate}. Additionally, it provides + * methods analogous to the {@code static} {@link Views} methods that operate on + * its {@link #delegate} and return {@code RandomAccessibleIntervalView}, {@code + * RandomAccessibleView}, or {@code RealRandomAccessibleView} wrappers. + *

+ * This provides a fluent API for conveniently chaining {@code Views} methods. + * For example + *

+ * {@code RandomAccessible< IntType > view =
+ *                img.view()
+ *                   .extend( Extension.mirrorSingle() )
+ *                   .permute( 0, 1 )
+ *                   .translate( 10, 10 );
+ * }
+ * 
+ * + * @author Tobias Pietzsch + * @author Michael Innerberger + * @see Views + */ +public interface RandomAccessibleView< T, V extends RandomAccessibleView< T, V > > extends RandomAccessible< T > +{ + RandomAccessible< T > delegate(); + + static < T, V extends RandomAccessibleView< T, V > > RandomAccessibleView< T, V > wrap( final RandomAccessible< T > delegate ) + { + return ( RandomAccessibleView< T, V > ) () -> delegate; + } + + // -- Views methods ------------------------------------------------------- + + /** + * Define an interval on this {@code RandomAccessible}. + *

+ * It is the callers responsibility to ensure that the source {@code + * RandomAccessible} is defined in the specified {@code interval}. + *

+ * For constructing an {@code Interval} from min and max coordinates, min + * coordinates and dimensions, by modifying other intervals, etc, see {@link + * FinalInterval} and {@link Intervals}. + * + * @param interval + * interval boundaries. + * + * @return a view on the given interval + */ + default RandomAccessibleIntervalView< T > interval( Interval interval ) + { + return RandomAccessibleIntervalView.wrap( Views.interval( delegate(), interval ) ); + } + + /** + * Take a (n-1)-dimensional slice of this n-dimensional + * {@code RandomAccessible}, by fixing the {@code d} dimension of + * coordinates to {@code pos}. + * + * @param d + * coordinate dimension to fix + * @param pos + * coordinate value to fix {@code d}th dimension to + * + * @return a view on the given slice + */ + default RandomAccessibleView< T, ? > slice( int d, long pos ) + { + return wrap( Views.hyperSlice( delegate(), d, pos ) ); + } + + /** + * Create a (n+1)-dimensional view of this n-dimensional + * {@code RandomAccessible}, by replicating values along the added axis. + *

+ * The additional dimension is the last dimension. For example, an XYZ view + * is created for an XY source. When accessing an XYZ sample in the view, + * the final coordinate is discarded and the source XY sample is accessed. + * + * @return a view with an additional dimension + */ + default RandomAccessibleView< T, ? > addDimension() + { + return wrap( Views.addDimension( delegate() ) ); + } + + /** + * Create a view that is translated by the given {@code translation} vector. + *

+ * The pixel at coordinates x in this {@code RandomAccessible} has + * coordinates (x + translation) in the resulting view. + * + * @param translation + * translation vector + * + * @return a translated view + */ + default RandomAccessibleView< T, ? > translate( long... translation ) + { + return wrap( Views.translate( delegate(), translation ) ); + } + + /** + * Create a view that is translated by the inverse of the given {@code + * translation} vector. + *

+ * The pixel at coordinates x in this {@code RandomAccessible} has + * coordinates (x - translation) in the resulting view. + * + * @param translation + * translation vector + * + * @return an inverse-translated view + */ + default RandomAccessibleView< T, ? > translateInverse( long... translation ) + { + return wrap( Views.translateInverse( delegate(), translation ) ); + } + + /** + * Sample only every stepdth value of a + * source {@link RandomAccessible}. This is effectively an integer scaling + * transformation. + *

+ * The provided {@code steps} vector is expanded or truncated to the + * dimensionality of this {@code RandomAccessible}. When expanding ({@code + * steps.length < this.numDimensions()}), the last element is repeated. + * + * @param steps + * the subsampling step sizes + * + * @return a subsampled view + */ + default RandomAccessibleView< T, ? > subsample( final long... steps ) + { + return wrap( Views.subsample( delegate(), Util.expandArray( steps, numDimensions() ) ) ); + } + + /** + * Create a view rotated 90 degrees, mapping {@code fromAxis} to {@code + * toAxis}. + *

+ * For example, {@code fromAxis=0, toAxis=1} means that the {@code X} axis + * of this {@code RandomAccessible} is mapped to the {@code Y} axis of the + * rotated view. Correspondingly, the {@code Y} axis is mapped to {@code + * -X}. All other axes remain unchanged. This corresponds to a 90 degree + * clock-wise rotation of this {@code RandomAccessible} in the {@code XY} + * plane. + * + * @param fromAxis + * axis index + * @param toAxis + * axis index that {@code fromAxis} should be rotated to + * + * @return a view rotated 90 degrees + */ + default RandomAccessibleView< T, ? > rotate( int fromAxis, int toAxis ) + { + return wrap( Views.rotate( delegate(), fromAxis, toAxis ) ); + } + + /** + * Create a view with permuted axes where the specified {@code fromAxis} to + * {@code toAxis} are swapped (while all other axes remain unchanged). + * + * @param fromAxis + * axis index + * @param toAxis + * axis index that {@code fromAxis} should be swapped with + * + * @return a view with permuted axes + */ + default RandomAccessibleView< T, ? > permute( int fromAxis, int toAxis ) + { + return wrap( Views.permute( delegate(), fromAxis, toAxis ) ); + } + + /** + * Create a view with permuted axes where the specified {@code fromAxis} is + * moved to index {@code toAxis} while the order of other axes is preserved. + *

+ * For example, if {@code fromAxis=2, toAxis=4} and the axis order of this + * {@code RandomAccessible} is {@code XYCZT}, the resulting view will have + * the axis order {@code XYZTC}. + * + * @param fromAxis + * axis index + * @param toAxis + * axis index that {@code fromAxis} should be moved to + * + * @return a view with permuted axes + */ + default RandomAccessibleView< T, ? > moveAxis( int fromAxis, int toAxis ) + { + return wrap( Views.moveAxis( delegate(), fromAxis, toAxis ) ); + } + + /** + * Invert the {@code axis} with the given index. + *

+ * For example, if {@code axis=1}, then coordinate {@code (x,y,z)} in the + * resulting view corresponds to coordinate {@code (x,-y,z)} in this {@code + * RandomAccessible}. + * + * @param axis + * the axis to invert + * + * @return a view with {@code axis} inverted + */ + default RandomAccessibleView< T, ? > invertAxis( int axis ) + { + return wrap( Views.invertAxis( delegate(), axis ) ); + } + + /** + * Interpolation method to use with {@link #interpolate}. {@code + * Interpolation} instances can be created using {@link #nearestNeighbor}, + * {@link #nLinear}, {@link #clampingNLinear}, or {@link #lanczos}. + *

+ * Usage example: + *

+	 * {@code
+	 * RealRandomAccessible interpolated =
+	 *                img.view()
+	 *                   .extend( Extension.zero() )
+	 *                   .interpolate( Interpolation.lanczos() );
+	 * }
+	 * 
+ * + * @param + * pixel type ot the {@code RandomAccessible} to be interpolated + */ + class Interpolation< T > + { + final InterpolatorFactory< T, ? super RandomAccessible< T > > factory; + + private Interpolation( InterpolatorFactory< T, ? super RandomAccessible< T > > factory ) + { + this.factory = factory; + } + + /** + * Create {@code Interpolation} using {@link NearestNeighborInterpolatorFactory}. + */ + public static < T > Interpolation< T > nearestNeighbor() + { + return new Interpolation<>( new NearestNeighborInterpolatorFactory<>() ); + } + + /** + * Create {@code Interpolation} using {@link NLinearInterpolatorFactory}. + */ + public static < T extends NumericType< T > > Interpolation< T > nLinear() + { + return new Interpolation< T >( new NLinearInterpolatorFactory<>() ); + } + + /** + * Create {@code Interpolation} using {@link ClampingNLinearInterpolatorFactory}. + */ + public static < T extends NumericType< T > > Interpolation< T > clampingNLinear() + { + return new Interpolation< T >( new ClampingNLinearInterpolatorFactory<>() ); + } + + /** + * Create {@code Interpolation} using {@link LanczosInterpolatorFactory}. + */ + public static < T extends RealType< T > > Interpolation< T > lanczos() + { + return new Interpolation< T >( new LanczosInterpolatorFactory<>() ); + } + } + + /** + * Create a {@link RealRandomAccessible} view of this {@code RandomAccessible} + * using interpolation with the given {@code interpolation} method. + *

+ * {@link Interpolation} can be created by one of its static factory + * methods. For example + *

+	 * {@code
+	 * RealRandomAccessible< IntType > interpolated =
+	 *                img.view()
+	 *                   .extend( Extension.zero() )
+	 *                   .interpolate( Interpolation.lanczos() );
+	 * }
+	 * 
+ * + * @param interpolation + * the {@link Interpolation} method + * + * @return an interpolated view + */ + default RealRandomAccessibleView< T > interpolate( final Interpolation< T > interpolation ) + { + return RealRandomAccessibleView.wrap( Views.interpolate( delegate(), interpolation.factory ) ); + } + + /** + * Create a view of this {@code RandomAccessible} converted to pixel type + * {@code U}. + *

+ * Pixel values {@code T} are converted to {@code U} using the given {@code + * converter}. A {@code Converter} is equivalent to a {@code BiConsumer} that reads a value from its first argument and writes a converted + * value to its second argument. + * + * @param + * target pixel type + * @param targetSupplier + * creates instances of {@code U} for storing converted values + * @param converter + * converts pixel values from {@code T} to {@code U} + * + * @return a converted view + */ + default < U > RandomAccessibleView< U, ? > convert( + final Supplier< U > targetSupplier, + final Converter< ? super T, ? super U > converter ) + { + return wrap( Converters.convert2( delegate(), converter, targetSupplier ) ); + } + + /** + * Create a view of this {@code RandomAccessible} converted to pixel type + * {@code U}. + *

+ * Pixel values {@code T} are converted to {@code U} using {@code + * Converter}s created by the given {@code converterSupplier}. A {@code + * Converter} is equivalent to a {@code BiConsumer} that reads a value + * from its first argument and writes a converted value to its second + * argument. + * + * @param + * target pixel type + * @param targetSupplier + * creates instances of {@code U} for storing converted values + * @param converterSupplier + * converts pixel values from {@code T} to {@code U} + * + * @return a converted view + */ + default < U > RandomAccessibleView< U, ? > convert( + final Supplier< U > targetSupplier, + final Supplier< Converter< ? super T, ? super U > > converterSupplier ) + { + return wrap( Converters.convert2( delegate(), converterSupplier, targetSupplier ) ); + } + + /** + * Apply the specified {@code function} to this {@code RandomAccessible} and + * return the result. + * + * @param function + * function to evaluate on this {@code RandomAccessible} + * @param + * the type of the result of the function + * + * @return {@code function.apply(this)} + */ + default < U > U use( Function< ? super V, U > function ) + { + return function.apply( ( V ) this ); + } + + + // -- RandomAccessible ---------------------------------------------------- + + @Override + default RandomAccessibleView< T, ? > view() + { + return this; + } + + @Override + default T getType() + { + return delegate().getType(); + } + + @Override + default int numDimensions() + { + return delegate().numDimensions(); + } + + @Override + default RandomAccess< T > randomAccess() + { + return delegate().randomAccess(); + } + + @Override + default RandomAccess< T > randomAccess( final Interval interval ) + { + return delegate().randomAccess(interval); + } +} diff --git a/src/main/java/net/imglib2/view/fluent/RealRandomAccessibleView.java b/src/main/java/net/imglib2/view/fluent/RealRandomAccessibleView.java new file mode 100644 index 000000000..8d731c46a --- /dev/null +++ b/src/main/java/net/imglib2/view/fluent/RealRandomAccessibleView.java @@ -0,0 +1,197 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2024 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.view.fluent; + +import java.util.function.Function; +import java.util.function.Supplier; + +import net.imglib2.RealInterval; +import net.imglib2.RealRandomAccess; +import net.imglib2.RealRandomAccessible; +import net.imglib2.converter.Converter; +import net.imglib2.converter.Converters; +import net.imglib2.view.Views; + +/** + * Gateway for creating light-weight views on a {@code RealRandomAccessible}. + *

+ * A view is itself a {@code RealRandomAccessible} or {@code RandomAccessible} + * whose accessors transform coordinates and/or values on-the-fly without + * copying the underlying data. Consecutive transformations are concatenated and + * simplified to provide optimally efficient accessors. + *

+ * The {@code RealRandomAccessibleView} gateway implements {@code + * RealRandomAccessible}, forwarding all methods to its {@link #delegate}. + * Additionally, it provides methods analogous to the {@code static} {@link + * Views} methods that operate on its {@link #delegate} and return {@code + * RandomAccessibleIntervalView}, {@code RandomAccessibleView}, or {@code + * RealRandomAccessibleView} wrappers. + *

+ * This provides a fluent API for conveniently chaining {@code Views} methods. + * For example + *

+ * {@code RealRandomAccessible< IntType > view =
+ *                img.view()
+ *                   .extend( Extension.border() )
+ *                   .interpolate( Interpolation.nLinear() );
+ * }
+ * 
+ * + * @author Tobias Pietzsch + * @author Michael Innerberger + * @see Views + */ +public interface RealRandomAccessibleView< T > extends RealRandomAccessible< T > +{ + RealRandomAccessible< T > delegate(); + + static < T > RealRandomAccessibleView< T > wrap( final RealRandomAccessible< T > delegate ) + { + return () -> delegate; + } + + // -- Views methods ------------------------------------------------------- + + /** + * Create a rasterized {@code RandomAccessible} view of this {@code + * RealRandomAccessible} by providing {@code RandomAccess} at integer + * coordinates. + * + * @return a rasterized view + */ + default RandomAccessibleView< T, ? > raster() + { + return RandomAccessibleView.wrap( Views.raster( delegate() ) ); + } + + /** + * Create a view of this {@code RealRandomAccessible} converted to pixel + * type {@code U}. + *

+ * Pixel values {@code T} are converted to {@code U} using the given {@code + * converter}. A {@code Converter} is equivalent to a {@code BiConsumer} that reads a value from its first argument and writes a converted + * value to its second argument. + * + * @param converter + * converts pixel values from {@code T} to {@code U} + * @param targetSupplier + * creates instances of {@code U} for storing converted values + * @param + * target pixel type + * + * @return a converted view + */ + default < U > RealRandomAccessibleView< U > convert( + final Converter< ? super T, ? super U > converter, + final Supplier< U > targetSupplier ) + { + return wrap( Converters.convert2( delegate(), converter, targetSupplier ) ); + } + + /** + * Create a view of this {@code RealRandomAccessible} converted to pixel + * type {@code U}. + *

+ * Pixel values {@code T} are converted to {@code U} using {@code + * Converter}s created by the given {@code converterSupplier}. A {@code + * Converter} is equivalent to a {@code BiConsumer} that reads a value + * from its first argument and writes a converted value to its second + * argument. + * + * @param converterSupplier + * converts pixel values from {@code T} to {@code U} + * @param targetSupplier + * creates instances of {@code U} for storing converted values + * @param + * target pixel type + * + * @return a converted view + */ + default < U > RealRandomAccessibleView< U > convert( + final Supplier< Converter< ? super T, ? super U > > converterSupplier, + final Supplier< U > targetSupplier ) + { + return wrap( Converters.convert2( delegate(), converterSupplier, targetSupplier ) ); + } + + /** + * Apply the specified {@code function} to this {@code RealRandomAccessible} + * and return the result. + * + * @param function + * function to evaluate on this {@code RealRandomAccessible} + * @param + * the type of the result of the function + * + * @return {@code function.apply(this)} + */ + default < U > U use( Function< ? super RealRandomAccessibleView< T >, U > function ) + { + return function.apply( this ); + } + + + // -- RealRandomAccessible ------------------------------------------------ + + @Override + default RealRandomAccessibleView< T > realView() + { + return this; + } + + @Override + default T getType() + { + return delegate().getType(); + } + + @Override + default int numDimensions() + { + return delegate().numDimensions(); + } + + @Override + default RealRandomAccess< T > realRandomAccess() + { + return delegate().realRandomAccess(); + } + + @Override + default RealRandomAccess< T > realRandomAccess( final RealInterval interval ) + { + return delegate().realRandomAccess( interval ); + } +} diff --git a/src/test/java/net/imglib2/view/fluent/StreamifiedViewsExample.java b/src/test/java/net/imglib2/view/fluent/StreamifiedViewsExample.java new file mode 100644 index 000000000..36be9f446 --- /dev/null +++ b/src/test/java/net/imglib2/view/fluent/StreamifiedViewsExample.java @@ -0,0 +1,73 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2024 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.view.fluent; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealRandomAccessible; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.view.fluent.RandomAccessibleView.Interpolation; +import net.imglib2.view.fluent.RandomAccessibleIntervalView.Extension; + +public class StreamifiedViewsExample +{ + public static void main( String[] args ) + { + RandomAccessibleInterval< IntType > img = ArrayImgs.ints( 100, 100 ); + + RealRandomAccessible< IntType > view = img.view() + .extend( Extension.zero() ) + .interpolate( Interpolation.lanczos() ); + + RealRandomAccessible< DoubleType > doubleView = img.view() + .extend( Extension.zero() ) + .permute( 0, 1 ) + .convert( DoubleType::new, ( i, o ) -> o.set( i.get() ) ) + .interpolate( Interpolation.lanczos() ); + + RandomAccess< IntType > access = img.view() + .extend( Extension.border() ) + .interpolate( Interpolation.nLinear() ) + .raster() + .interval( img ) + .randomAccess(); + + Cursor< IntType > cursor = img.view() + .interval( img ) + .cursor(); + } +} diff --git a/src/test/java/net/imglib2/view/fluent/StreamifiedViewsUseExample.java b/src/test/java/net/imglib2/view/fluent/StreamifiedViewsUseExample.java new file mode 100644 index 000000000..f286fd230 --- /dev/null +++ b/src/test/java/net/imglib2/view/fluent/StreamifiedViewsUseExample.java @@ -0,0 +1,67 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2024 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.view.fluent; + +import java.util.function.Function; + +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.view.fluent.RandomAccessibleIntervalView.Extension; + +public class StreamifiedViewsUseExample +{ + public static void main( String[] args ) + { + RandomAccessibleInterval< IntType > img = ArrayImgs.ints( 100, 100 ); + + final Function< RandomAccessible< ? >, String > fnRa = ra -> "accepts RandomAccessible"; + final Function< RandomAccessibleInterval< ? >, String > fnRai = rai -> "accepts RandomAccessibleInterval"; + + img.view().permute( 0, 1 ).extend( Extension.border() ).use( fnRa ); + img.view().permute( 0, 1 ).use( fnRa ); +// img.view().permute( 0, 1 ).extend( Extensions.border() ).use( fnRai ); // doesn't compile + img.view().permute( 0, 1 ).use( fnRai ); + + img.view().permute( 0, 1 ).extend( Extension.border() ).use( v -> { + RandomAccessible< ? > ra = v.delegate(); + return "lambda"; + } ); + img.view().permute( 0, 1 ).use( v -> { + RandomAccessibleInterval< ? > rai = v.delegate(); + return "lambda"; + } ); + } +}