From ba36be1f167f36f6a5589a3920e30fa33033174b Mon Sep 17 00:00:00 2001 From: tpietzsch Date: Mon, 21 Oct 2024 16:36:59 +0200 Subject: [PATCH] Revise blocks API * Instead of delegating execution to BlockProcessor, UnaryBlockOperator takes care of chaining operations now: The new compute(BlockSupplier source, Interval, Object dest) method handles source interval computation, getting source data, and computing result in one go, instead of the sequential BlockProcessor API. This is much nicer. It enables pass-through operators (Convert.Identity or logging for example). It should also be easier now to extend the API to keep data on the GPU for sequences of GPU operators. * The copy/compute API all operate on Interval now. Other signatures have been removed for simplicity. With the new BlockInterval class from imglib2 core, unnecessarily converting between int[] and long[] is usually avoided. --- .../blocks/AbstractBlockProcessor.java | 50 ++--- .../blocks/AbstractBlockSupplier.java | 5 +- .../AbstractDimensionlessBlockProcessor.java | 54 ++--- ...r.java => AbstractUnaryBlockOperator.java} | 58 +++--- .../algorithm/blocks/BlockProcessor.java | 59 +++--- .../blocks/BlockProcessorTargetInterval.java | 78 ------- .../algorithm/blocks/BlockSupplier.java | 191 ++++++++---------- .../blocks/ConcatenatedBlockProcessor.java | 96 --------- .../blocks/ConcatenatedBlockSupplier.java | 33 ++- .../ConcatenatedUnaryBlockOperator.java | 80 ++++++++ .../blocks/DefaultUnaryBlockOperator.java | 122 ++++------- .../blocks/PrimitiveBlocksSupplier.java | 11 +- ...ier.java => TilingUnaryBlockOperator.java} | 120 ++++++----- .../algorithm/blocks/UnaryBlockOperator.java | 92 ++++----- .../algorithm/blocks/convert/Convert.java | 44 +++- .../transform/AbstractTransformProcessor.java | 12 +- .../util/BlockProcessorSourceInterval.java | 75 ------- .../downsample/DownsampleBenchmarkFull.java | 2 +- .../downsample/DownsampleBenchmarkFull2.java | 2 +- .../transform/TransformBenchmark3D.java | 27 ++- .../TransformBenchmark3DonlyCompute.java | 11 +- .../transform/TransformPlayground3D.java | 3 +- 22 files changed, 476 insertions(+), 749 deletions(-) rename src/main/java/net/imglib2/algorithm/blocks/{NoOpUnaryBlockOperator.java => AbstractUnaryBlockOperator.java} (66%) delete mode 100644 src/main/java/net/imglib2/algorithm/blocks/BlockProcessorTargetInterval.java delete mode 100644 src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockProcessor.java create mode 100644 src/main/java/net/imglib2/algorithm/blocks/ConcatenatedUnaryBlockOperator.java rename src/main/java/net/imglib2/algorithm/blocks/{TilingBlockSupplier.java => TilingUnaryBlockOperator.java} (60%) delete mode 100644 src/main/java/net/imglib2/algorithm/blocks/util/BlockProcessorSourceInterval.java diff --git a/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockProcessor.java b/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockProcessor.java index abd095b9b..6c1923486 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockProcessor.java +++ b/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockProcessor.java @@ -35,10 +35,8 @@ import static net.imglib2.util.Util.safeInt; -import java.util.Arrays; - import net.imglib2.Interval; -import net.imglib2.algorithm.blocks.util.BlockProcessorSourceInterval; +import net.imglib2.blocks.BlockInterval; import net.imglib2.blocks.TempArray; import net.imglib2.type.PrimitiveType; import net.imglib2.util.Intervals; @@ -51,12 +49,12 @@ * adaptable number of dimensions (such as converters), see {@link * AbstractDimensionlessBlockProcessor}. *

