Skip to content

Commit

Permalink
Add affine transform UnaryBlockOperator
Browse files Browse the repository at this point in the history
  • Loading branch information
tpietzsch committed Mar 30, 2024
1 parent 0babe09 commit 118853d
Show file tree
Hide file tree
Showing 6 changed files with 1,246 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package net.imglib2.algorithm.blocks.transform;

import java.util.Arrays;
import java.util.function.Supplier;
import net.imglib2.Interval;
import net.imglib2.RealInterval;
import net.imglib2.algorithm.blocks.BlockProcessor;
import net.imglib2.algorithm.blocks.util.BlockProcessorSourceInterval;
import net.imglib2.blocks.TempArray;
import net.imglib2.type.PrimitiveType;
import net.imglib2.util.CloseableThreadLocal;
import net.imglib2.util.Intervals;

/**
* Abstract base class for {@link Affine3DProcessor} and {@link
* Affine2DProcessor}. Implements source/target interval computation, and {@code
* TempArray} and thread-safe setup.
*
* @param <T>
* recursive type of this {@code AbstractTransformProcessor} (for {@link #threadSafeSupplier})
* @param <P>
* input/output primitive array type (i.e., float[] or double[])
*/
abstract class AbstractTransformProcessor< T extends AbstractTransformProcessor< T, P >, P > implements BlockProcessor< P, P >
{
PrimitiveType primitiveType;

Transform.Interpolation interpolation;

final int n;

final long[] destPos;

final int[] destSize;

final long[] sourcePos;

final int[] sourceSize;

private int sourceLength;

private final BlockProcessorSourceInterval sourceInterval;

private final TempArray< P > tempArray;

Supplier< T > threadSafeSupplier;

AbstractTransformProcessor( final int n, final Transform.Interpolation interpolation, final PrimitiveType primitiveType )
{
this.primitiveType = primitiveType;
this.interpolation = interpolation;
this.n = n;
destPos = new long[ n ];
destSize = new int[ n ];
sourcePos = new long[ n ];
sourceSize = new int[ n ];
sourceInterval = new BlockProcessorSourceInterval( this );
tempArray = TempArray.forPrimitiveType( primitiveType );
}

AbstractTransformProcessor( T transform )
{
// re-use
primitiveType = transform.primitiveType;
interpolation = transform.interpolation;
n = transform.n;
threadSafeSupplier = transform.threadSafeSupplier;

// init empty
destPos = new long[ n ];
destSize = new int[ n ];
sourcePos = new long[ n ];
sourceSize = new int[ n ];

// init new instance
sourceInterval = new BlockProcessorSourceInterval( this );
tempArray = TempArray.forPrimitiveType( primitiveType );
}

abstract T newInstance();

@Override
public Supplier< T > threadSafeSupplier()
{
if ( threadSafeSupplier == null )
threadSafeSupplier = CloseableThreadLocal.withInitial( this::newInstance )::get;
return threadSafeSupplier;
}

abstract RealInterval estimateBounds( Interval interval );

@Override
public void setTargetInterval( final Interval interval )
{
interval.min( destPos );
Arrays.setAll( destSize, d -> ( int ) interval.dimension( d ) );

final RealInterval bounds = estimateBounds( interval );
switch ( interpolation )
{
case NEARESTNEIGHBOR:
Arrays.setAll( sourcePos, d -> Math.round( bounds.realMin( d ) - 0.5 ) );
Arrays.setAll( sourceSize, d -> ( int ) ( Math.round( bounds.realMax( d ) + 0.5 ) - sourcePos[ d ] ) + 1 );
break;
case NLINEAR:
Arrays.setAll( sourcePos, d -> ( long ) Math.floor( bounds.realMin( d ) - 0.5 ) );
Arrays.setAll( sourceSize, d -> ( int ) ( ( long ) Math.floor( bounds.realMax( d ) + 0.5 ) - sourcePos[ d ] ) + 2 );
break;
}
sourceLength = safeInt( Intervals.numElements( sourceSize ) );
}

static int safeInt( final long value )
{
if ( value > Integer.MAX_VALUE )
throw new IllegalArgumentException( "value too large" );
return ( int ) value;
}

@Override
public long[] getSourcePos()
{
return sourcePos;
}

@Override
public int[] getSourceSize()
{
return sourceSize;
}

@Override
public Interval getSourceInterval()
{
return sourceInterval;
}

@Override
public P getSourceBuffer()
{
return tempArray.get( sourceLength );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package net.imglib2.algorithm.blocks.transform;

import net.imglib2.Interval;
import net.imglib2.RealInterval;
import net.imglib2.algorithm.blocks.BlockProcessor;
import net.imglib2.realtransform.AffineTransform2D;
import net.imglib2.type.PrimitiveType;

/**
* A {@link BlockProcessor} for interpolation and affine transform, using {@link
* AffineTransform2D} and 2D source/target.
*
* @param <P>
* input/output primitive array type (i.e., float[] or double[])
*/
class Affine2DProcessor< P > extends AbstractTransformProcessor< Affine2DProcessor< P >, P >
{
private final AffineTransform2D transformToSource;

private final TransformLine2D< P > transformLine;

private final double pdest[] = new double[ 2 ];

private final double psrc[] = new double[ 2 ];

Affine2DProcessor(
final AffineTransform2D transformToSource,
final Transform.Interpolation interpolation,
final PrimitiveType primitiveType )
{
this( transformToSource, interpolation, primitiveType, TransformLine2D.of( interpolation, primitiveType ) );
}

private Affine2DProcessor(
final AffineTransform2D transformToSource,
final Transform.Interpolation interpolation,
final PrimitiveType primitiveType,
final TransformLine2D< P > transformLine )
{
super( 2, interpolation, primitiveType );
this.transformToSource = transformToSource;
this.transformLine = transformLine;
}

private Affine2DProcessor( Affine2DProcessor< P > processor )
{
super( processor );
transformToSource = processor.transformToSource;
transformLine = processor.transformLine;
}

@Override
Affine2DProcessor newInstance()
{
return new Affine2DProcessor( this );
}

@Override
RealInterval estimateBounds( final Interval interval )
{
return transformToSource.estimateBounds( interval );
}

// specific to 3D
@Override
public void compute( final P src, final P dest )
{
final float d0 = transformToSource.d( 0 ).getFloatPosition( 0 );
final float d1 = transformToSource.d( 0 ).getFloatPosition( 1 );
final int ds0 = destSize[ 0 ];
final int ss0 = sourceSize[ 0 ];
pdest[ 0 ] = destPos[ 0 ];
int i = 0;
for ( int y = 0; y < destSize[ 1 ]; ++y )
{
pdest[ 1 ] = y + destPos[ 1 ];
transformToSource.apply( pdest, psrc );
float sf0 = ( float ) ( psrc[ 0 ] - sourcePos[ 0 ] );
float sf1 = ( float ) ( psrc[ 1 ] - sourcePos[ 1 ] );
transformLine.apply( src, dest, i, ds0, d0, d1, ss0, sf0, sf1 );
i += ds0;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package net.imglib2.algorithm.blocks.transform;

import net.imglib2.Interval;
import net.imglib2.RealInterval;
import net.imglib2.algorithm.blocks.BlockProcessor;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.PrimitiveType;

/**
* A {@link BlockProcessor} for interpolation and affine transform, using {@link
* AffineTransform3D} and 3D source/target.
*
* @param <P>
* input/output primitive array type (i.e., float[] or double[])
*/
class Affine3DProcessor< P > extends AbstractTransformProcessor< Affine3DProcessor< P >, P >
{
private final AffineTransform3D transformToSource;

private final TransformLine3D< P > transformLine;

private final double pdest[] = new double[ 3 ];

private final double psrc[] = new double[ 3 ];

Affine3DProcessor(
final AffineTransform3D transformToSource,
final Transform.Interpolation interpolation,
final PrimitiveType primitiveType )
{
this( transformToSource, interpolation, primitiveType, TransformLine3D.of( interpolation, primitiveType ) );
}

private Affine3DProcessor(
final AffineTransform3D transformToSource,
final Transform.Interpolation interpolation,
final PrimitiveType primitiveType,
final TransformLine3D< P > transformLine )
{
super( 3, interpolation, primitiveType );
this.transformToSource = transformToSource;
this.transformLine = transformLine;
}

private Affine3DProcessor( Affine3DProcessor< P > processor )
{
super( processor );
transformToSource = processor.transformToSource;
transformLine = processor.transformLine;
}

@Override
Affine3DProcessor newInstance()
{
return new Affine3DProcessor( this );
}

@Override
RealInterval estimateBounds( final Interval interval )
{
return transformToSource.estimateBounds( interval );
}

// specific to 3D
@Override
public void compute( final P src, final P dest )
{
final float d0 = transformToSource.d( 0 ).getFloatPosition( 0 );
final float d1 = transformToSource.d( 0 ).getFloatPosition( 1 );
final float d2 = transformToSource.d( 0 ).getFloatPosition( 2 );
final int ds0 = destSize[ 0 ];
final int ss0 = sourceSize[ 0 ];
final int ss1 = sourceSize[ 1 ] * ss0;
pdest[ 0 ] = destPos[ 0 ];
int i = 0;
for ( int z = 0; z < destSize[ 2 ]; ++z )
{
pdest[ 2 ] = z + destPos[ 2 ];
for ( int y = 0; y < destSize[ 1 ]; ++y )
{
pdest[ 1 ] = y + destPos[ 1 ];
transformToSource.apply( pdest, psrc );
float sf0 = ( float ) ( psrc[ 0 ] - sourcePos[ 0 ] );
float sf1 = ( float ) ( psrc[ 1 ] - sourcePos[ 1 ] );
float sf2 = ( float ) ( psrc[ 2 ] - sourcePos[ 2 ] );
transformLine.apply( src, dest, i, ds0, d0, d1, d2, ss0, ss1, sf0, sf1, sf2 );
i += ds0;
}
}
}
}
Loading

0 comments on commit 118853d

Please sign in to comment.