diff --git a/pom.xml b/pom.xml index 7d8c8f8e..c673c1bd 100644 --- a/pom.xml +++ b/pom.xml @@ -152,10 +152,12 @@ net.imglib2 imglib2 + 5.13.1-SNAPSHOT net.imglib2 imglib2-realtransform + 3.1.3-SNAPSHOT net.imglib2 diff --git a/src/main/java/bdv/tools/transformation/RealTransformedSource.java b/src/main/java/bdv/tools/transformation/RealTransformedSource.java new file mode 100644 index 00000000..781381cc --- /dev/null +++ b/src/main/java/bdv/tools/transformation/RealTransformedSource.java @@ -0,0 +1,210 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2022 BigDataViewer developers. + * %% + * 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 bdv.tools.transformation; + +import java.util.function.BiFunction; +import java.util.function.Supplier; + +import bdv.viewer.Interpolation; +import bdv.viewer.Source; +import bdv.viewer.render.DefaultMipmapOrdering; +import bdv.viewer.render.MipmapOrdering; +import mpicbg.spim.data.sequence.VoxelDimensions; +import net.imglib2.Interval; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealRandomAccessible; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.realtransform.InvertibleRealTransform; +import net.imglib2.realtransform.RealTransform; +import net.imglib2.realtransform.RealTransformRealRandomAccessible; +import net.imglib2.realtransform.RealTransformSequence; +import net.imglib2.util.Intervals; +import net.imglib2.util.RealIntervals; +import net.imglib2.view.Views; + +/** + * A {@link Source} that wraps another {@link Source}, transforming it with a + * {@link InvertibleRealTransform}. + * + * @author John Bogovic + * + * @param the type + */ +public class RealTransformedSource < T > implements Source< T >, MipmapOrdering +{ + /** + * The wrapped {@link Source}. + */ + private final Source< T > source; + + private final String name; + + /** + * This is either the {@link #source} itself, if it implements + * {@link MipmapOrdering}, or a {@link DefaultMipmapOrdering}. + */ + private final MipmapOrdering sourceMipmapOrdering; + + private InvertibleRealTransform transform; + + private final Supplier< Boolean > boundingBoxCullingSupplier; + + private final BiFunction< RealTransform, Interval, Interval > boundingBoxEstimator; + + public RealTransformedSource( final Source< T > source, final String name, + final InvertibleRealTransform transform ) + { + this( source, name, transform, + (t,i) -> { return Intervals.smallestContainingInterval( + RealIntervals.boundingIntervalFaces( i, t, 5 )); }, + null ); + } + + public RealTransformedSource( final Source< T > source, final String name, + final BiFunction< RealTransform, Interval, Interval > boundingBoxEstimator, + final InvertibleRealTransform transform ) + { + this( source, name, transform, boundingBoxEstimator, null ); + } + + public RealTransformedSource( final Source< T > source, final String name, + final InvertibleRealTransform transform, + final BiFunction< RealTransform, Interval, Interval > boundingBoxEstimator, + final Supplier< Boolean > doBoundingBoxCulling ) + { + this.source = source; + this.name = name; + this.boundingBoxEstimator = boundingBoxEstimator; + this.boundingBoxCullingSupplier = doBoundingBoxCulling; + setTransform( transform ); + + sourceMipmapOrdering = MipmapOrdering.class.isInstance( source ) ? + ( MipmapOrdering ) source : new DefaultMipmapOrdering( source ); + } + + @Override + public boolean isPresent( final int t ) + { + return source.isPresent( t ); + } + + @Override + public boolean doBoundingBoxCulling() + { + if( boundingBoxCullingSupplier != null ) + return boundingBoxCullingSupplier.get(); + else + return source.doBoundingBoxCulling(); + } + + public void setTransform( final InvertibleRealTransform transform ) + { + this.transform = transform; + } + + public Source< T > getWrappedSource() + { + return source; + } + + @Override + public RandomAccessibleInterval< T > getSource( final int t, final int level ) + { + // TODO expose interp method - it probably matters + @SuppressWarnings("unchecked") + final RealTransformRealRandomAccessible interpSrc = (RealTransformRealRandomAccessible)getInterpolatedSource( t, level, Interpolation.NEARESTNEIGHBOR ); + + final AffineTransform3D transform = new AffineTransform3D(); + source.getSourceTransform( t, level, transform ); + final RealTransformSequence totalInverseTransform = new RealTransformSequence(); + totalInverseTransform.add( transform.inverse() ); + totalInverseTransform.add( transform.inverse() ); + totalInverseTransform.add( transform ); + final Interval boundingInterval = boundingBoxEstimator.apply( totalInverseTransform, source.getSource( t, level ) ); + + return Views.interval( Views.raster(interpSrc), boundingInterval ); + } + + @Override + public RealRandomAccessible< T > getInterpolatedSource( final int t, final int level, final Interpolation method ) + { + final RealRandomAccessible realSrc = source.getInterpolatedSource( t, level, method ); + final AffineTransform3D transform = new AffineTransform3D(); + source.getSourceTransform( t, level, transform ); + + final RealTransformSequence totalTransform = new RealTransformSequence(); + totalTransform.add( transform ); + totalTransform.add( transform ); + totalTransform.add( transform.inverse() ); + + return new RealTransformRealRandomAccessible< T, RealTransform >( realSrc, totalTransform ); + } + + @Override + public synchronized void getSourceTransform( final int t, final int level, final AffineTransform3D transform ) + { + source.getSourceTransform( t, level, transform ); + } + + public InvertibleRealTransform getTransform() + { + return transform; + } + + @Override + public T getType() + { + return source.getType(); + } + + @Override + public String getName() + { + return name; + } + + @Override + public VoxelDimensions getVoxelDimensions() + { + return source.getVoxelDimensions(); + } + + @Override + public int getNumMipmapLevels() + { + return source.getNumMipmapLevels(); + } + + @Override + public synchronized MipmapHints getMipmapHints( final AffineTransform3D screenTransform, final int timepoint, final int previousTimepoint ) + { + return sourceMipmapOrdering.getMipmapHints( screenTransform, timepoint, previousTimepoint ); + } + +}