diff --git a/src/main/java/net/imglib2/realtransform/AbstractAffineTransform.java b/src/main/java/net/imglib2/realtransform/AbstractAffineTransform.java index 6030524..f35dd00 100644 --- a/src/main/java/net/imglib2/realtransform/AbstractAffineTransform.java +++ b/src/main/java/net/imglib2/realtransform/AbstractAffineTransform.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 @@ -34,17 +34,19 @@ package net.imglib2.realtransform; +import Jama.Matrix; +import net.imglib2.RealInterval; import net.imglib2.RealLocalizable; import net.imglib2.RealPoint; import net.imglib2.RealPositionable; -import Jama.Matrix; +import net.imglib2.realtransform.interval.IntervalSamplingMethod; /** * An abstract implementation of an affine transformation that returns default * values referring to the identity transformation for all fields. This * implementation is not thread safe. Create a {@link #copy()} for each * consumer. - * + * * @author Stephan Saalfeld */ public abstract class AbstractAffineTransform implements AffineGet, AffineSet @@ -136,7 +138,7 @@ public int numTargetDimensions() { return n; } - + @Override public void apply( final double[] source, final double[] target ) { @@ -148,11 +150,11 @@ public void apply( final double[] source, final double[] target ) for ( int c = 0; c < n; ++c ) tmp[ r ] += source[ c ] * a.get( r, c ); } - + for ( int r = 0; r < n; ++r ) target[ r ] = tmp[ r ] + t[ r ]; } - + @Override public void apply( final float[] source, final float[] target ) { @@ -164,7 +166,7 @@ public void apply( final float[] source, final float[] target ) for ( int c = 0; c < n; ++c ) tmp[ r ] += source[ c ] * a.get( r, c ); } - + for ( int r = 0; r < n; ++r ) target[ r ] = ( float )( tmp[ r ] + t[ r ] ); } @@ -180,7 +182,7 @@ public void apply( final RealLocalizable source, final RealPositionable target ) for ( int c = 0; c < n; ++c ) tmp[ r ] += source.getDoublePosition( c ) * a.get( r, c ); } - + for ( int r = 0; r < n; ++r ) target.setPosition( tmp[ r ] + t[ r ], r ); } @@ -215,4 +217,10 @@ public RealLocalizable d( final int d ) return ds[ d ]; } + + @Override + public RealInterval boundingInterval( final RealInterval interval, final IntervalSamplingMethod samplingMethdod ) + { + return AffineGet.super.boundingInterval( interval, IntervalSamplingMethod.CORNERS ); + } } diff --git a/src/main/java/net/imglib2/realtransform/AbstractScale.java b/src/main/java/net/imglib2/realtransform/AbstractScale.java index c223f20..25e3f15 100644 --- a/src/main/java/net/imglib2/realtransform/AbstractScale.java +++ b/src/main/java/net/imglib2/realtransform/AbstractScale.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 @@ -34,9 +34,12 @@ package net.imglib2.realtransform; +import net.imglib2.FinalRealInterval; +import net.imglib2.RealInterval; import net.imglib2.RealLocalizable; import net.imglib2.RealPoint; import net.imglib2.RealPositionable; +import net.imglib2.realtransform.interval.IntervalSamplingMethod; /** * n-d arbitrary scaling. Abstract base implementation. @@ -63,7 +66,7 @@ public AbstractScale( final double... s ) /** * Set the scale vector. - * + * * @param s * s.length <= the number of dimensions of this * {@link AbstractScale} @@ -74,7 +77,7 @@ public AbstractScale( final double... s ) public void applyInverse( final double[] source, final double[] target ) { assert source.length >= s.length && target.length >= s.length : "Input dimensions too small."; - + for ( int d = 0; d < s.length; ++d ) source[ d ] = target[ d ] / s[ d ]; } @@ -83,17 +86,17 @@ public void applyInverse( final double[] source, final double[] target ) public void applyInverse( final float[] source, final float[] target ) { assert source.length >= s.length && target.length >= s.length : "Input dimensions too small."; - + for ( int d = 0; d < s.length; ++d ) source[ d ] = ( float )( target[ d ] / s[ d ] ); - + } @Override public void applyInverse( final RealPositionable source, final RealLocalizable target ) { assert source.numDimensions() >= s.length && target.numDimensions() >= s.length : "Input dimensions too small."; - + for ( int d = 0; d < s.length; ++d ) source.setPosition( target.getDoublePosition( d ) / s[ d ], d ); } @@ -123,7 +126,7 @@ public int numTargetDimensions() public void apply( final double[] source, final double[] target ) { assert source.length >= s.length && target.length >= s.length : "Input dimensions too small."; - + for ( int d = 0; d < s.length; ++d ) target[ d ] = source[ d ] * s[ d ]; } @@ -132,7 +135,7 @@ public void apply( final double[] source, final double[] target ) public void apply( final float[] source, final float[] target ) { assert source.length >= s.length && target.length >= s.length : "Input dimensions too small."; - + for ( int d = 0; d < s.length; ++d ) target[ d ] = ( float )( source[ d ] * s[ d ] ); } @@ -141,7 +144,7 @@ public void apply( final float[] source, final float[] target ) public void apply( final RealLocalizable source, final RealPositionable target ) { assert source.numDimensions() >= s.length && target.numDimensions() >= s.length : "Input dimensions too small."; - + for ( int d = 0; d < s.length; ++d ) target.setPosition( source.getDoublePosition( d ) * s[ d ], d ); } @@ -150,7 +153,7 @@ public void apply( final RealLocalizable source, final RealPositionable target ) public double get( final int row, final int column ) { assert row >= 0 && row < numDimensions() : "Dimension index out of bounds."; - + return row == column ? s[ row ] : 0; } @@ -168,7 +171,7 @@ public double[] getRowPackedCopy() public RealLocalizable d( final int d ) { assert d >= 0 && d < numDimensions() : "Dimension index out of bounds."; - + return ds[ d ]; } @@ -186,14 +189,33 @@ public double[] getScaleCopy() { return s.clone(); } - + @Override - public double getTranslation( final int d ) { + public double getTranslation( final int d ) + { return 0.0; } @Override - public double[] getTranslationCopy() { + public double[] getTranslationCopy() + { return new double[ s.length ]; } + + @Override + public RealInterval boundingInterval( final RealInterval interval, final IntervalSamplingMethod samplingMethdod ) + { + assert interval.numDimensions() >= s.length : "Interval does not have enough dimensions."; + + final double[] min = interval.minAsDoubleArray(); + final double[] max = interval.maxAsDoubleArray(); + + for ( int d = 0; d < s.length; ++d ) + { + min[ d ] *= s[ d ]; + max[ d ] *= s[ d ]; + } + + return new FinalRealInterval( min, max, false ); + } } diff --git a/src/main/java/net/imglib2/realtransform/AbstractTranslation.java b/src/main/java/net/imglib2/realtransform/AbstractTranslation.java index 694bc9a..7c594eb 100644 --- a/src/main/java/net/imglib2/realtransform/AbstractTranslation.java +++ b/src/main/java/net/imglib2/realtransform/AbstractTranslation.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 @@ -34,13 +34,16 @@ package net.imglib2.realtransform; +import net.imglib2.FinalRealInterval; +import net.imglib2.RealInterval; import net.imglib2.RealLocalizable; import net.imglib2.RealPoint; import net.imglib2.RealPositionable; +import net.imglib2.realtransform.interval.IntervalSamplingMethod; /** * n-d translation. Abstract base implementation. - * + * * @author Stephan Saalfeld */ abstract public class AbstractTranslation implements TranslationGet @@ -77,7 +80,7 @@ public AbstractTranslation( final double... t ) /** * Set the translation vector. - * + * * @param t * t.length <= the number of dimensions of this * {@link AbstractTranslation} @@ -86,7 +89,7 @@ public AbstractTranslation( final double... t ) /** * Set one value of the translation vector. - * + * * @param t * t.length <= the number of dimensions of this * {@link AbstractTranslation} @@ -197,7 +200,7 @@ public RealLocalizable d( final int d ) return ds[ d ]; } - + @Override public double getScale( final int d ) { return 0.0; @@ -224,4 +227,21 @@ public double[] getTranslationCopy() @Override abstract public AbstractTranslation inverse(); + + @Override + public RealInterval boundingInterval( final RealInterval interval, final IntervalSamplingMethod samplingMethdod ) + { + assert interval.numDimensions() >= t.length : "Interval does not have enough dimensions."; + + final double[] min = interval.minAsDoubleArray(); + final double[] max = interval.maxAsDoubleArray(); + + for ( int d = 0; d < t.length; ++d ) + { + min[ d ] += t[ d ]; + max[ d ] += t[ d ]; + } + + return new FinalRealInterval( min, max, false ); + } } diff --git a/src/main/java/net/imglib2/realtransform/RealTransform.java b/src/main/java/net/imglib2/realtransform/RealTransform.java index 2f09892..8634e9d 100644 --- a/src/main/java/net/imglib2/realtransform/RealTransform.java +++ b/src/main/java/net/imglib2/realtransform/RealTransform.java @@ -34,9 +34,11 @@ package net.imglib2.realtransform; +import net.imglib2.RealInterval; import net.imglib2.RealLocalizable; import net.imglib2.RealPoint; import net.imglib2.RealPositionable; +import net.imglib2.realtransform.interval.IntervalSamplingMethod; /** * Transformation from Rn to Rm. @@ -257,4 +259,26 @@ default boolean isIdentity() { return false; } + + /** + * Estimate the {@link RealInterval} that bounds the given RealInterval + * after being transformed by a {@link RealTransform}. + *

+ * For arbitrary transformations, it is not necessarily possible to + * directly calculate the resulting bounding box trivially, therefore, + * a sampling methods for coordinates in the source interval must be + * provided. {@link RealTransform}s that can calculate the bounding + * interval more efficiently than by sampling coordinates are encouraged + * to override this method and to ignore the provided sampling method. + * + * @param interval + * the real interval + * @param samplingMethod + * the method used to sample coordinates of the source interval + * @return the bounding interval + */ + default RealInterval boundingInterval( final RealInterval interval, final IntervalSamplingMethod samplingMethod ) + { + return samplingMethod.bounds( interval, this ); + } } diff --git a/src/main/java/net/imglib2/realtransform/ScaleAndTranslation.java b/src/main/java/net/imglib2/realtransform/ScaleAndTranslation.java index b28624e..758fe75 100644 --- a/src/main/java/net/imglib2/realtransform/ScaleAndTranslation.java +++ b/src/main/java/net/imglib2/realtransform/ScaleAndTranslation.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 @@ -36,11 +36,14 @@ */ package net.imglib2.realtransform; +import net.imglib2.FinalRealInterval; +import net.imglib2.RealInterval; import net.imglib2.RealLocalizable; import net.imglib2.RealPoint; import net.imglib2.RealPositionable; import net.imglib2.concatenate.Concatenable; import net.imglib2.concatenate.PreConcatenable; +import net.imglib2.realtransform.interval.IntervalSamplingMethod; /** * An n transform that applies a scaling first and then shifts coordinates. @@ -319,11 +322,28 @@ public Class< ScaleAndTranslationGet > getConcatenableClass() { return ScaleAndTranslationGet.class; } - - + + @Override public boolean isIdentity() { return RealViewsSimplifyUtils.isIdentity( this ); } + + @Override + public RealInterval boundingInterval( final RealInterval interval, final IntervalSamplingMethod samplingMethdod ) + { + assert interval.numDimensions() >= n : "Interval does not have enough dimensions."; + + final double[] min = interval.minAsDoubleArray(); + final double[] max = interval.maxAsDoubleArray(); + + for ( int d = 0; d < n; ++d ) + { + min[ d ] = min[ d ] * scales[ d ] + translations[ d ]; + max[ d ] = max[ d ] * scales[ d ] + translations[ d ]; + } + + return new FinalRealInterval( min, max, false ); + } } diff --git a/src/main/java/net/imglib2/realtransform/interval/Corners.java b/src/main/java/net/imglib2/realtransform/interval/Corners.java new file mode 100644 index 0000000..4101e0d --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/Corners.java @@ -0,0 +1,98 @@ +/* + * #%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.realtransform.interval; + +import java.util.Arrays; + +import net.imglib2.FinalRealInterval; +import net.imglib2.RealInterval; +import net.imglib2.iterator.IntervalIterator; +import net.imglib2.realtransform.RealTransform; + +/** + * + * @author John Bogovic + * @author Stephan Saalfeld + */ +public class Corners implements IntervalSamplingMethod +{ + /** + * + * @param interval + * the real interval + * @param transform + * the transformation + * @return the bounding interval + */ + @Override + public RealInterval bounds( final RealInterval interval, final RealTransform transform ) + { + final int nd = interval.numDimensions(); + final double[] pt = new double[ nd ]; + + final double[] min = new double[ nd ]; + final double[] max = new double[ nd ]; + Arrays.fill( min, Double.MAX_VALUE ); + Arrays.fill( max, Double.MIN_VALUE ); + + // iterate over the corners of an nd-hypercube + final long[] unitInterval = new long[ nd ]; + Arrays.fill( unitInterval, 2 ); + final IntervalIterator it = new IntervalIterator( unitInterval ); + while ( it.hasNext() ) + { + it.fwd(); + for ( int d = 0; d < nd; d++ ) + { + if ( it.getLongPosition( d ) == 0 ) + pt[ d ] = interval.realMin( d ); + else + pt[ d ] = interval.realMax( d ); + } + + transform.apply( pt, pt ); + for ( int d = 0; d < nd; d++ ) + { + if ( pt[ d ] < min[ d ] ) + min[ d ] = pt[ d ]; + + if ( pt[ d ] > max[ d ] ) + max[ d ] = pt[ d ]; + } + } + + return new FinalRealInterval( min, max, false ); + } +} \ No newline at end of file diff --git a/src/main/java/net/imglib2/realtransform/interval/Faces.java b/src/main/java/net/imglib2/realtransform/interval/Faces.java new file mode 100644 index 0000000..629e16a --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/Faces.java @@ -0,0 +1,106 @@ +/* + * #%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.realtransform.interval; + +import java.util.Arrays; + +import net.imglib2.FinalRealInterval; +import net.imglib2.RealInterval; +import net.imglib2.iterator.LocalizingRealIntervalIterator; +import net.imglib2.realtransform.RealTransform; + +/** + * @author John Bogovic + * @author Stephan Saalfeld + */ +public abstract class Faces implements IntervalSamplingMethod +{ + protected abstract double[] spacing( final RealInterval interval ); + + /** + * Estimate the {@link RealInterval} that bounds the given RealInterval + * after being transformed by a {@link RealTransform}. + *

+ * This implementation estimates the bounding interval by transforming points + * on the faces of the given real interval. + * + * @param interval + * the real interval + * @param transform + * the transformation + * @return the bounding interval + */ + @Override + public RealInterval bounds( final RealInterval interval, final RealTransform transform ) + { + assert interval.numDimensions() >= transform.numSourceDimensions() : "Interval dimensions too small for transformation."; + + final int nSource = transform.numSourceDimensions(); + final int nTarget = transform.numTargetDimensions(); + + final double[] itSpacing = spacing( interval ); + + final double[] min = new double[ nTarget ]; + final double[] max = new double[ nTarget ]; + + Arrays.fill( min, Double.MAX_VALUE ); + Arrays.fill( max, Double.MIN_VALUE ); + + final double[] itMin = new double[ nTarget ]; + final double[] itMax = new double[ nTarget ]; + for( int i = 0; i < nSource; i++ ) + { + interval.realMin( itMin ); + interval.realMax( itMax ); + itMin[ i ] = interval.realMin( i ); + itMax[ i ] = interval.realMin( i ); + IntervalSamplingMethod.transformedCoordinateBounds( + transform, + new LocalizingRealIntervalIterator( itMin, itMax, itSpacing ), + min, + max ); + + itMin[ i ] = interval.realMax( i ); + itMax[ i ] = interval.realMax( i ); + IntervalSamplingMethod.transformedCoordinateBounds( + transform, + new LocalizingRealIntervalIterator( itMin, itMax, itSpacing ), + min, + max ); + } + + return new FinalRealInterval( min, max, false ); + } +} \ No newline at end of file diff --git a/src/main/java/net/imglib2/realtransform/interval/FacesSpacing.java b/src/main/java/net/imglib2/realtransform/interval/FacesSpacing.java new file mode 100644 index 0000000..b7f01d1 --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/FacesSpacing.java @@ -0,0 +1,63 @@ +/* + * #%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.realtransform.interval; + +import java.util.Arrays; + +import net.imglib2.RealInterval; + +/** + * @author John Bogovic + * @author Stephan Saalfeld + */ +public class FacesSpacing extends Faces +{ + private final double[] spacing; + + public FacesSpacing( final double... spacing ) + { + this.spacing = spacing; + } + + @Override + protected double[] spacing( final RealInterval interval ) + { + final double[] out = Arrays.copyOf( spacing, interval.numDimensions() ); + for ( int i = spacing.length; i < out.length; ++i ) + out[ i ] = out[ i - 1 ]; + + return out; + } +} \ No newline at end of file diff --git a/src/main/java/net/imglib2/realtransform/interval/FacesSteps.java b/src/main/java/net/imglib2/realtransform/interval/FacesSteps.java new file mode 100644 index 0000000..1977ea2 --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/FacesSteps.java @@ -0,0 +1,66 @@ +/* + * #%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.realtransform.interval; + +import net.imglib2.RealInterval; + +/** + * @author John Bogovic + * @author Stephan Saalfeld + */ +public class FacesSteps extends Faces +{ + private final long[] steps; + + public FacesSteps( final long... steps ) + { + this.steps = steps; + } + + @Override + protected double[] spacing( final RealInterval interval ) + { + final double[] spacing = new double[ interval.numDimensions() ]; + for( int i = 0; i < spacing.length; i++ ) + { + final double w = interval.realMax( i ) - interval.realMin( i ); + if( i < steps.length ) + spacing[ i ] = w / steps[ i ]; + else + spacing[ i ] = w / steps[ steps.length - 1 ]; + } + return spacing; + } +} \ No newline at end of file diff --git a/src/main/java/net/imglib2/realtransform/interval/IntervalSamplingMethod.java b/src/main/java/net/imglib2/realtransform/interval/IntervalSamplingMethod.java new file mode 100644 index 0000000..b6ebbe5 --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/IntervalSamplingMethod.java @@ -0,0 +1,112 @@ +/* + * #%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.realtransform.interval; + +import net.imglib2.RealInterval; +import net.imglib2.RealPoint; +import net.imglib2.iterator.LocalizingRealIntervalIterator; +import net.imglib2.realtransform.RealTransform; + +/** + * + * @author John Bogovic + * @author Stephan Saalfeld + * + */ +public interface IntervalSamplingMethod +{ + /** + * Singleton instance of stateless {@link Corners} method. Use instead of + * creating new instances with {@code new Corners()}. + */ + static Corners CORNERS = new Corners(); + + /** + * Transforms all points produced by the {@link LocalizingRealIntervalIterator} + * with the provided {@link RealTransform} and stores the min and max coordinates + * for each dimension in the provided arrays. + *

+ * Min and max arrays are not reset before iteration, meaning this method + * may be called repeatedly with different iterators to find bounds of + * the union of iterated points. + * + * @param transform a transformation + * @param it the real interval iterator + * @param min the min coordinate array to modify + * @param max the max coordinate array to modify + */ + static void transformedCoordinateBounds( + final RealTransform transform, + final LocalizingRealIntervalIterator it, + final double[] min, + final double[] max ) + { + assert + transform.numTargetDimensions() <= min.length && + transform.numTargetDimensions() <= max.length : + "Transformation target dimensionality too large for min and max vectors."; + + assert + transform.numSourceDimensions() <= it.numDimensions() : + "Transformation target dimensionality too large for min and max vectors."; + + final int nTarget = transform.numTargetDimensions(); + final RealPoint targetPoint = new RealPoint( nTarget ); + while( it.hasNext() ) + { + it.fwd(); + transform.apply( it, targetPoint ); + for( int d = 0; d < nTarget; d++ ) + { + final double p = targetPoint.getDoublePosition( d ); + if( p < min[ d ] ) + min[ d ] = p; + + if( p > max[ d ] ) + max[ d ] = p; + } + } + } + + /** + * + * @param interval + * the real interval + * @param transform + * the transformation + * @return the bounding interval + */ + RealInterval bounds( final RealInterval interval, final RealTransform transform ); +} \ No newline at end of file diff --git a/src/main/java/net/imglib2/realtransform/interval/Volume.java b/src/main/java/net/imglib2/realtransform/interval/Volume.java new file mode 100644 index 0000000..ec7b5e9 --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/Volume.java @@ -0,0 +1,86 @@ +/* + * #%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.realtransform.interval; + +import java.util.Arrays; + +import net.imglib2.FinalRealInterval; +import net.imglib2.RealInterval; +import net.imglib2.iterator.LocalizingRealIntervalIterator; +import net.imglib2.realtransform.RealTransform; + +/** + * @author John Bogovic + * @author Stephan Saalfeld + */ +public abstract class Volume implements IntervalSamplingMethod +{ + protected abstract double[] spacing( final RealInterval interval ); + + /** + * Estimate the {@link RealInterval} that bounds the given RealInterval + * after being transformed by a {@link RealTransform}. + *

+ * This implementation estimates the bounding interval by transforming + * points in the volume of the given real interval. + * + * @param interval + * the real interval + * @param transform + * the transformation + * @return the bounding interval + */ + @Override + public RealInterval bounds( final RealInterval interval, final RealTransform transform ) + { + assert interval.numDimensions() >= transform.numSourceDimensions() : "Interval dimensions too small for transformation."; + + final int nTarget = transform.numTargetDimensions(); + + final double[] min = new double[ nTarget ]; + final double[] max = new double[ nTarget ]; + + Arrays.fill( min, Double.MAX_VALUE ); + Arrays.fill( max, Double.MIN_VALUE ); + + IntervalSamplingMethod.transformedCoordinateBounds( + transform, + new LocalizingRealIntervalIterator( interval, spacing( interval ) ), + min, + max ); + + return new FinalRealInterval( min, max, false ); + } +} \ No newline at end of file diff --git a/src/main/java/net/imglib2/realtransform/interval/VolumeSpacing.java b/src/main/java/net/imglib2/realtransform/interval/VolumeSpacing.java new file mode 100644 index 0000000..1ba46da --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/VolumeSpacing.java @@ -0,0 +1,63 @@ +/* + * #%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.realtransform.interval; + +import java.util.Arrays; + +import net.imglib2.RealInterval; + +/** + * @author John Bogovic + * @author Stephan Saalfeld + */ +public class VolumeSpacing extends Volume +{ + private final double[] spacing; + + public VolumeSpacing( final double... spacing ) + { + this.spacing = spacing; + } + + @Override + protected double[] spacing( final RealInterval interval ) + { + final double[] out = Arrays.copyOf( spacing, interval.numDimensions() ); + for ( int i = spacing.length; i < out.length; ++i ) + out[ i ] = out[ i - 1 ]; + + return out; + } +} \ No newline at end of file diff --git a/src/main/java/net/imglib2/realtransform/interval/VolumeSteps.java b/src/main/java/net/imglib2/realtransform/interval/VolumeSteps.java new file mode 100644 index 0000000..ccc3887 --- /dev/null +++ b/src/main/java/net/imglib2/realtransform/interval/VolumeSteps.java @@ -0,0 +1,65 @@ +/* + * #%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.realtransform.interval; + +import net.imglib2.RealInterval; + +/** + * @author John Bogovic + * @author Stephan Saalfeld + */ +public class VolumeSteps extends Volume +{ + private final long[] steps; + + public VolumeSteps( final long... steps ) + { + this.steps = steps; + } + + @Override + protected double[] spacing( final RealInterval interval ) + { + final double[] spacing = new double[ interval.numDimensions() ]; + final int l = Math.min( spacing.length, steps.length ); + for( int i = 0; i < l; i++ ) + spacing[ i ] = ( interval.realMax( i ) - interval.realMin( i ) ) / steps[ i ]; + + for ( int i = steps.length; i < spacing.length; ++i ) + spacing[ i ] = ( interval.realMax( i ) - interval.realMin( i ) ) / steps[ steps.length - 1 ]; + + return spacing; + } +} \ No newline at end of file diff --git a/src/test/java/net/imglib2/realtransform/interval/RealIntervalsTests.java b/src/test/java/net/imglib2/realtransform/interval/RealIntervalsTests.java new file mode 100644 index 0000000..10f917b --- /dev/null +++ b/src/test/java/net/imglib2/realtransform/interval/RealIntervalsTests.java @@ -0,0 +1,203 @@ + +/*- + * #%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.realtransform.interval; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import net.imglib2.FinalRealInterval; +import net.imglib2.RealInterval; +import net.imglib2.RealPoint; +import net.imglib2.position.FunctionRealRandomAccessible; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.realtransform.PositionFieldTransform; + +public class RealIntervalsTests { + + static final double EPS = 1e-9; + static final FinalRealInterval itvl = new FinalRealInterval( + new double[] { 0, 0, 0 }, new double[] { 40, 30, 20 }); + + @Test + public void testBboxCornersAffine() + { + final AffineTransform3D xfm = new AffineTransform3D(); + xfm.scale(2, 3, 4); + + final RealInterval bbox = xfm.boundingInterval( itvl, IntervalSamplingMethod.CORNERS ); + assertEquals( "max x ", itvl.realMax(0) * 2, bbox.realMax(0), EPS ); + assertEquals( "max y ", itvl.realMax(1) * 3, bbox.realMax(1), EPS ); + assertEquals( "max z ", itvl.realMax(2) * 4, bbox.realMax(2), EPS ); + } + + @Test + public void testBboxFacesAffine() + { + final AffineTransform3D xfm = new AffineTransform3D(); + xfm.scale(2, 3, 4); + + final RealInterval bbox = xfm.boundingInterval( itvl, new FacesSpacing( 10, 10, 10 ) ); + assertEquals( "max x ", itvl.realMax(0) * 2, bbox.realMax(0), EPS ); + assertEquals( "max y ", itvl.realMax(1) * 3, bbox.realMax(1), EPS ); + assertEquals( "max z ", itvl.realMax(2) * 4, bbox.realMax(2), EPS ); + } + + @Test + public void testBboxVolumeAffine() + { + final AffineTransform3D xfm = new AffineTransform3D(); + xfm.scale(2, 3, 4); + + final RealInterval bbox = xfm.boundingInterval( itvl, new VolumeSpacing( 10 ) ); + assertEquals( "max x ", itvl.realMax(0) * 2, bbox.realMax(0), EPS ); + assertEquals( "max y ", itvl.realMax(1) * 3, bbox.realMax(1), EPS ); + assertEquals( "max z ", itvl.realMax(2) * 4, bbox.realMax(2), EPS ); + } + + @Test + public void testBboxCornersPfield() + { + final PositionFieldTransform xfm = pfield(); + + // estimating with the corners does NOT correctly estimate the bounding box + // for this transformation. Make sure that it behaves as expected - + // returning the original interval. + final RealInterval bbox = xfm.boundingInterval( itvl, IntervalSamplingMethod.CORNERS ); + assertEquals( "min x ", itvl.realMin(0), bbox.realMin(0), EPS ); + assertEquals( "max x ", itvl.realMax(0), bbox.realMax(0), EPS ); + + assertEquals( "min y ", itvl.realMin(1), bbox.realMin(1), EPS ); + assertEquals( "max y ", itvl.realMax(1), bbox.realMax(1), EPS ); + + assertEquals( "min z ", itvl.realMin(2), bbox.realMin(2), EPS ); + assertEquals( "max z ", itvl.realMax(2), bbox.realMax(2), EPS ); + } + + @Test + public void testBboxFacesPfield() + { + final PositionFieldTransform xfm = pfield(); + final RealInterval bbox = xfm.boundingInterval( itvl, new FacesSpacing( 5, 5, 5 ) ); + assertEquals( "min x ", itvl.realMin(0), bbox.realMin(0), EPS ); + assertEquals( "max x ", itvl.realMax(0) + 5, bbox.realMax(0), EPS ); + + assertEquals( "min y ", itvl.realMin(1) - 5, bbox.realMin(1), EPS ); + assertEquals( "max y ", itvl.realMax(1), bbox.realMax(1), EPS ); + + assertEquals( "min z ", itvl.realMin(2), bbox.realMin(2), EPS ); + assertEquals( "max z ", itvl.realMax(2), bbox.realMax(2), EPS ); + + final RealInterval bboxSamples = xfm.boundingInterval( itvl, new FacesSteps( 8, 6, 4 ) ); + assertEquals( "min x ", itvl.realMin(0), bboxSamples.realMin(0), EPS ); + assertEquals( "max x ", itvl.realMax(0) + 5, bboxSamples.realMax(0), EPS ); + + assertEquals( "min y ", itvl.realMin(1) - 5, bboxSamples.realMin(1), EPS ); + assertEquals( "max y ", itvl.realMax(1), bboxSamples.realMax(1), EPS ); + + assertEquals( "min z ", itvl.realMin(2), bboxSamples.realMin(2), EPS ); + assertEquals( "max z ", itvl.realMax(2), bboxSamples.realMax(2), EPS ); + } + + @Test + public void testBboxVolumePfield() + { + final PositionFieldTransform xfm = pfield(); + final RealInterval bbox = xfm.boundingInterval( itvl, new VolumeSpacing( 5 ) ); + assertEquals( "min x ", itvl.realMin(0), bbox.realMin(0), EPS ); + assertEquals( "max x ", itvl.realMax(0) + 5, bbox.realMax(0), EPS ); + + assertEquals( "min y ", itvl.realMin(1) - 5, bbox.realMin(1), EPS ); + assertEquals( "max y ", itvl.realMax(1), bbox.realMax(1), EPS ); + + assertEquals( "min z ", itvl.realMin(2), bbox.realMin(2), EPS ); + assertEquals( "max z ", itvl.realMax(2), bbox.realMax(2), EPS ); + + final RealInterval bboxSamples = xfm.boundingInterval( itvl, new VolumeSteps( 8, 6, 4 ) ); + assertEquals( "min x samples", itvl.realMin(0), bboxSamples.realMin(0), EPS ); + assertEquals( "max x samples", itvl.realMax(0) + 5, bboxSamples.realMax(0), EPS ); + + assertEquals( "min y samples", itvl.realMin(1) - 5, bboxSamples.realMin(1), EPS ); + assertEquals( "max y samples", itvl.realMax(1), bboxSamples.realMax(1), EPS ); + + assertEquals( "min z samples", itvl.realMin(2), bboxSamples.realMin(2), EPS ); + assertEquals( "max z samples", itvl.realMax(2), bboxSamples.realMax(2), EPS ); + } + + /** + * This strange position field is designed so that different bounding box + * estimation methods perform differently. + * + * @return a position field. + */ + private static PositionFieldTransform pfield() + { + // center + final double xc = 15; + final double yc = 20; + + // width + final double xwidth = 3; + final double ywidth = 3; + + // amount + final double dx = 5; + final double dy = -5; + + final FunctionRealRandomAccessible vecField = new FunctionRealRandomAccessible<>( 3, + (p,v) -> { + final double x = p.getDoublePosition( 0 ); + final double y = p.getDoublePosition( 1 ); + final double z = p.getDoublePosition( 2 ); + + if( y >= xc - xwidth && y < xc + xwidth ) + v.setPosition(x + dx, 0); + else + v.setPosition(x, 0); + + if( x >= yc - ywidth && x < yc + ywidth ) + v.setPosition(y + dy, 1); + else + v.setPosition(y, 1); + + // identity for z + v.setPosition(z, 2); + }, + () -> new RealPoint( 3 )); + + return new PositionFieldTransform( vecField ); + } + +}