From d7f88e9d25be410ff1dec64290bd10f65b78600a Mon Sep 17 00:00:00 2001 From: tpietzsch Date: Tue, 7 Sep 2021 13:30:12 +0200 Subject: [PATCH 1/4] Improve CellGrid javadoc --- .../java/net/imglib2/img/cell/CellGrid.java | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/src/main/java/net/imglib2/img/cell/CellGrid.java b/src/main/java/net/imglib2/img/cell/CellGrid.java index a6a94fe7e..6a53fcb00 100644 --- a/src/main/java/net/imglib2/img/cell/CellGrid.java +++ b/src/main/java/net/imglib2/img/cell/CellGrid.java @@ -59,6 +59,13 @@ public class CellGrid private final int hashcode; + /** + * @param dimensions + * the dimensions of the image (in pixels, not in cells). + * @param cellDimensions + * the dimensions of a standard cell (in pixels). Cells on the max border + * of the image may be cut off and have different dimensions. + */ public CellGrid( final long[] dimensions, final int[] cellDimensions ) @@ -92,17 +99,27 @@ public int numDimensions() return n; } + /** + * Get the number of cells in each dimension as a new long[]. + */ public long[] getGridDimensions() { return numCells.clone(); } - public void gridDimensions( final long[] s ) + /** + * Write the number of cells in each dimension into the provided {@code + * dimensions} array. + */ + public void gridDimensions( final long[] dimensions ) { for ( int i = 0; i < n; ++i ) - s[ i ] = numCells[ i ]; + dimensions[ i ] = numCells[ i ]; } + /** + * Get the number of cells in dimension {@code d}. + */ public long gridDimension( final int d ) { return numCells[ d ]; @@ -118,10 +135,9 @@ public long[] getImgDimensions() } /** - * Write the number of pixels in each dimension into long[]. Note, that this - * is the number of pixels in all cells combined, not the number of cells! - * - * @param dimensions + * Write the number of pixels in each dimension into the provided {@code + * dimensions} array. Note, that this is the number of pixels in all cells + * combined, not the number of cells! */ public void imgDimensions( final long[] dimensions ) { @@ -130,10 +146,8 @@ public void imgDimensions( final long[] dimensions ) } /** - * Get the number of pixels in a given dimension d. Note, that this - * is the number of pixels in all cells combined, not the number of cells! - * - * @param d + * Get the number of pixels in dimension {@code d}. Note, that this is the number + * of pixels in all cells combined, not the number of cells! */ public long imgDimension( final int d ) { @@ -141,11 +155,18 @@ public long imgDimension( final int d ) } /** - * Write the number of pixels in a standard cell in each dimension into - * long[]. Cells on the max border of the image may be cut off and have - * different dimensions. - * - * @param dimensions + * Get the number of pixels in a standard cell in each dimension as a new int[]. + * Cells on the borders of the image may be cut off and have different dimensions. + */ + public int[] getCellDimensions() + { + return cellDimensions.clone(); + } + + /** + * Write the number of pixels in a standard cell in each dimension into the + * provided {@code dimensions} array. Cells on the max border of the image may be + * cut off and have different dimensions. */ public void cellDimensions( final int[] dimensions ) { @@ -154,11 +175,8 @@ public void cellDimensions( final int[] dimensions ) } /** - * Get the number of pixels in a standard cell in a given dimension - * d. Cells on the max border of the image may be cut off and have - * different dimensions. - * - * @param d + * Get the number of pixels in a standard cell in dimension {@code d}. Cells on the + * max border of the image may be cut off and have different dimensions. */ public int cellDimension( final int d ) { From 093297ea3b6c7ad4dd9dd63c2b530544d88cf8c4 Mon Sep 17 00:00:00 2001 From: tpietzsch Date: Tue, 7 Sep 2021 13:50:03 +0200 Subject: [PATCH 2/4] CellGrid provides Cell intervals as RandomAccessibleInterval --- .../java/net/imglib2/img/cell/CellGrid.java | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/main/java/net/imglib2/img/cell/CellGrid.java b/src/main/java/net/imglib2/img/cell/CellGrid.java index 6a53fcb00..dfd60e71d 100644 --- a/src/main/java/net/imglib2/img/cell/CellGrid.java +++ b/src/main/java/net/imglib2/img/cell/CellGrid.java @@ -35,9 +35,20 @@ import java.util.Arrays; +import java.util.Iterator; +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.Point; import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; import net.imglib2.util.IntervalIndexer; +import net.imglib2.util.Intervals; import net.imglib2.util.Util; +import net.imglib2.view.RandomAccessibleIntervalCursor; /** * Defines {@link AbstractCellImg} geometry and translates between image, cell, @@ -82,6 +93,8 @@ public CellGrid( borderSize[ d ] = ( int ) ( dimensions[ d ] - ( numCells[ d ] - 1 ) * cellDimensions[ d ] ); } hashcode = 31 * Arrays.hashCode( dimensions ) + Arrays.hashCode( cellDimensions ); + + cellIntervals = new CellIntervals(); } public CellGrid( final CellGrid grid ) @@ -92,6 +105,7 @@ public CellGrid( final CellGrid grid ) numCells = grid.numCells.clone(); borderSize = grid.borderSize.clone(); hashcode = grid.hashcode; + cellIntervals = new CellIntervals(); } public int numDimensions() @@ -237,6 +251,17 @@ public void getCellDimensions( final long[] cellGridPosition, final long[] cellM } } + public void getCellInterval( final long[] cellGridPosition, final long[] cellMin, final long[] cellMax ) + { + for ( int d = 0; d < n; ++d ) + { + final long gridPos = cellGridPosition[ d ]; + final int cellDim = ( gridPos + 1 == numCells[ d ] ) ? borderSize[ d ] : cellDimensions[ d ]; + cellMin[ d ] = gridPos * cellDimensions[ d ]; + cellMax[ d ] = cellMin[ d ] + cellDim - 1; + } + } + /** * From the position of a cell in the grid, compute the size of the cell in * dimension {@code d}. The size will be the standard @@ -337,4 +362,120 @@ public String toString() + "( dims = " + Util.printCoordinates( dimensions ) + ", cellDims = " + Util.printCoordinates( cellDimensions ) + " )"; } + + private class CellIntervalsRA extends Point implements RandomAccess< Interval > + { + private final long[] min = new long[ CellGrid.this.n ]; + + private final long[] max = new long[ CellGrid.this.n ]; + + private final Interval interval = FinalInterval.wrap( min, max ); + + @Override + public Interval get() + { + getCellInterval( position, min, max ); + return interval; + } + + CellIntervalsRA() + { + super( CellGrid.this.n ); + } + + CellIntervalsRA( CellIntervalsRA ra ) + { + super( ra ); + } + + @Override + public RandomAccess< Interval > copyRandomAccess() + { + return copy(); + } + + @Override + public RandomAccess< Interval > copy() + { + return new CellIntervalsRA( this ); + } + } + + public class CellIntervals implements RandomAccessibleInterval< Interval >, IterableInterval< Interval > + { + private final long size = Intervals.numElements( numCells ); + + @Override + public int numDimensions() + { + return n; + } + + @Override + public long min( final int d ) + { + return 0; + } + + @Override + public long max( final int d ) + { + return numCells[ d ] - 1; + } + + @Override + public RandomAccess< Interval > randomAccess() + { + return new CellIntervalsRA(); + } + + @Override + public RandomAccess< Interval > randomAccess( final Interval interval ) + { + return randomAccess(); + } + + @Override + public Cursor< Interval > cursor() + { + return new RandomAccessibleIntervalCursor<>( this ); + } + + @Override + public Cursor< Interval > localizingCursor() + { + return cursor(); + } + + @Override + public long size() + { + return size; + } + + @Override + public Interval firstElement() + { + return cursor().next(); + } + + @Override + public FlatIterationOrder iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Interval > iterator() + { + return cursor(); + } + } + + private final CellIntervals cellIntervals; + + public CellIntervals cellIntervals() + { + return cellIntervals; + } } From bf2f30927905bdf07a6837162543f697a3e78878 Mon Sep 17 00:00:00 2001 From: tpietzsch Date: Tue, 7 Sep 2021 15:55:20 +0200 Subject: [PATCH 3/4] Add Grid utility class. This is similar to CellGrid but all coordinates are long[] and an additional offset can be specified, so that cells at the min border are truncated. --- src/main/java/net/imglib2/util/Grid.java | 578 +++++++++++++++++++++++ 1 file changed, 578 insertions(+) create mode 100644 src/main/java/net/imglib2/util/Grid.java diff --git a/src/main/java/net/imglib2/util/Grid.java b/src/main/java/net/imglib2/util/Grid.java new file mode 100644 index 000000000..9aefdb6d4 --- /dev/null +++ b/src/main/java/net/imglib2/util/Grid.java @@ -0,0 +1,578 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2021 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.util; + +import java.util.Arrays; +import java.util.Iterator; +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.Point; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.cell.CellGrid; +import net.imglib2.view.RandomAccessibleIntervalCursor; + +/** + * Defines a regular grid on an interval and translates between interval and grid + * coordinates. + *

+ * In contrast to {@link CellGrid}, all coordinates are in {@code long}, and + * gridlines can be shifted such that cells at the min border are truncated. + * + * @author Tobias Pietzsch + */ +public class Grid +{ + private final int n; + + private final long[] dimensions; + + private final long[] cellDimensions; + + private final long[] minBorderSize; + + private final long[] numCells; + + private final long[] maxBorderSize; + + private final int hashcode; + + /** + * @param dimensions + * the dimensions of the image (in pixels, not in cells). + * @param cellDimensions + * the dimensions of a standard cell (in pixels). Cells on the max border + * of the image may be cut off and have different dimensions. + */ + public Grid( + final long[] dimensions, + final long[] cellDimensions ) + { + this( dimensions, cellDimensions, new long[ dimensions.length ] ); + } + + /** + * @param dimensions + * the dimensions of the image (in pixels, not in cells). + * @param cellDimensions + * the dimensions of a standard cell (in pixels). Cells on the borders + * of the image may be cut off and have different dimensions. + * @param offset + * offset of the grid on the min border. Cell on the min border of the + * image will be cut off according to offset. For example, if {@code + * cellDimensions={10,10}} and {@code offset={5,0}} then the first cell + * will have dimensions {@code {5,10}} (summing {@code dimensions} is + * larger than {@code {10,10}}. + */ + public Grid( + final long[] dimensions, + final long[] cellDimensions, + final long[] offset ) + { + this.n = dimensions.length; + this.dimensions = dimensions.clone(); + this.cellDimensions = cellDimensions.clone(); + + numCells = new long[ n ]; + minBorderSize = new long[ n ]; + maxBorderSize = new long[ n ]; + for ( int d = 0; d < n; ++d ) + { + final long cd = cellDimensions[ d ]; + final long po = ( offset[ d ] % cd + cd ) % cd; // project offset into [0, cd). Also works for negative offsets. + minBorderSize[ d ] = Math.min( po == 0 ? cellDimensions[ d ] : po, dimensions[ d ] ); + final long d0 = dimensions[ d ] - minBorderSize[ d ]; + numCells[ d ] = d0 == 0 ? 1 : ( d0 - 1 ) / cd + 2; + maxBorderSize[ d ] = ( int ) ( d0 - ( numCells[ d ] - 2 ) * cd ); + } + + int hash = Arrays.hashCode( dimensions ); + hash = hash * 31 + Arrays.hashCode( cellDimensions ); + hash = hash * 31 + Arrays.hashCode( minBorderSize ); + hashcode = hash; + + cellIntervals = new CellIntervals(); + } + + public Grid( final Grid grid ) + { + n = grid.n; + dimensions = grid.dimensions.clone(); + cellDimensions = grid.cellDimensions.clone(); + numCells = grid.numCells.clone(); + minBorderSize = grid.minBorderSize.clone(); + maxBorderSize = grid.maxBorderSize.clone(); + hashcode = grid.hashcode; + cellIntervals = new CellIntervals(); + } + + public int numDimensions() + { + return n; + } + + /** + * Get the number of cells in each dimension as a new long[]. + */ + public long[] getGridDimensions() + { + return numCells.clone(); + } + + /** + * Write the number of cells in each dimension into the provided {@code + * dimensions} array. + */ + public void gridDimensions( final long[] dimensions ) + { + for ( int i = 0; i < n; ++i ) + dimensions[ i ] = numCells[ i ]; + } + + /** + * Get the number of cells in dimension {@code d}. + */ + public long gridDimension( final int d ) + { + return numCells[ d ]; + } + + /** + * Get the number of pixels in each dimension as a new long[]. Note, that this + * is the number of pixels in all cells combined, not the number of cells! + */ + public long[] getImgDimensions() + { + return dimensions.clone(); + } + + /** + * Write the number of pixels in each dimension into the provided {@code + * dimensions} array. Note, that this is the number of pixels in all cells + * combined, not the number of cells! + */ + public void imgDimensions( final long[] dimensions ) + { + for ( int i = 0; i < n; ++i ) + dimensions[ i ] = this.dimensions[ i ]; + } + + /** + * Get the number of pixels in dimension {@code d}. Note, that this is the number + * of pixels in all cells combined, not the number of cells! + */ + public long imgDimension( final int d ) + { + return dimensions[ d ]; + } + + /** + * Get the number of pixels in a standard cell in each dimension as a new long[]. + * Cells on the borders of the image may be cut off and have different dimensions. + */ + public long[] getCellDimensions() + { + return cellDimensions.clone(); + } + + /** + * Write the number of pixels in a standard cell in each dimension into the + * provided {@code dimensions} array. Cells on the max borders of the image may be + * cut off and have different dimensions. + */ + public void cellDimensions( final long[] dimensions ) + { + for ( int i = 0; i < n; ++i ) + dimensions[ i ] = cellDimensions[ i ]; + } + + /** + * Get the number of pixels in a standard cell in dimension {@code d}. Cells on the + * borders of the image may be cut off and have different dimensions. + */ + public long cellDimension( final int d ) + { + return cellDimensions[ d ]; + } + + /** + * From the index of a cell in the grid, compute the image position of the + * first pixel of the cell (the offset of the cell in image coordinates) and + * the dimensions of the cell. The dimensions will be the standard + * {@link #cellDimensions} unless the cell is at the border of the image in + * which case it might be truncated. + *

+ * Note, that this method assumes that the cell grid has flat iteration + * order. It this is not the case, use + * {@link #getCellDimensions(long[], long[], long[])}. + *

+ * + * @param index + * flattened grid coordinates of the cell. + * @param cellMin + * offset of the cell in image coordinates are written here. + * @param cellDims + * dimensions of the cell are written here. + */ + public void getCellDimensions( long index, final long[] cellMin, final long[] cellDims ) + { + for ( int d = 0; d < n; ++d ) + { + final long j = index / numCells[ d ]; + final long gridPos = index - j * numCells[ d ]; + index = j; + if ( gridPos == 0 ) + { + cellDims[ d ] = minBorderSize[ d ]; + cellMin[ d ] = 0; + } + else if ( gridPos == numCells[ d ] - 1 ) + { + cellDims[ d ] = maxBorderSize[ d ]; + cellMin[ d ] = minBorderSize[ d ] + ( gridPos - 1 ) * cellDimensions[ d ]; + } + else + { + cellDims[ d ] = cellDimensions[ d ]; + cellMin[ d ] = minBorderSize[ d ] + ( gridPos - 1 ) * cellDimensions[ d ]; + } + } + } + + /** + * From the position of a cell in the grid, compute the image position of + * the first pixel of the cell (the offset of the cell in image coordinates) + * and the dimensions of the cell. The dimensions will be the standard + * {@link #cellDimensions} unless the cell is at the border of the image in + * which case it might be truncated. + * + * @param cellGridPosition + * grid coordinates of the cell. + * @param cellMin + * offset of the cell in image coordinates are written here. + * @param cellDims + * dimensions of the cell are written here. + */ + public void getCellDimensions( final long[] cellGridPosition, final long[] cellMin, final long[] cellDims ) + { + for ( int d = 0; d < n; ++d ) + { + final long gridPos = cellGridPosition[ d ]; + if ( gridPos == 0 ) + { + cellDims[ d ] = minBorderSize[ d ]; + cellMin[ d ] = 0; + } + else if ( gridPos == numCells[ d ] - 1 ) + { + cellDims[ d ] = maxBorderSize[ d ]; + cellMin[ d ] = minBorderSize[ d ] + ( gridPos - 1 ) * cellDimensions[ d ]; + } + else + { + cellDims[ d ] = cellDimensions[ d ]; + cellMin[ d ] = minBorderSize[ d ] + ( gridPos - 1 ) * cellDimensions[ d ]; + } + } + } + + public void getCellInterval( final long[] cellGridPosition, final long[] cellMin, final long[] cellMax ) + { + for ( int d = 0; d < n; ++d ) + { + final long gridPos = cellGridPosition[ d ]; + if ( gridPos == 0 ) + { + cellMin[ d ] = 0; + cellMax[ d ] = minBorderSize[ d ] - 1; + } + else if ( gridPos == numCells[ d ] - 1 ) + { + cellMin[ d ] = minBorderSize[ d ] + ( gridPos - 1 ) * cellDimensions[ d ]; + cellMax[ d ] = cellMin[ d ] + maxBorderSize[ d ] - 1; + } + else + { + cellMin[ d ] = minBorderSize[ d ] + ( gridPos - 1 ) * cellDimensions[ d ]; + cellMax[ d ] = cellMin[ d ] + cellDimensions[ d ] - 1; + } + } + } + + /** + * From the position of a cell in the grid, compute the size of the cell in + * dimension {@code d}. The size will be the standard + * {@link #cellDimensions} unless the cell is at the border of the image in + * which case it might be truncated. + * + * @param d + * dimension index + * @param cellGridPosition + * grid coordinates of the cell in dimension {@code d}. + * + * @return size of the cell in dimension {@code d}. + */ + public long getCellDimension( final int d, final long cellGridPosition ) + { + if ( cellGridPosition == 0 ) + return minBorderSize[ d ]; + else if ( cellGridPosition == numCells[ d ] - 1 ) + return maxBorderSize[ d ]; + else + return cellDimensions[ d ]; + } + + /** + * From the position of a cell in the grid, compute the image position in + * dimension {@code d} of the first pixel of the cell (the offset of the + * cell in image coordinates). + * + * @param d + * dimension index + * @param cellGridPosition + * grid coordinates of the cell in dimension {@code d}. + * + * @return offset of the cell in dimension {@code d} (in image coordinates). + */ + public long getCellMin( final int d, final long cellGridPosition ) + { + if ( cellGridPosition == 0 ) + return 0; + else + return minBorderSize[ d ] + ( cellGridPosition - 1 ) * cellDimensions[ d ]; + } + + /** + * From the flattened index of a cell in the grid, compute the position of a + * cell in the grid. + * + * @param index + * flattened grid coordinates of the cell. + * @param cellGridPosition + * grid coordinates of the cell are written here. + */ + public void getCellGridPositionFlat( final long index, final long[] cellGridPosition ) + { + IntervalIndexer.indexToPosition( index, numCells, cellGridPosition ); + } + + /** + * Get the grid position of the cell containing the element at {@code position}. + * + * @param position + * position of an element in the image. + * @param cellPos + * is set to the grid position of the cell containing the element. + */ + public void getCellPosition( final long[] position, final long[] cellPos ) + { + for ( int d = 0; d < n; ++d ) + { + if ( position[ d ] < minBorderSize[ d ] ) + cellPos[ d ] = 0; + else + cellPos[ d ] = ( position[ d ] - minBorderSize[ d ] ) / cellDimensions[ d ]; + } + } + + /** + * Get the grid position of the cell containing the element at {@code position}. + * + * @param position + * position of an element in the image. + * @param cellPos + * is set to the grid position of the cell containing the element. + */ + public void getCellPosition( final long[] position, final Positionable cellPos ) + { + for ( int d = 0; d < n; ++d ) + { + if ( position[ d ] < minBorderSize[ d ] ) + cellPos.setPosition( 0, d ); + else + cellPos.setPosition( ( position[ d ] - minBorderSize[ d ] ) / cellDimensions[ d ], d ); + } + } + + @Override + public int hashCode() + { + return hashcode; + } + + @Override + public boolean equals( final Object obj ) + { + if ( obj instanceof Grid ) + { + final Grid other = ( Grid ) obj; + return Arrays.equals( dimensions, other.dimensions ) + && Arrays.equals( cellDimensions, other.cellDimensions ) + && Arrays.equals( minBorderSize, other.minBorderSize ); + } + return false; + } + + @Override + public String toString() + { + final long[] offset = new long[ n ]; + Arrays.setAll( offset, d -> cellDimensions[ d ] == minBorderSize[ d ] ? 0 : minBorderSize[ d ] ); + + return getClass().getSimpleName() + + "( dims = " + Util.printCoordinates( dimensions ) + + ", cellDims = " + Util.printCoordinates( cellDimensions ) + + ", offset = " + Util.printCoordinates( offset ) + " )"; + } + + private class CellIntervalsRA extends Point implements RandomAccess< Interval > + { + private final long[] min = new long[ Grid.this.n ]; + + private final long[] max = new long[ Grid.this.n ]; + + private final Interval interval = FinalInterval.wrap( min, max ); + + @Override + public Interval get() + { + getCellInterval( position, min, max ); + return interval; + } + + CellIntervalsRA() + { + super( Grid.this.n ); + } + + CellIntervalsRA( CellIntervalsRA ra ) + { + super( ra ); + } + + @Override + public RandomAccess< Interval > copyRandomAccess() + { + return copy(); + } + + @Override + public RandomAccess< Interval > copy() + { + return new CellIntervalsRA( this ); + } + } + + public class CellIntervals implements RandomAccessibleInterval< Interval >, IterableInterval< Interval > + { + private final long size = Intervals.numElements( numCells ); + + @Override + public int numDimensions() + { + return n; + } + + @Override + public long min( final int d ) + { + return 0; + } + + @Override + public long max( final int d ) + { + return numCells[ d ] - 1; + } + + @Override + public RandomAccess< Interval > randomAccess() + { + return new CellIntervalsRA(); + } + + @Override + public RandomAccess< Interval > randomAccess( final Interval interval ) + { + return randomAccess(); + } + + @Override + public Cursor< Interval > cursor() + { + return new RandomAccessibleIntervalCursor<>( this ); + } + + @Override + public Cursor< Interval > localizingCursor() + { + return cursor(); + } + + @Override + public long size() + { + return size; + } + + @Override + public Interval firstElement() + { + return cursor().next(); + } + + @Override + public FlatIterationOrder iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Interval > iterator() + { + return cursor(); + } + } + + private final CellIntervals cellIntervals; + + public CellIntervals cellIntervals() + { + return cellIntervals; + } +} From ae1c8c209816209aa2eb8e706078a3d2144c8bb0 Mon Sep 17 00:00:00 2001 From: tpietzsch Date: Tue, 7 Sep 2021 15:57:42 +0200 Subject: [PATCH 4/4] Views.tiles() to tile RAI, implemented via more general Views.intervals() --- .../java/net/imglib2/view/FunctionView.java | 92 +++++++++++++++++++ src/main/java/net/imglib2/view/Views.java | 63 +++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 src/main/java/net/imglib2/view/FunctionView.java diff --git a/src/main/java/net/imglib2/view/FunctionView.java b/src/main/java/net/imglib2/view/FunctionView.java new file mode 100644 index 000000000..4f43f2679 --- /dev/null +++ b/src/main/java/net/imglib2/view/FunctionView.java @@ -0,0 +1,92 @@ +/*- + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2021 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; + +import java.util.function.Function; + +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.converter.AbstractConvertedRandomAccess; + +class FunctionView< A, B > implements RandomAccessible< B > +{ + final protected RandomAccessible< A > source; + + private final Function< A, B > function; + + class FunctionRandomAccess extends AbstractConvertedRandomAccess< A, B > + { + FunctionRandomAccess( final RandomAccess< A > source ) + { + super( source ); + } + + @Override + public B get() + { + return function.apply( source.get() ); + } + + @Override + public FunctionRandomAccess copy() + { + return new FunctionRandomAccess( this.source ); + } + } + + public FunctionView( final RandomAccessible< A > source, final Function< A, B > function ) + { + this.source = source; + this.function = function; + } + + @Override + public int numDimensions() + { + return source.numDimensions(); + } + + @Override + public RandomAccess< B > randomAccess() + { + return new FunctionRandomAccess( source.randomAccess() ); + } + + @Override + public RandomAccess< B > randomAccess( final Interval interval ) + { + return new FunctionRandomAccess( source.randomAccess( interval ) ); + } +} diff --git a/src/main/java/net/imglib2/view/Views.java b/src/main/java/net/imglib2/view/Views.java index f63bd060f..ea368539c 100644 --- a/src/main/java/net/imglib2/view/Views.java +++ b/src/main/java/net/imglib2/view/Views.java @@ -69,6 +69,7 @@ import net.imglib2.type.numeric.IntegerType; import net.imglib2.type.numeric.NumericType; import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Grid; import net.imglib2.util.Intervals; import net.imglib2.util.Pair; import net.imglib2.util.Util; @@ -399,6 +400,68 @@ public static < T > IntervalView< T > interval( final RandomAccessible< T > rand return new IntervalView<>( randomAccessible, interval ); } + /** + * Given a RandomAccessible {@code img}, and a RandomAccessible of Intervals + * {@code intervals}, return a RandomAccessible of + * {@link #interval(RandomAccessible, Interval) interval}s on {@code img}. + *

+ * For example, if {@code intervals} contains intervals that tile the + * interval of an image, this method returns a RandomAccessible of the tiles + * of that image. + */ + public static < T > RandomAccessible< RandomAccessibleInterval< T > > intervals( final RandomAccessible< T > img, final RandomAccessible< ? extends Interval > intervals ) + { + return new FunctionView<>( intervals, interval -> Views.interval( img, interval ) ); + } + + /** + * Given a RandomAccessible {@code img}, and a RandomAccessibleInterval of + * intervals {@code intervals}, return a RandomAccessibleInterval of + * {@link #interval(RandomAccessible, Interval) interval}s on {@code img}. + *

+ * For example, if {@code intervals} contains intervals that tile the + * interval of an image, this method returns a RandomAccessible of the tiles + * of that image. + */ + public static < T > RandomAccessibleInterval< RandomAccessibleInterval< T > > intervals( final RandomAccessible< T > img, final RandomAccessibleInterval< ? extends Interval > intervals ) + { + return Views.interval( intervals( img, ( RandomAccessible< ? extends Interval > ) intervals ), intervals ); + } + + /** + * Split the {@code source} image into blocks of the given {@code tileSize}. + * + * @param tileSize + * size of a "standard" block (blocks at the border may be truncated). + * If {@code tileSize.length} doesn't match the number of {@code source} + * dimensions, it is truncated or extended with 1s. + */ + public static < T > RandomAccessibleInterval< RandomAccessibleInterval< T > > tiles( final RandomAccessibleInterval< T > source, final long... tileSize ) + { + long[] b = tileSize; + if ( tileSize.length != source.numDimensions() ) + { + b = Arrays.copyOf( tileSize, source.numDimensions() ); + if ( b.length > tileSize.length ) + Arrays.fill( b, tileSize.length, b.length, 1 ); + } + final Grid grid = new Grid( source.dimensionsAsLongArray(), b ); + return intervals( source, grid.cellIntervals() ); + } + + /** + * Split the {@code source} image into blocks of the given {@code tileSize}. + * + * @param tileSize + * size of a "standard" block (blocks at the border may be truncated). + * If {@code tileSize.length} doesn't match the number of {@code source} + * dimensions, it is truncated or extended with 1s. + */ + public static < T > RandomAccessibleInterval< RandomAccessibleInterval< T > > tiles( final RandomAccessibleInterval< T > source, final int... tileSize ) + { + return tiles( source, Util.int2long( tileSize ) ); + } + /** * Create view that is rotated by 90 degrees. The rotation is specified by * the fromAxis and toAxis arguments.