- * {@link BlockProcessor#getSourcePos() getSourcePos()}, {@link - * BlockProcessor#getSourceSize() getSourceSize()}, and {@link - * BlockProcessor#getSourceInterval() getSourceInterval()} are implemented to - * return the {@code protected} fields {@code long[] sourcePos} and {@code - * }int[] sourceSize}. The {@code }protected} method {@code }int sourceLength()} - * can be used to get the number of elements in the source interval. + * A {@link BlockInterval} of the desired number of dimensions is exposed + * through {@link #getSourceInterval()}. (For convenience, {@code min} and + * {@code dimensions} of the interval are also exposed through the {@code + * protected} fields {@link #sourcePos} and {@link #sourceSize}.) The {@code + * protected} method {@link #sourceLength()} returns the number of elements in + * the source interval. *

* {@link BlockProcessor#getSourceBuffer() getSourceBuffer()} is implemented * according to the {@code sourcePrimitiveType} specified at construction. @@ -70,53 +68,41 @@ public abstract class AbstractBlockProcessor< I, O > implements BlockProcessor< { private final TempArray< I > tempArray; + private final BlockInterval sourceInterval; + protected final long[] sourcePos; protected final int[] sourceSize; - private final BlockProcessorSourceInterval sourceInterval = new BlockProcessorSourceInterval( this ); - protected AbstractBlockProcessor( final PrimitiveType sourcePrimitiveType, final int numSourceDimensions ) { tempArray = TempArray.forPrimitiveType( sourcePrimitiveType ); - sourcePos = new long[ numSourceDimensions ]; - sourceSize = new int[ numSourceDimensions ]; + sourceInterval = new BlockInterval( numSourceDimensions ); + sourcePos = sourceInterval.min(); + sourceSize = sourceInterval.size(); } protected AbstractBlockProcessor( final AbstractBlockProcessor< I, O > proc ) { tempArray = proc.tempArray.newInstance(); - final int numSourceDimensions = proc.sourcePos.length; - sourcePos = new long[ numSourceDimensions ]; - sourceSize = new int[ numSourceDimensions ]; + sourceInterval = new BlockInterval( proc.sourceInterval.numDimensions() ); + sourcePos = sourceInterval.min(); + sourceSize = sourceInterval.size(); } protected int sourceLength() { - return safeInt( Intervals.numElements( sourceSize ) ); + return safeInt( Intervals.numElements( sourceInterval ) ); } @Override public void setTargetInterval( final Interval interval ) { - interval.min( sourcePos ); - Arrays.setAll( sourceSize, d -> safeInt( interval.dimension( d ) ) ); - } - - @Override - public long[] getSourcePos() - { - return sourcePos; - } - - @Override - public int[] getSourceSize() - { - return sourceSize; + sourceInterval.setFrom( interval ); } @Override - public Interval getSourceInterval() + public BlockInterval getSourceInterval() { return sourceInterval; } diff --git a/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockSupplier.java b/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockSupplier.java index 7e0d513c3..80e734201 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockSupplier.java +++ b/src/main/java/net/imglib2/algorithm/blocks/AbstractBlockSupplier.java @@ -35,6 +35,7 @@ import java.util.function.Supplier; +import net.imglib2.Interval; import net.imglib2.type.NativeType; import net.imglib2.util.CloseableThreadLocal; @@ -68,9 +69,9 @@ public int numDimensions() } @Override - public void copy( final long[] srcPos, final Object dest, final int[] size ) + public void copy( final Interval interval, final Object dest ) { - threadSafeSupplier.get().copy( srcPos, dest, size ); + threadSafeSupplier.get().copy( interval, dest ); } @Override diff --git a/src/main/java/net/imglib2/algorithm/blocks/AbstractDimensionlessBlockProcessor.java b/src/main/java/net/imglib2/algorithm/blocks/AbstractDimensionlessBlockProcessor.java index e73bc918b..97f5c0fd2 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/AbstractDimensionlessBlockProcessor.java +++ b/src/main/java/net/imglib2/algorithm/blocks/AbstractDimensionlessBlockProcessor.java @@ -35,10 +35,8 @@ import static net.imglib2.util.Util.safeInt; -import java.util.Arrays; - import net.imglib2.Interval; -import net.imglib2.algorithm.blocks.util.BlockProcessorSourceInterval; +import net.imglib2.blocks.BlockInterval; import net.imglib2.blocks.TempArray; import net.imglib2.type.PrimitiveType; import net.imglib2.util.Intervals; @@ -50,12 +48,10 @@ * number of dimensions (such as converters). For {@code BlockProcessor} with a * fixed number of source dimensions, see {@link AbstractBlockProcessor}. *

- * {@link BlockProcessor#getSourcePos() getSourcePos()}, {@link - * BlockProcessor#getSourceSize() getSourceSize()}, and {@link - * BlockProcessor#getSourceInterval() getSourceInterval()} are implemented to - * return the {@code protected} fields {@code long[] sourcePos} and {@code - * }int[] sourceSize}. The {@code }protected} method {@code }int sourceLength()} - * can be used to get the number of elements in the source interval. + * A {@link BlockInterval} is exposed through {@link #getSourceInterval()} + * (after {@link #setTargetInterval} has been called). The {@code protected} + * method {@link #sourceLength()} returns the number of elements in the source + * interval. *

* {@link BlockProcessor#getSourceBuffer() getSourceBuffer()} is implemented * according to the {@code sourcePrimitiveType} specified at construction. @@ -69,11 +65,7 @@ public abstract class AbstractDimensionlessBlockProcessor< I, O > implements Blo { private final TempArray< I > tempArray; - protected long[] sourcePos; - - protected int[] sourceSize; - - private final BlockProcessorSourceInterval sourceInterval = new BlockProcessorSourceInterval( this ); + protected BlockInterval sourceInterval; protected AbstractDimensionlessBlockProcessor( final PrimitiveType sourcePrimitiveType ) { @@ -88,26 +80,24 @@ protected AbstractDimensionlessBlockProcessor( final AbstractDimensionlessBlockP @Override public void setTargetInterval( final Interval interval ) { - updateNumSourceDimsensions( interval.numDimensions() ); - interval.min( sourcePos ); - Arrays.setAll( sourceSize, d -> safeInt( interval.dimension( d ) ) ); + updateNumSourceDimensions( interval.numDimensions() ); + sourceInterval.setFrom( interval ); } /** - * Re-allocates {@code sourcePos} and {@code sourceSize} arrays if they do - * not already exist and have {@code length==n}. + * Re-allocates {@code sourceInterval} if it does not already exist and + * match {@code numDimensions()==n}. * * @param n * new number of source dimensions * - * @return {@code true} if {@code sourcePos} and {@code sourceSize} arrays were re-allocated + * @return {@code true} if {@code sourceInterval} was re-allocated */ - protected boolean updateNumSourceDimsensions( final int n ) + protected boolean updateNumSourceDimensions( final int n ) { - if ( sourcePos == null || sourcePos.length != n ) + if ( sourceInterval == null || sourceInterval.numDimensions() != n ) { - sourcePos = new long[ n ]; - sourceSize = new int[ n ]; + sourceInterval = new BlockInterval( n ); return true; } return false; @@ -115,23 +105,11 @@ protected boolean updateNumSourceDimsensions( final int n ) protected int sourceLength() { - return safeInt( Intervals.numElements( sourceSize ) ); - } - - @Override - public long[] getSourcePos() - { - return sourcePos; - } - - @Override - public int[] getSourceSize() - { - return sourceSize; + return safeInt( Intervals.numElements( sourceInterval ) ); } @Override - public Interval getSourceInterval() + public BlockInterval getSourceInterval() { return sourceInterval; } diff --git a/src/main/java/net/imglib2/algorithm/blocks/NoOpUnaryBlockOperator.java b/src/main/java/net/imglib2/algorithm/blocks/AbstractUnaryBlockOperator.java similarity index 66% rename from src/main/java/net/imglib2/algorithm/blocks/NoOpUnaryBlockOperator.java rename to src/main/java/net/imglib2/algorithm/blocks/AbstractUnaryBlockOperator.java index be69c830b..26786b261 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/NoOpUnaryBlockOperator.java +++ b/src/main/java/net/imglib2/algorithm/blocks/AbstractUnaryBlockOperator.java @@ -11,13 +11,13 @@ * %% * 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 @@ -33,65 +33,61 @@ */ package net.imglib2.algorithm.blocks; -import java.util.function.Supplier; - import net.imglib2.type.NativeType; -import net.imglib2.util.Cast; -import net.imglib2.util.CloseableThreadLocal; /** - * Does nothing. This should be eliminated when concatenating through - * {@link UnaryBlockOperator#andThen(UnaryBlockOperator)} or {@link BlockSupplier#andThen(UnaryBlockOperator)}. + * Abstract implementation of {@link UnaryBlockOperator}. + * Takes care of source/target type/dimensionality boilerplate. * + * @param + * source type * @param + * target type */ -public class NoOpUnaryBlockOperator< S extends NativeType< S >, T extends NativeType< T > > implements UnaryBlockOperator< S, T > +public abstract class AbstractUnaryBlockOperator< S extends NativeType< S >, T extends NativeType< T > > implements UnaryBlockOperator< S, T > { - @Override - public < I, O > BlockProcessor< I, O > blockProcessor() + private final S sourceType; + private final T targetType; + private final int numSourceDimensions; + private final int numTargetDimensions; + + protected AbstractUnaryBlockOperator( S sourceType, T targetType, int numSourceDimensions, int numTargetDimensions ) { - throw new UnsupportedOperationException(); + this.sourceType = sourceType; + this.targetType = targetType; + this.numSourceDimensions = numSourceDimensions; + this.numTargetDimensions = numTargetDimensions; } - @Override - public < U extends NativeType< U > > UnaryBlockOperator< S, U > andThen( UnaryBlockOperator< T, U > op ) + protected AbstractUnaryBlockOperator( AbstractUnaryBlockOperator< S, T > op ) { - return Cast.unchecked( op ); + this.sourceType = op.sourceType; + this.targetType = op.targetType; + this.numSourceDimensions = op.numSourceDimensions; + this.numTargetDimensions = op.numTargetDimensions; } @Override public S getSourceType() { - throw new UnsupportedOperationException(); + return sourceType; } @Override public T getTargetType() { - throw new UnsupportedOperationException(); + return targetType; } @Override public int numSourceDimensions() { - return 0; + return numSourceDimensions; } @Override public int numTargetDimensions() { - return 0; - } - - @Override - public UnaryBlockOperator< S, T > independentCopy() - { - return this; - } - - @Override - public UnaryBlockOperator< S, T > threadSafe() - { - return this; + return numTargetDimensions; } } diff --git a/src/main/java/net/imglib2/algorithm/blocks/BlockProcessor.java b/src/main/java/net/imglib2/algorithm/blocks/BlockProcessor.java index 1bbb25658..fbfae4a09 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/BlockProcessor.java +++ b/src/main/java/net/imglib2/algorithm/blocks/BlockProcessor.java @@ -34,14 +34,13 @@ package net.imglib2.algorithm.blocks; import net.imglib2.Interval; -import net.imglib2.blocks.PrimitiveBlocks; /** * A {@code BlockProcessor} computes values in a flattened primitive output * array from values in a flattened primitive input array. *

- * Typically, {@code BlockProcessor} should not be used directly, but - * wrapped in {@link UnaryBlockOperator} which has the ImgLib2 {@code Type}s + * Typically, {@code BlockProcessor} should not be used directly, but wrapped in + * {@link DefaultUnaryBlockOperator} which has the ImgLib2 {@code Type}s * corresponding to {@code I}, {@code O}. This helps to avoid mistakes with * unchecked (primitive array) type casts. * @@ -56,21 +55,26 @@ public interface BlockProcessor< I, O > void setTargetInterval( Interval interval ); - default void setTargetInterval( long[] pos, int[] size ) - { - setTargetInterval( new BlockProcessorTargetInterval( pos, size ) ); - } - - long[] getSourcePos(); - - int[] getSourceSize(); - Interval getSourceInterval(); /** * Get a {@code src} array of sufficient size. *

- * Consecutive calls may return the same array, but don't have to. + * Consecutive calls may return the same array (but don't have to). + *

+ * E.g., this is a BUG: + *

{@code
+	 *     blockProcessor.setTargetInterval( interval );
+	 *     blockSupplier.copy( blockProcessor.getSourceInterval(), blockProcessor.getSourceBuffer() );
+	 *     blockProcessor.compute( blockProcessor.getSourceBuffer(), dest );
+	 * }
+ * Use this instead: + *
{@code
+	 *     blockProcessor.setTargetInterval( interval );
+	 *     Object buf = blockProcessor.getSourceBuffer();
+	 *     blockSupplier.copy( blockProcessor.getSourceInterval(), buf );
+	 *     blockProcessor.compute( buf, dest );
+	 * }
*/ I getSourceBuffer(); @@ -78,18 +82,17 @@ default void setTargetInterval( long[] pos, int[] size ) * Compute the {@code dest} array from the {@code src} array. *

* {@code src} and {@code dest} are expected to be flattened arrays of the - * dimensions specified in {@link #setTargetInterval} and computed in {@link - * #getSourceSize}, respectively. The typical sequence is: + * dimensions specified in {@link #setTargetInterval} and provided in {@link + * #getSourceInterval}, respectively. The typical sequence is: *

    *
  1. A given target array {@code dest} with known flattened layout and min * position should be computed.
  2. *
  3. Call {@link #setTargetInterval}. This will compute the corresponding - * {@link #getSourceInterval source interval} (also available as {@link - * #getSourcePos}, {@link #getSourceSize}) including {@code + * {@link #getSourceInterval source interval} including {@code * BlockProcessor}-specific transformations, padding, etc.
  4. *
  5. Fill a {@code src} array (either provided by {@link * #getSourceBuffer}, or otherwise allocated) with the input data (see - * {@link PrimitiveBlocks#copy}).
  6. + * {@link BlockSupplier#copy}). *
  7. Call {@code compute(src, dest)} to compute the target array.
  8. *
* Note, that the {@code src} and {@code dest} arrays may be larger than @@ -97,8 +100,8 @@ default void setTargetInterval( long[] pos, int[] size ) * case the trailing elements are ignored. *

* Typically, {@code BlockProcessor} should not be used directly, but - * wrapped in {@link UnaryBlockOperator} which has the ImgLib2 {@code Type}s - * corresponding to {@code I}, {@code O}. + * wrapped in {@link DefaultUnaryBlockOperator} which has the ImgLib2 {@code + * Type}s corresponding to {@code I}, {@code O}. * * @param src * flattened primitive array with input values @@ -106,20 +109,4 @@ default void setTargetInterval( long[] pos, int[] size ) * flattened primitive array to fill with output values */ void compute( I src, O dest ); - - /** - * Returns a {@code BlockProcessor concatenated} such that - *

{@code
-	 *   concatenated.compute(src, dest);
-	 * }
- * is equivalent to - *
{@code
-	 *   this.compute(src, tmp);
-	 *   processor.compute(tmp, dest);
-	 * }
- */ - default < P > BlockProcessor< I, P > andThen( BlockProcessor< O, P > processor ) - { - return new ConcatenatedBlockProcessor<>( this, processor ); - } } diff --git a/src/main/java/net/imglib2/algorithm/blocks/BlockProcessorTargetInterval.java b/src/main/java/net/imglib2/algorithm/blocks/BlockProcessorTargetInterval.java deleted file mode 100644 index 08c2862dd..000000000 --- a/src/main/java/net/imglib2/algorithm/blocks/BlockProcessorTargetInterval.java +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * #%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.algorithm.blocks; - -import net.imglib2.Interval; - -/** - * Wraps {@code long[] pos} and {@code int[] size} as a {@code Interval}, for - * the default implementation of {@link BlockProcessor#setTargetInterval(long[], - * int[])}. - */ -class BlockProcessorTargetInterval implements Interval -{ - private final long[] min; - - private final int[] size; - - BlockProcessorTargetInterval( long[] min, int[] size ) - { - this.min = min; - this.size = size; - } - - @Override - public long min( final int i ) - { - return min[ i ]; - } - - @Override - public long max( final int i ) - { - return min[ i ] + size[ i ] - 1; - } - - @Override - public long dimension( final int d ) - { - return size[ d ]; - } - - @Override - public int numDimensions() - { - return min.length; - } -} diff --git a/src/main/java/net/imglib2/algorithm/blocks/BlockSupplier.java b/src/main/java/net/imglib2/algorithm/blocks/BlockSupplier.java index b8983f452..34cf27acb 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/BlockSupplier.java +++ b/src/main/java/net/imglib2/algorithm/blocks/BlockSupplier.java @@ -33,12 +33,6 @@ */ package net.imglib2.algorithm.blocks; -import static net.imglib2.blocks.PrimitiveBlocks.OnFallback.WARN; -import static net.imglib2.util.Util.safeInt; - -import java.util.Arrays; -import java.util.function.Function; - import net.imglib2.EuclideanSpace; import net.imglib2.Interval; import net.imglib2.RandomAccessible; @@ -46,44 +40,50 @@ import net.imglib2.blocks.PrimitiveBlocks; import net.imglib2.cache.img.CachedCellImg; import net.imglib2.type.NativeType; -import net.imglib2.util.Cast; -import net.imglib2.util.Util; -public interface BlockSupplier< T extends NativeType< T > > extends Typed< T >, EuclideanSpace -{ - /** - * Copy a block from the ({@code T}-typed) source into primitive arrays (of - * the appropriate type). - * - * @param srcPos - * min coordinate of the block to copy - * @param dest - * primitive array to copy into. Must correspond to {@code T}, for - * example, if {@code T} is {@code UnsignedByteType} then {@code dest} must - * be {@code byte[]}. - * @param size - * the size of the block to copy - */ - void copy( long[] srcPos, Object dest, int[] size ); +import java.util.function.Function; - /** - * Copy a block from the ({@code T}-typed) source into primitive arrays (of - * the appropriate type). - * - * @param srcPos - * min coordinate of the block to copy - * @param dest - * primitive array to copy into. Must correspond to {@code T}, for - * example, if {@code T} is {@code UnsignedByteType} then {@code dest} must - * be {@code byte[]}. - * @param size - * the size of the block to copy - */ - default void copy( int[] srcPos, Object dest, int[] size ) - { - copy( Util.int2long( srcPos ), dest, size ); - } +import static net.imglib2.blocks.PrimitiveBlocks.OnFallback.WARN; +/** + * Provides blocks of data from a {@code NativeType} source. + * Use the {@link BlockSupplier#copy} method to copy a block out of the + * source into flat primitive array (of the appropriate type). + *

+ * Use the static method {@link BlockSupplier#of(RandomAccessible) + * BlockSupplier.of} to create a {@code BlockSupplier} accessor from an {@code + * RandomAccessible} source. (This is just a thin wrapper around {@link + * PrimitiveBlocks}). + *

+ * Currently, only pixel types {@code T} are supported that map one-to-one to a + * primitive type. (For example, {@code ComplexDoubleType} or {@code + * Unsigned4BitType} are not supported.) + *

+ * If a source {@code RandomAccessible} view construction cannot be understood, + * {@link BlockSupplier#of(RandomAccessible) BlockSupplier.of} will return a + * fall-back implementation. Fallback can be configured with the optional {@link + * PrimitiveBlocks.OnFallback OnFallback} argument to {@link + * BlockSupplier#of(RandomAccessible, PrimitiveBlocks.OnFallback) + * BlockSupplier.of}. + *

+ * Use {@link BlockSupplier#andThen} to decorate a {@code BlockSupplier} with a + * sequence of {@code UnaryBlockOperator}s. + *

+ * Use {@link BlockSupplier#toCellImg} to create {@code CachedCellImg} which + * copies cells from a {@code BlockSupplier}. + *

+ * Implementations are not thread-safe in general. Use {@link #threadSafe()} to + * obtain a thread-safe instance (implemented using {@link ThreadLocal} copies). + * E.g., + *

{@code
+ * 		BlockSupplier blocks = BlockSupplier.of(view).threadSafe();
+ * }
+ * + * @param + * pixel type + */ +public interface BlockSupplier< T extends NativeType< T > > extends Typed< T >, EuclideanSpace +{ /** * Copy a block from the ({@code T}-typed) source into primitive arrays (of * the appropriate type). @@ -95,13 +95,7 @@ default void copy( int[] srcPos, Object dest, int[] size ) * example, if {@code T} is {@code UnsignedByteType} then {@code dest} must * be {@code byte[]}. */ - default void copy( Interval interval, Object dest ) - { - final long[] srcPos = interval.minAsLongArray(); - final int[] size = new int[ srcPos.length ]; - Arrays.setAll( size, d -> safeInt( interval.dimension( d ) ) ); - copy( srcPos, dest, size ); - } + void copy( Interval interval, Object dest ); /** * Get a thread-safe version of this {@code BlockSupplier}. @@ -116,40 +110,12 @@ default void copy( Interval interval, Object dest ) BlockSupplier< T > independentCopy(); /** - * Returns a new {@code BlockSupplier} that handles {@link #copy} requests - * by splitting into {@code tileSize} portions that are each handled by this - * {@code BlockSupplier} and assembled into the final result. - *

- * Example use cases: - *

    - *
  • Compute large outputs (e.g. for writing to N5 or wrapping as {@code - * ArrayImg}) with operators that have better performance with small - * block sizes.
  • - *
  • Avoid excessively large blocks when chaining downsampling - * operators.
  • - *
- * - * @param tileSize - * (maximum) dimensions of a request to the {@code srcSupplier}. - * {@code tileSize} is expanded or truncated to the necessary size. For - * example, if {@code tileSize=={64}} and this {@code BlockSupplier} is 3D, - * then {@code tileSize} is expanded to {@code {64, 64, 64}}. - */ - default BlockSupplier< T > tile( int... tileSize ) - { - return new TilingBlockSupplier<>( this, tileSize ); - } - - /** - * Returns a {@code UnaryBlockOperator} that is equivalent to applying - * {@code this}, and then applying {@code op} to the result. + * Returns a {@code BlockSupplier} that provide blocks using the given + * {@code operator} with this {@code BlockSupplier} as input. */ default < U extends NativeType< U > > BlockSupplier< U > andThen( UnaryBlockOperator< T, U > operator ) { - if ( operator instanceof NoOpUnaryBlockOperator ) - return Cast.unchecked( this ); - else - return new ConcatenatedBlockSupplier<>( this.independentCopy(), operator.independentCopy() ); + return operator.applyTo( this ); } default < U extends NativeType< U > > BlockSupplier< U > andThen( Function< BlockSupplier< T >, UnaryBlockOperator< T, U > > function ) @@ -157,25 +123,6 @@ default < U extends NativeType< U > > BlockSupplier< U > andThen( Function< Bloc return andThen( function.apply( this ) ); } - /** - * Return a {@code CachedCellImg} which copies cells from this {@code BlockSupplier}. - * - * @param dimensions - * dimensions of the {@code CachedCellImg} to create - * @param cellDimensions - * block size of the {@code CachedCellImg} to create. - * This is extended or truncated as necessary. - * For example if {@code cellDimensions={64,32}} then for creating a 3D - * image it will be augmented to {@code {64,32,32}}. For creating a 1D image - * it will be truncated to {@code {64}}. - * - * @return a {@code CachedCellImg} which copies cells from this {@code BlockSupplier}. - */ - default CachedCellImg< T, ? > toCellImg( final long[] dimensions, final int... cellDimensions ) - { - return BlockAlgoUtils.cellImg( this, dimensions, cellDimensions ); - } - /** * Create a {@code BlockSupplier} accessor for an arbitrary {@code * RandomAccessible} source. Many View constructions (that ultimately end in @@ -229,11 +176,47 @@ static < T extends NativeType< T > > BlockSupplier< T > of( return new PrimitiveBlocksSupplier<>( PrimitiveBlocks.of( ra, onFallback ) ); } - /* - * Wrap the given {@code PrimitiveBlocks} as a {@code BlockSupplier}. + /** + * Return a {@code CachedCellImg} which copies cells from this {@code BlockSupplier}. + * + * @param dimensions + * dimensions of the {@code CachedCellImg} to create + * @param cellDimensions + * block size of the {@code CachedCellImg} to create. + * This is extended or truncated as necessary. + * For example if {@code cellDimensions={64,32}} then for creating a 3D + * image it will be augmented to {@code {64,32,32}}. For creating a 1D image + * it will be truncated to {@code {64}}. + * + * @return a {@code CachedCellImg} which copies cells from this {@code BlockSupplier}. + */ + default CachedCellImg< T, ? > toCellImg( final long[] dimensions, final int... cellDimensions ) + { + return BlockAlgoUtils.cellImg( this, dimensions, cellDimensions ); + } + + /** + * Returns a new {@code BlockSupplier} that handles {@link #copy} requests + * by splitting into {@code tileSize} portions that are each handled by this + * {@code BlockSupplier} and assembled into the final result. + *

+ * Example use cases: + *

    + *
  • Compute large outputs (e.g. for writing to N5 or wrapping as {@code + * ArrayImg}) with operators that have better performance with small + * block sizes.
  • + *
  • Avoid excessively large blocks when chaining downsampling + * operators.
  • + *
+ * + * @param tileSize + * (maximum) dimensions of a request to the {@code srcSupplier}. + * {@code tileSize} is expanded or truncated to the necessary size. For + * example, if {@code tileSize=={64}} and this {@code BlockSupplier} is 3D, + * then {@code tileSize} is expanded to {@code {64, 64, 64}}. */ -// static < T extends NativeType< T > > BlockSupplier< T > of( PrimitiveBlocks< T > blocks ) -// { -// return new PrimitiveBlocksSupplier<>( blocks ); -// } + default BlockSupplier< T > tile( int... tileSize ) + { + return this.andThen( new TilingUnaryBlockOperator<>( getType(), numDimensions(), tileSize ) ); + } } diff --git a/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockProcessor.java b/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockProcessor.java deleted file mode 100644 index 6b2d26040..000000000 --- a/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockProcessor.java +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * #%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.algorithm.blocks; - -import net.imglib2.Interval; - -class ConcatenatedBlockProcessor< I, K, O > implements BlockProcessor< I, O > -{ - private final BlockProcessor< I, K > p0; - - private final BlockProcessor< K, O > p1; - - public ConcatenatedBlockProcessor( - BlockProcessor< I, K > p0, - BlockProcessor< K, O > p1 ) - { - this.p0 = p0; - this.p1 = p1; - } - - @Override - public ConcatenatedBlockProcessor< I, K, O > independentCopy() - { - return new ConcatenatedBlockProcessor<>( p0.independentCopy(), p1.independentCopy() ); - } - - @Override - public void setTargetInterval( final Interval interval ) - { - p1.setTargetInterval( interval ); - p0.setTargetInterval( p1.getSourceInterval() ); - } - - @Override - public long[] getSourcePos() - { - return p0.getSourcePos(); - } - - @Override - public int[] getSourceSize() - { - return p0.getSourceSize(); - } - - @Override - public Interval getSourceInterval() - { - return p0.getSourceInterval(); - } - - @Override - public I getSourceBuffer() - { - return p0.getSourceBuffer(); - } - - @Override - public void compute( final I src, final O dest ) - { - final K temp = p1.getSourceBuffer(); - p0.compute( src, temp ); - p1.compute( temp, dest ); - } -} diff --git a/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockSupplier.java b/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockSupplier.java index b7917073f..691b7572c 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockSupplier.java +++ b/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedBlockSupplier.java @@ -33,26 +33,23 @@ */ package net.imglib2.algorithm.blocks; +import net.imglib2.Interval; import net.imglib2.type.NativeType; -import net.imglib2.util.Cast; -class ConcatenatedBlockSupplier< T extends NativeType< T > > extends AbstractBlockSupplier< T > +class ConcatenatedBlockSupplier< S extends NativeType< S >, T extends NativeType< T > > extends AbstractBlockSupplier< T > { - private final BlockSupplier< ? > p0; + private final BlockSupplier< S > src; - private final BlockProcessor< ?, ? > p1; - - private final T type; + private final UnaryBlockOperator< S, T > operator; private final int numDimensions; - public < S extends NativeType< S > > ConcatenatedBlockSupplier( + ConcatenatedBlockSupplier( final BlockSupplier< S > srcSupplier, final UnaryBlockOperator< S, T > operator ) { - this.p0 = srcSupplier; - this.p1 = operator.blockProcessor(); - this.type = operator.getTargetType(); + this.src = srcSupplier; + this.operator = operator; if ( operator.numSourceDimensions() > 0 ) { if ( srcSupplier.numDimensions() != operator.numSourceDimensions() ) @@ -65,18 +62,17 @@ public < S extends NativeType< S > > ConcatenatedBlockSupplier( } } - private ConcatenatedBlockSupplier( final ConcatenatedBlockSupplier< T > s ) + private ConcatenatedBlockSupplier( final ConcatenatedBlockSupplier< S, T > s ) { - p0 = s.p0.independentCopy(); - p1 = s.p1.independentCopy(); - type = s.type; + src = s.src.independentCopy(); + operator = s.operator.independentCopy(); numDimensions = s.numDimensions; } @Override public T getType() { - return type; + return operator.getTargetType(); } @Override @@ -86,12 +82,9 @@ public int numDimensions() } @Override - public void copy( final long[] srcPos, final Object dest, final int[] size ) + public void copy( final Interval interval, final Object dest ) { - p1.setTargetInterval( srcPos, size ); - final Object src = p1.getSourceBuffer(); - p0.copy( p1.getSourcePos(), src, p1.getSourceSize() ); - p1.compute( Cast.unchecked( src ), Cast.unchecked( dest ) ); + operator.compute( src, interval, dest ); } @Override diff --git a/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedUnaryBlockOperator.java b/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedUnaryBlockOperator.java new file mode 100644 index 000000000..3c8c9907d --- /dev/null +++ b/src/main/java/net/imglib2/algorithm/blocks/ConcatenatedUnaryBlockOperator.java @@ -0,0 +1,80 @@ +package net.imglib2.algorithm.blocks; + +import net.imglib2.Interval; +import net.imglib2.type.NativeType; + +class ConcatenatedUnaryBlockOperator< + S extends NativeType< S >, + T extends NativeType< T >, + U extends NativeType< U > > + implements UnaryBlockOperator< S, U > +{ + private final UnaryBlockOperator< S, T > op1; + private final UnaryBlockOperator< T, U > op2; + private final int numSourceDimensions; + private final int numTargetDimensions; + + ConcatenatedUnaryBlockOperator( UnaryBlockOperator< S, T > op1, UnaryBlockOperator< T, U > op2 ) + { + this.op1 = op1; + this.op2 = op2; + + final boolean op1HasDims = op1.numSourceDimensions() > 0; + final boolean op2HasDims = op2.numSourceDimensions() > 0; + if ( op2HasDims && op1HasDims && op1.numTargetDimensions() != op2.numSourceDimensions() ) { + throw new IllegalArgumentException( "UnaryBlockOperator cannot be concatenated: number of dimensions mismatch." ); + } + this.numSourceDimensions = op1HasDims ? op1.numSourceDimensions() : op2.numSourceDimensions(); + this.numTargetDimensions = op2HasDims ? op2.numTargetDimensions() : op1.numTargetDimensions(); + } + + private ConcatenatedUnaryBlockOperator( ConcatenatedUnaryBlockOperator< S, T, U > op ) + { + this.op1 = op.op1.independentCopy(); + this.op2 = op.op2.independentCopy(); + this.numSourceDimensions = op.numSourceDimensions; + this.numTargetDimensions = op.numTargetDimensions; + } + + @Override + public void compute( final BlockSupplier< S > src, final Interval interval, final Object dest ) + { + applyTo( src ).copy( interval, dest ); + } + + @Override + public S getSourceType() + { + return op1.getSourceType(); + } + + @Override + public U getTargetType() + { + return op2.getTargetType(); + } + + @Override + public int numSourceDimensions() + { + return 0; + } + + @Override + public int numTargetDimensions() + { + return 0; + } + + @Override + public UnaryBlockOperator< S, U > independentCopy() + { + return new ConcatenatedUnaryBlockOperator<>( this ); + } + + @Override + public BlockSupplier< U > applyTo( final BlockSupplier< S > blocks ) + { + return op2.applyTo( op1.applyTo( blocks ) ); + } +} diff --git a/src/main/java/net/imglib2/algorithm/blocks/DefaultUnaryBlockOperator.java b/src/main/java/net/imglib2/algorithm/blocks/DefaultUnaryBlockOperator.java index fdba81fcc..cf059fbe2 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/DefaultUnaryBlockOperator.java +++ b/src/main/java/net/imglib2/algorithm/blocks/DefaultUnaryBlockOperator.java @@ -33,10 +33,9 @@ */ package net.imglib2.algorithm.blocks; -import java.util.function.Supplier; +import net.imglib2.Interval; import net.imglib2.type.NativeType; import net.imglib2.util.Cast; -import net.imglib2.util.CloseableThreadLocal; /** * Default implementation of {@link UnaryBlockOperator}. @@ -48,117 +47,64 @@ *

* The {@link UnaryBlockOperator} can then be used in {@link * BlockSupplier#andThen(UnaryBlockOperator)} chains in a type- and - * simensionality-safe manner. + * dimensionality-safe manner. * * @param * source type * @param * target type */ -public class DefaultUnaryBlockOperator< S extends NativeType< S >, T extends NativeType< T > > implements UnaryBlockOperator< S, T > +public class DefaultUnaryBlockOperator< S extends NativeType< S >, T extends NativeType< T > > extends AbstractUnaryBlockOperator< S, T > { - private final S sourceType; - private final T targetType; - private final int numSourceDimensions; - private final int numTargetDimensions; - private final BlockProcessor< ?, ? > blockProcessor; + @SuppressWarnings( "rawtypes" ) + private final BlockProcessor blockProcessor; public DefaultUnaryBlockOperator( S sourceType, T targetType, int numSourceDimensions, int numTargetDimensions, BlockProcessor< ?, ? > blockProcessor ) { - this.sourceType = sourceType; - this.targetType = targetType; - this.numSourceDimensions = numSourceDimensions; - this.numTargetDimensions = numTargetDimensions; + super( sourceType, targetType, numSourceDimensions, numTargetDimensions ); this.blockProcessor = blockProcessor; } - @Override - public < I, O > BlockProcessor< I, O > blockProcessor() - { - return Cast.unchecked( blockProcessor ); - } - - @Override - public S getSourceType() - { - return sourceType; - } - - @Override - public T getTargetType() - { - return targetType; - } - - @Override - public int numSourceDimensions() + private DefaultUnaryBlockOperator( DefaultUnaryBlockOperator< S, T > op ) { - return numSourceDimensions; + super( op ); + this.blockProcessor = op.blockProcessor.independentCopy(); } + @SuppressWarnings( "unchecked" ) @Override - public int numTargetDimensions() + public void compute( final BlockSupplier< S > src, final Interval interval, final Object dest ) { - return numTargetDimensions; + blockProcessor.setTargetInterval( interval ); + final Object buf = blockProcessor.getSourceBuffer(); + src.copy( blockProcessor.getSourceInterval(), buf ); + blockProcessor.compute( buf, dest ); } @Override public UnaryBlockOperator< S, T > independentCopy() { - return new DefaultUnaryBlockOperator<>( sourceType, targetType, numSourceDimensions, numTargetDimensions, blockProcessor.independentCopy() ); + return new DefaultUnaryBlockOperator<>( this ); } - private Supplier< UnaryBlockOperator< S, T > > threadSafeSupplier; - - @Override - public UnaryBlockOperator< S, T > threadSafe() + /** + * Get (an instance of) the wrapped {@code BlockProcessor}. + *

+ * Note that this involves an unchecked cast, that is, the returned {@code + * BlockProcessor} will be cast to the {@code I, O} types expected by + * the caller. + *

+ * This is mostly intended for debugging. + * + * @param + * input primitive array type, e.g., float[]. Must correspond to S. + * @param + * output primitive array type, e.g., float[]. Must correspond to T. + * + * @return an instance of the wrapped {@code BlockProcessor} + */ + public < I, O > BlockProcessor< I, O > blockProcessor() { - if ( threadSafeSupplier == null ) - threadSafeSupplier = CloseableThreadLocal.withInitial( this::independentCopy )::get; - return new UnaryBlockOperator< S, T >() - { - @Override - public < I, O > BlockProcessor< I, O > blockProcessor() - { - return threadSafeSupplier.get().blockProcessor(); - } - - @Override - public S getSourceType() - { - return sourceType; - } - - @Override - public T getTargetType() - { - return targetType; - } - - @Override - public int numSourceDimensions() - { - return numSourceDimensions; - } - - @Override - public int numTargetDimensions() - { - return numTargetDimensions; - } - - @Override - public UnaryBlockOperator< S, T > threadSafe() - { - return this; - } - - @Override - public UnaryBlockOperator< S, T > independentCopy() - { - return DefaultUnaryBlockOperator.this.independentCopy().threadSafe(); - } - }; + return Cast.unchecked( blockProcessor ); } - } diff --git a/src/main/java/net/imglib2/algorithm/blocks/PrimitiveBlocksSupplier.java b/src/main/java/net/imglib2/algorithm/blocks/PrimitiveBlocksSupplier.java index 09e259d12..368912012 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/PrimitiveBlocksSupplier.java +++ b/src/main/java/net/imglib2/algorithm/blocks/PrimitiveBlocksSupplier.java @@ -33,6 +33,7 @@ */ package net.imglib2.algorithm.blocks; +import net.imglib2.Interval; import net.imglib2.blocks.PrimitiveBlocks; import net.imglib2.type.NativeType; @@ -58,15 +59,9 @@ public int numDimensions() } @Override - public void copy( final long[] srcPos, final Object dest, final int[] size ) + public void copy( final Interval interval, final Object dest ) { - blocks.copy( srcPos, dest, size ); - } - - @Override - public void copy( final int[] srcPos, final Object dest, final int[] size ) - { - blocks.copy( srcPos, dest, size ); + blocks.copy( interval, dest ); } @Override diff --git a/src/main/java/net/imglib2/algorithm/blocks/TilingBlockSupplier.java b/src/main/java/net/imglib2/algorithm/blocks/TilingUnaryBlockOperator.java similarity index 60% rename from src/main/java/net/imglib2/algorithm/blocks/TilingBlockSupplier.java rename to src/main/java/net/imglib2/algorithm/blocks/TilingUnaryBlockOperator.java index c24714290..b54ddf942 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/TilingBlockSupplier.java +++ b/src/main/java/net/imglib2/algorithm/blocks/TilingUnaryBlockOperator.java @@ -11,13 +11,13 @@ * %% * 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 @@ -33,6 +33,8 @@ */ package net.imglib2.algorithm.blocks; +import net.imglib2.Interval; +import net.imglib2.blocks.BlockInterval; import net.imglib2.blocks.SubArrayCopy; import net.imglib2.blocks.TempArray; import net.imglib2.type.NativeType; @@ -42,17 +44,17 @@ import net.imglib2.util.Util; /** - * {@code TilingBlockSupplier} wraps a source {@code BlockSupplier}, and splits - * large {@link BlockSupplier#copy} requests into several smaller {@code copy} - * calls on the source {@code BlockSupplier}. + * {@code TilingUnaryBlockOperator} fulfills large {@link #compute} requests + * by requesting several smaller blocks from the source {@code BlockSupplier} + * and assembling them into the requested large block. *

* Each {@code copy} on the source {@code BlockSupplier} requests (at most) an * interval of the {@code tileSize} specified in the constructor. The source * {@code BlockSupplier.copy} writes to a temporary buffer, which is then copied * into the appropriate portion of the {@code dest} buffer. *

- * {@link BlockSupplier#copy} requests that are smaller or equal to {@code - * tileSize} are passed directly to the source {@code BlockSupplier}. + * {@link #compute} requests that are smaller or equal to {@code tileSize} are + * passed directly to the source {@code BlockSupplier}. *

* Example use cases: *

    @@ -66,11 +68,11 @@ * pixel type * @param

    * corresponding primitive array type + * + * @see BlockSupplier#tile */ -class TilingBlockSupplier< T extends NativeType< T >, P > extends AbstractBlockSupplier< T > +class TilingUnaryBlockOperator< T extends NativeType< T >, P > extends AbstractUnaryBlockOperator< T, T > { - private final BlockSupplier< T > p0; - private final int[] innerTileSize; private final int[] borderTileSize; private final int[] numTiles; @@ -80,80 +82,76 @@ class TilingBlockSupplier< T extends NativeType< T >, P > extends AbstractBlockS private final SubArrayCopy.Typed< P, P > subArrayCopy; - final int[] tile_pos_in_dest; - final long[] tile_pos_in_src; - final int[] tile_origin; - final int[] tile_size; + private final int[] tile_pos_in_dest; + private final long[] tile_pos_in_src; + private final int[] tile_size; + private final BlockInterval tile_interval_in_src; + private final int[] zero; /** - * Create a {@code BlockSupplier} that handles {@link BlockSupplier#copy} + * Create a {@code UnaryBlockOperator} that handles {@link #compute} * requests by splitting into {@code tileSize} portions that are each - * handled by the given {@code srcSupplier} and assembled into the final + * handled by the source {@code BlockSupplier} and assembled into the final * result. * - * @param srcSupplier - * source {@code BlockSupplier} to wrap. + * @param type + * pixel type (source and target) of this operator + * @param numDimensions + * number of dimensions (source and target) of this operator * @param tileSize - * (maximum) dimensions of a request to the {@code srcSupplier}. + * (maximum) dimensions of a request to the source {@code BlockSupplier}. * {@code tileSize} is expanded or truncated to the necessary size. * For example, if {@code tileSize=={64}} when wrapping a 3D {@code * srcSupplier}, {@code tileSize} is expanded to {@code {64, 64, * 64}}. */ - public TilingBlockSupplier( - final BlockSupplier< T > srcSupplier, - final int... tileSize ) + public TilingUnaryBlockOperator( final T type, final int numDimensions, final int... tileSize ) { - this.p0 = srcSupplier; - final int n = srcSupplier.numDimensions(); - innerTileSize = Util.expandArray( tileSize, n ); + super( type, type, numDimensions, numDimensions ); + + innerTileSize = Util.expandArray( tileSize, numDimensions ); innerTileNumElements = Util.safeInt( Intervals.numElements( innerTileSize ) ); - borderTileSize = new int[ n ]; - numTiles = new int[ n ]; + borderTileSize = new int[ numDimensions ]; + numTiles = new int[ numDimensions ]; - final PrimitiveType primitiveType = srcSupplier.getType().getNativeTypeFactory().getPrimitiveType(); + final PrimitiveType primitiveType = type.getNativeTypeFactory().getPrimitiveType(); tempArray = TempArray.forPrimitiveType( primitiveType ); subArrayCopy = SubArrayCopy.forPrimitiveType( primitiveType ); - tile_pos_in_dest = new int[ n ]; - tile_pos_in_src = new long[ n ]; - tile_origin = new int[ n ]; - tile_size = new int[ n ]; + tile_pos_in_dest = new int[ numDimensions ]; + tile_pos_in_src = new long[ numDimensions ]; + tile_size = new int[ numDimensions ]; + tile_interval_in_src = BlockInterval.wrap( tile_pos_in_src, tile_size ); + zero = new int[ numDimensions ]; } - private TilingBlockSupplier( final TilingBlockSupplier< T, P > s ) + private TilingUnaryBlockOperator( TilingUnaryBlockOperator< T, P > op ) { - p0 = s.p0.independentCopy(); - innerTileSize = s.innerTileSize; - innerTileNumElements = s.innerTileNumElements; - tempArray = s.tempArray.newInstance(); - subArrayCopy = s.subArrayCopy; + super( op ); + + innerTileSize = op.innerTileSize; + innerTileNumElements = op.innerTileNumElements; + tempArray = op.tempArray.newInstance(); + subArrayCopy = op.subArrayCopy; - final int n = numDimensions(); + final int n = op.numTargetDimensions(); borderTileSize = new int[ n ]; numTiles = new int[ n ]; tile_pos_in_dest = new int[ n ]; tile_pos_in_src = new long[ n ]; - tile_origin = new int[ n ]; tile_size = new int[ n ]; + tile_interval_in_src = BlockInterval.wrap( tile_pos_in_src, tile_size ); + zero = op.zero; } @Override - public T getType() + public void compute( final BlockSupplier< T > src, final Interval interval, final Object dest ) { - return p0.getType(); - } + final BlockInterval blockInterval = BlockInterval.asBlockInterval( interval ); + final long[] srcPos = blockInterval.min(); + final int[] size = blockInterval.size(); - @Override - public int numDimensions() - { - return p0.numDimensions(); - } - - @Override - public void copy( final long[] srcPos, final Object dest, final int[] size ) - { - final int n = numDimensions(); + final int n = numTargetDimensions(); boolean singleTile = true; for ( int d = 0; d < n; ++d ) { @@ -164,16 +162,16 @@ public void copy( final long[] srcPos, final Object dest, final int[] size ) } if ( singleTile ) { - p0.copy( srcPos, dest, size ); + src.copy( blockInterval, dest ); } else { final P tile_buf = tempArray.get( innerTileNumElements ); - compute_tiles_recursively( n - 1, srcPos, Cast.unchecked( dest ), size, tile_buf ); + compute_tiles_recursively( n - 1, src, srcPos, Cast.unchecked( dest ), size, tile_buf ); } } - private void compute_tiles_recursively( final int d, final long[] srcPos, final P dest, final int[] dest_size, final P tile_buf ) { + private void compute_tiles_recursively( final int d, final BlockSupplier< T > src, final long[] srcPos, final P dest, final int[] dest_size, final P tile_buf ) { final int numTiles = this.numTiles[ d ]; for ( int i = 0; i < numTiles; ++i ) { @@ -182,19 +180,19 @@ private void compute_tiles_recursively( final int d, final long[] srcPos, final tile_size[ d ] = ( i == numTiles - 1 ) ? borderTileSize[ d ] : innerTileSize[ d ]; if ( d == 0 ) { - p0.copy( tile_pos_in_src, tile_buf, tile_size ); - subArrayCopy.copy( tile_buf, tile_size, tile_origin, dest, dest_size, tile_pos_in_dest, tile_size ); + src.copy( tile_interval_in_src, tile_buf ); + subArrayCopy.copy( tile_buf, tile_size, zero, dest, dest_size, tile_pos_in_dest, tile_size ); } else { - compute_tiles_recursively( d - 1, srcPos, dest, dest_size, tile_buf ); + compute_tiles_recursively( d - 1, src, srcPos, dest, dest_size, tile_buf ); } } } @Override - public BlockSupplier< T > independentCopy() + public UnaryBlockOperator< T, T > independentCopy() { - return new TilingBlockSupplier<>( this ); + return new TilingUnaryBlockOperator<>( this ); } } diff --git a/src/main/java/net/imglib2/algorithm/blocks/UnaryBlockOperator.java b/src/main/java/net/imglib2/algorithm/blocks/UnaryBlockOperator.java index 0c2f31b91..dfc368e23 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/UnaryBlockOperator.java +++ b/src/main/java/net/imglib2/algorithm/blocks/UnaryBlockOperator.java @@ -33,43 +33,47 @@ */ package net.imglib2.algorithm.blocks; +import net.imglib2.Interval; import net.imglib2.algorithm.blocks.convert.Convert; import net.imglib2.type.NativeType; -import net.imglib2.util.Cast; /** - * Wraps {@code BlockProcessor}, where {@code I} is the primitive array - * type backing ImgLib2 {@code NativeType} {@code S} and {@code O} is the - * primitive array type backing ImgLib2 {@code NativeType} {@code T}. + * A {@code UnaryBlockOperator} computes blocks of {@code NativeType} data + * from blocks of {@code NativeType} data. *

    - * Typically, {@code UnaryBlockOperator} should be used rather than {@link - * BlockProcessor} directly, to avoid mistakes with unchecked (primitive array) - * type casts. + * Use the {@link UnaryBlockOperator#compute(BlockSupplier, Interval, Object)} + * method to compute a block of data and store it into a flat primitive array + * (of the appropriate primitive type corresponding to {@code T}). The input + * block (appropriately sized and positioned to produce the desired output + * {@code Interval}) is read from a {@code BlockSupplier}. + *

    + * Typically, {@code UnaryBlockOperator} are chained to a particular {@code + * BlockSupplier} using {@link BlockSupplier#andThen} (or {@link #applyTo}, + * instead of calling {@code compute()} explicitly. + *

    + * Implementations are not thread-safe in general. Use {@link + * #independentCopy()} to obtain independent instances. * * @param - * source type + * source pixel type * @param - * target type + * target pixel type */ public interface UnaryBlockOperator< S extends NativeType< S >, T extends NativeType< T > > { /** - * Get (an instance of) the wrapped {@code BlockProcessor}. - *

    - * Note that this involves an unchecked cast, that is, the returned {@code - * BlockProcessor} will be cast to the {@code I, O} types expected by - * the caller. - *

    - * This is mostly intented for internal use, e.g., in {@link BlockAlgoUtils}. + * Compute a block (using {@code src} to provide input data) and store into the given primitive array (of the appropriate type). * - * @param - * input primitive array type, e.g., float[]. Must correspond to S. - * @param - * output primitive array type, e.g., float[]. Must correspond to T. - * - * @return an instance of the wrapped {@code BlockProcessor} + * @param src + * the {@code BlockSupplier} that provides input data + * @param interval + * the output interval to compute + * @param dest + * primitive array to store computation result. Must correspond to {@code T}, for + * example, if {@code T} is {@code UnsignedByteType} then {@code dest} must + * be {@code byte[]}. */ - < I, O > BlockProcessor< I, O > blockProcessor(); + void compute( BlockSupplier< S > src, Interval interval, Object dest ); S getSourceType(); @@ -101,12 +105,6 @@ public interface UnaryBlockOperator< S extends NativeType< S >, T extends Native */ int numTargetDimensions(); - /** - * Get a thread-safe version of this {@code UnaryBlockOperator}. - * (Implemented as a wrapper that makes {@link ThreadLocal} copies). - */ - UnaryBlockOperator< S, T > threadSafe(); - /** * Returns an instance of this {@link UnaryBlockOperator} that can be used * independently, e.g., in another thread or in another place in the same @@ -114,28 +112,22 @@ public interface UnaryBlockOperator< S extends NativeType< S >, T extends Native */ UnaryBlockOperator< S, T > independentCopy(); + /** + * Returns a {@code BlockSupplier} that provide blocks by {@link #compute + * computing} on input blocks from the given {@code BlockSupplier}. + */ + default BlockSupplier< T > applyTo( BlockSupplier< S > blocks ) + { + return new ConcatenatedBlockSupplier<>( blocks, independentCopy() ); + } + /** * Returns a {@code UnaryBlockOperator} that is equivalent to applying * {@code this}, and then applying {@code op} to the result. */ default < U extends NativeType< U > > UnaryBlockOperator< S, U > andThen( UnaryBlockOperator< T, U > op ) { - if ( op instanceof NoOpUnaryBlockOperator ) - return Cast.unchecked( this ); - - final boolean thisHasDimensions = numSourceDimensions() > 0; - final boolean opHasDimensions = op.numSourceDimensions() > 0; - if ( opHasDimensions && thisHasDimensions && numTargetDimensions() != op.numSourceDimensions() ) { - throw new IllegalArgumentException( "UnaryBlockOperator cannot be concatenated: number of dimensions mismatch." ); - } - final int numSourceDimensions = thisHasDimensions ? numSourceDimensions() : op.numSourceDimensions(); - final int numTargetDimensions = opHasDimensions ? op.numTargetDimensions() : numTargetDimensions(); - return new DefaultUnaryBlockOperator<>( - getSourceType(), - op.getTargetType(), - numSourceDimensions, - numTargetDimensions, - blockProcessor().andThen( op.blockProcessor() ) ); + return new ConcatenatedUnaryBlockOperator<>( this, op.independentCopy() ); } /** @@ -145,10 +137,7 @@ default < U extends NativeType< U > > UnaryBlockOperator< S, U > andThen( UnaryB */ default < U extends NativeType< U > > UnaryBlockOperator< U, T > adaptSourceType( U newSourceType, ClampType clamp ) { - if ( newSourceType.getClass().isInstance( getSourceType() ) ) - return Cast.unchecked( this ); - else - return Convert.createOperator( newSourceType, getSourceType(), clamp ).andThen( this ); + return Convert.createOperator( newSourceType, getSourceType(), clamp ).andThen( this ); } /** @@ -158,9 +147,6 @@ default < U extends NativeType< U > > UnaryBlockOperator< U, T > adaptSourceType */ default < U extends NativeType< U > > UnaryBlockOperator< S, U > adaptTargetType( U newTargetType, ClampType clamp ) { - if ( newTargetType.getClass().isInstance( getTargetType() ) ) - return Cast.unchecked( this ); - else - return this.andThen( Convert.createOperator( getTargetType(), newTargetType, clamp ) ); + return this.andThen( Convert.createOperator( getTargetType(), newTargetType, clamp ) ); } } diff --git a/src/main/java/net/imglib2/algorithm/blocks/convert/Convert.java b/src/main/java/net/imglib2/algorithm/blocks/convert/Convert.java index efff1ad31..922d0032e 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/convert/Convert.java +++ b/src/main/java/net/imglib2/algorithm/blocks/convert/Convert.java @@ -37,13 +37,15 @@ import java.util.function.Function; import java.util.function.Supplier; +import net.imglib2.Interval; +import net.imglib2.algorithm.blocks.AbstractUnaryBlockOperator; import net.imglib2.algorithm.blocks.BlockSupplier; import net.imglib2.algorithm.blocks.ClampType; import net.imglib2.algorithm.blocks.DefaultUnaryBlockOperator; -import net.imglib2.algorithm.blocks.NoOpUnaryBlockOperator; import net.imglib2.algorithm.blocks.UnaryBlockOperator; import net.imglib2.converter.Converter; import net.imglib2.type.NativeType; +import net.imglib2.util.Cast; /** * Create {@link UnaryBlockOperator} to convert blocks between standard ImgLib2 {@code Type}s. @@ -170,13 +172,51 @@ UnaryBlockOperator< S, T > createOperator( final S sourceType, final T targetTyp UnaryBlockOperator< S, T > createOperator( final S sourceType, final T targetType, final ClampType clamp ) { if ( Objects.equals( sourceType.getClass(), targetType.getClass() ) ) - return new NoOpUnaryBlockOperator<>(); + return Cast.unchecked( new Identity<>( sourceType, 0 ) ); return new DefaultUnaryBlockOperator<>( sourceType, targetType, 0, 0, new ConvertBlockProcessor<>( sourceType, targetType, clamp ) ); } + // TODO: move to upper level, make public? + static class Identity< T extends NativeType< T > > extends AbstractUnaryBlockOperator< T, T > + { + protected Identity( final T type, final int numDimensions ) + { + super( type, type, numDimensions, numDimensions ); + } + + protected Identity( final AbstractUnaryBlockOperator< T, T > op ) + { + super( op ); + } + + @Override + public void compute( final BlockSupplier< T > src, final Interval interval, final Object dest ) + { + src.copy( interval, dest ); + } + + @Override + public UnaryBlockOperator< T, T > independentCopy() + { + return this; + } + + @Override + public BlockSupplier< T > applyTo( final BlockSupplier< T > blocks ) + { + return blocks; + } + + @Override + public < U extends NativeType< U > > UnaryBlockOperator< T, U > andThen( final UnaryBlockOperator< T, U > op ) + { + return op; + } + } + /** * Create {@link UnaryBlockOperator} to convert blocks between {@code * sourceType} and {@code targetType} with the specified {@code Converter}. diff --git a/src/main/java/net/imglib2/algorithm/blocks/transform/AbstractTransformProcessor.java b/src/main/java/net/imglib2/algorithm/blocks/transform/AbstractTransformProcessor.java index a67ce5bd7..bda6bb1a5 100644 --- a/src/main/java/net/imglib2/algorithm/blocks/transform/AbstractTransformProcessor.java +++ b/src/main/java/net/imglib2/algorithm/blocks/transform/AbstractTransformProcessor.java @@ -33,19 +33,13 @@ */ package net.imglib2.algorithm.blocks.transform; -import static net.imglib2.util.Util.safeInt; - import java.util.Arrays; -import java.util.function.Supplier; import net.imglib2.Interval; import net.imglib2.RealInterval; import net.imglib2.algorithm.blocks.AbstractBlockProcessor; -import net.imglib2.algorithm.blocks.BlockProcessor; -import net.imglib2.algorithm.blocks.util.BlockProcessorSourceInterval; -import net.imglib2.blocks.TempArray; +import net.imglib2.blocks.BlockInterval; import net.imglib2.type.PrimitiveType; -import net.imglib2.util.Intervals; /** * Abstract base class for {@link Affine3DProcessor} and {@link @@ -96,9 +90,7 @@ abstract class AbstractTransformProcessor< P > extends AbstractBlockProcessor< P @Override public void setTargetInterval( final Interval interval ) { - interval.min( destPos ); - Arrays.setAll( destSize, d -> safeInt( interval.dimension( d ) ) ); - + BlockInterval.wrap( destPos, destSize ).setFrom( interval ); final RealInterval bounds = estimateBounds( interval ); switch ( interpolation ) { diff --git a/src/main/java/net/imglib2/algorithm/blocks/util/BlockProcessorSourceInterval.java b/src/main/java/net/imglib2/algorithm/blocks/util/BlockProcessorSourceInterval.java deleted file mode 100644 index 813c2ccec..000000000 --- a/src/main/java/net/imglib2/algorithm/blocks/util/BlockProcessorSourceInterval.java +++ /dev/null @@ -1,75 +0,0 @@ -/*- - * #%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.algorithm.blocks.util; - -import net.imglib2.Interval; -import net.imglib2.algorithm.blocks.BlockProcessor; - -/** - * Helper class that wraps {@link BlockProcessor#getSourcePos()} and {@link - * BlockProcessor#getSourceSize()} as an {@code Interval}. - */ -public class BlockProcessorSourceInterval implements Interval -{ - private BlockProcessor< ?, ? > p; - - public BlockProcessorSourceInterval( BlockProcessor< ?, ? > blockProcessor ) - { - this.p = blockProcessor; - } - - @Override - public int numDimensions() - { - return p.getSourcePos().length; - } - - @Override - public long min( final int d ) - { - return p.getSourcePos()[ d ]; - } - - @Override - public long max( final int d ) - { - return min( d ) + dimension( d ) - 1; - } - - @Override - public long dimension( final int d ) - { - return p.getSourceSize()[ d ]; - } -} diff --git a/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull.java b/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull.java index ccf1b92b5..51c20072b 100644 --- a/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull.java +++ b/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull.java @@ -123,7 +123,7 @@ public void setUp() { centerFloat = new CenterFloat( downsampleInDim ); centerFloat.setTargetInterval( new FinalInterval( Util.int2long( outputSize ) ) ); - System.arraycopy( centerFloat.getSourceSize(), 0, inputSize, 0, inputSize.length ); + System.arraycopy( centerFloat.getSourceInterval().size(), 0, inputSize, 0, inputSize.length ); inputF = new float[ ( int ) Intervals.numElements( inputSize ) ]; for ( int i = 0; i < inputF.length; i++ ) inputF[ i ] = random.nextFloat(); diff --git a/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull2.java b/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull2.java index 26490c834..b5e9681bd 100644 --- a/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull2.java +++ b/src/test/java/net/imglib2/algorithm/blocks/downsample/DownsampleBenchmarkFull2.java @@ -124,7 +124,7 @@ public void setUp() { halfPixelFloat = new HalfPixelFloat( downsampleInDim ); halfPixelFloat.setTargetInterval( new FinalInterval( Util.int2long( outputSize ) ) ); - System.arraycopy( halfPixelFloat.getSourceSize(), 0, inputSize, 0, inputSize.length ); + System.arraycopy( halfPixelFloat.getSourceInterval().size(), 0, inputSize, 0, inputSize.length ); inputF = new float[ ( int ) Intervals.numElements( inputSize ) ]; for ( int i = 0; i < inputF.length; i++ ) inputF[ i ] = random.nextFloat(); diff --git a/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3D.java b/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3D.java index ae0ab159b..60da49cb3 100644 --- a/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3D.java +++ b/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3D.java @@ -39,12 +39,14 @@ import java.util.concurrent.TimeUnit; import net.imglib2.Cursor; import net.imglib2.FinalInterval; +import net.imglib2.Interval; import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; import net.imglib2.RealRandomAccessible; import net.imglib2.algorithm.blocks.BlockProcessor; import net.imglib2.algorithm.blocks.ClampType; import net.imglib2.algorithm.blocks.ComputationType; +import net.imglib2.algorithm.blocks.DefaultUnaryBlockOperator; import net.imglib2.algorithm.blocks.transform.Transform.Interpolation; import net.imglib2.blocks.PrimitiveBlocks; import net.imglib2.converter.Converters; @@ -154,17 +156,23 @@ public void blocksnaiveSetup() new RealFloatConverter<>(), new FloatType() ) ); final FloatType type2 = new FloatType(); - processor = Transform.createAffineOperator( type2, affine, Interpolation.NLINEAR, ComputationType.AUTO, ClampType.CLAMP ).blockProcessor(); + processor = ( ( DefaultUnaryBlockOperator< ?, ? > ) + Transform.createAffineOperator( type2, affine, Interpolation.NLINEAR, ComputationType.AUTO, ClampType.CLAMP ) + ).blockProcessor(); blocksDouble = PrimitiveBlocks.of( Converters.convert( Views.extendZero( img ), new RealDoubleConverter<>(), new DoubleType() ) ); final DoubleType type1 = new DoubleType(); - processorDouble = Transform.createAffineOperator( type1, affine, Interpolation.NLINEAR, ComputationType.AUTO, ClampType.CLAMP ).blockProcessor(); + processorDouble = ( ( DefaultUnaryBlockOperator< ?, ? > ) + Transform.createAffineOperator( type1, affine, Interpolation.NLINEAR, ComputationType.AUTO, ClampType.CLAMP ) + ).blockProcessor(); blocksUnsignedByte = PrimitiveBlocks.of( Views.extendZero( img ) ); final UnsignedByteType type = new UnsignedByteType(); - processorUnsignedByte = Transform.createAffineOperator( type, affine, Interpolation.NLINEAR, ComputationType.AUTO, ClampType.CLAMP ).blockProcessor(); + processorUnsignedByte = ( ( DefaultUnaryBlockOperator< ?, ? > ) + Transform.createAffineOperator( type, affine, Interpolation.NLINEAR, ComputationType.AUTO, ClampType.CLAMP ) + ).blockProcessor(); blocksFloat(); blocksDouble(); blocksUnsignedByte(); @@ -179,9 +187,10 @@ public Object blocksFloat() long[] max = new long[ size.length ]; Arrays.setAll( max, d -> min[ d ] + size[ d ] - 1 ); processor.setTargetInterval( FinalInterval.wrap( min, max ) ); - blocks.copy( processor.getSourcePos(), processor.getSourceBuffer(), processor.getSourceSize() ); + final float[] buf = processor.getSourceBuffer(); + blocks.copy( processor.getSourceInterval(), buf ); final float[] dest = new float[ ( int ) Intervals.numElements( size ) ]; - processor.compute( processor.getSourceBuffer(), dest ); + processor.compute( buf, dest ); final RandomAccessibleInterval< FloatType > destImg = ArrayImgs.floats( dest, size[ 0 ], size[ 1 ], size[ 2 ] ); return destImg; } @@ -192,9 +201,10 @@ public Object blocksDouble() long[] max = new long[ size.length ]; Arrays.setAll( max, d -> min[ d ] + size[ d ] - 1 ); processorDouble.setTargetInterval( FinalInterval.wrap( min, max ) ); - blocksDouble.copy( processorDouble.getSourcePos(), processorDouble.getSourceBuffer(), processorDouble.getSourceSize() ); + final double[] buf = processorDouble.getSourceBuffer(); + blocksDouble.copy( processorDouble.getSourceInterval(), buf ); final double[] dest = new double[ ( int ) Intervals.numElements( size ) ]; - processorDouble.compute( processorDouble.getSourceBuffer(), dest ); + processorDouble.compute( buf, dest ); final RandomAccessibleInterval< DoubleType > destImg = ArrayImgs.doubles( dest, size[ 0 ], size[ 1 ], size[ 2 ] ); return destImg; } @@ -205,7 +215,8 @@ public Object blocksUnsignedByte() long[] max = new long[ size.length ]; Arrays.setAll( max, d -> min[ d ] + size[ d ] - 1 ); processorUnsignedByte.setTargetInterval( FinalInterval.wrap( min, max ) ); - blocksUnsignedByte.copy( processorUnsignedByte.getSourcePos(), processorUnsignedByte.getSourceBuffer(), processorUnsignedByte.getSourceSize() ); + final Interval buf = processorUnsignedByte.getSourceInterval(); + blocksUnsignedByte.copy( buf, processorUnsignedByte.getSourceBuffer() ); final byte[] dest = new byte[ ( int ) Intervals.numElements( size ) ]; processorUnsignedByte.compute( processorUnsignedByte.getSourceBuffer(), dest ); final RandomAccessibleInterval< UnsignedByteType > destImg = ArrayImgs.unsignedBytes( dest, size[ 0 ], size[ 1 ], size[ 2 ] ); diff --git a/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3DonlyCompute.java b/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3DonlyCompute.java index 95d6525e6..987e14778 100644 --- a/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3DonlyCompute.java +++ b/src/test/java/net/imglib2/algorithm/blocks/transform/TransformBenchmark3DonlyCompute.java @@ -39,6 +39,7 @@ import java.util.concurrent.TimeUnit; import net.imglib2.FinalInterval; import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.blocks.BlockSupplier; import net.imglib2.algorithm.blocks.transform.Transform.Interpolation; import net.imglib2.blocks.PrimitiveBlocks; import net.imglib2.converter.Converters; @@ -110,13 +111,14 @@ public TransformBenchmark3DonlyCompute() blocksnaiveSetup(); } - PrimitiveBlocks< FloatType > blocks; + BlockSupplier< FloatType > blocks; Affine3DProcessor< float[] > processor; float[] dest; + float[] src; public void blocksnaiveSetup() { - blocks = PrimitiveBlocks.of( + blocks = BlockSupplier.of( Converters.convert( Views.extendZero( img ), new RealFloatConverter<>(), @@ -125,14 +127,15 @@ public void blocksnaiveSetup() long[] max = new long[ size.length ]; Arrays.setAll( max, d -> min[ d ] + size[ d ] - 1 ); processor.setTargetInterval( FinalInterval.wrap( min, max ) ); - blocks.copy( processor.getSourcePos(), processor.getSourceBuffer(), processor.getSourceSize() ); + src = processor.getSourceBuffer(); + blocks.copy( processor.getSourceInterval(), src ); dest = new float[ ( int ) Intervals.numElements( size ) ]; } @Benchmark public void compute() { - processor.compute( processor.getSourceBuffer(), dest ); + processor.compute( src, dest ); } public static void main( String[] args ) throws RunnerException diff --git a/src/test/java/net/imglib2/algorithm/blocks/transform/TransformPlayground3D.java b/src/test/java/net/imglib2/algorithm/blocks/transform/TransformPlayground3D.java index b85b9d534..e4b19c0ec 100644 --- a/src/test/java/net/imglib2/algorithm/blocks/transform/TransformPlayground3D.java +++ b/src/test/java/net/imglib2/algorithm/blocks/transform/TransformPlayground3D.java @@ -45,6 +45,7 @@ import net.imglib2.RealRandomAccessible; import net.imglib2.algorithm.blocks.BlockSupplier; import net.imglib2.algorithm.blocks.transform.Transform.Interpolation; +import net.imglib2.blocks.BlockInterval; import net.imglib2.img.Img; import net.imglib2.img.array.ArrayImg; import net.imglib2.img.array.ArrayImgFactory; @@ -107,7 +108,7 @@ public static void main( String[] args ) .of( Views.extendZero( img ) ) .andThen( Transform.affine( affine, Interpolation.NLINEAR ) ); final byte[] dest = new byte[ ( int ) Intervals.numElements( size ) ]; - blocks.copy( min, dest, size ); + blocks.copy( BlockInterval.wrap( min, size ), dest ); final RandomAccessibleInterval< UnsignedByteType > destImg = ArrayImgs.unsignedBytes( dest, size[ 0 ], size[ 1 ], size[ 2 ] ); // ----------------------------------------------