Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add affine transform UnaryBlockOperator #101

Merged
merged 2 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading