All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.imglib2.transform.integer.MixedTransform Maven / Gradle / Ivy

There is a newer version: 7.1.4
Show newest version
/*
 * #%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.transform.integer;

import java.util.Arrays;
import net.imglib2.Localizable;
import net.imglib2.Positionable;
import net.imglib2.concatenate.Concatenable;
import net.imglib2.concatenate.PreConcatenable;

/**
 * Mixed transform allows to express common integer view transformations such as
 * translation, rotation, rotoinversion, and projection.
 *
 * 

* It transform a n-dimensional source vector to a m-dimensional target vector, * and can be represented as a m+1 × n+1 homogeneous * matrix. The mixed transform can be decomposed as follows: *

*
    *
  1. project down (discard some components of the source vector)
  2. *
  3. component permutation
  4. *
  5. component inversion
  6. *
  7. project up (add zero components in the target vector)
  8. *
  9. translation
  10. *
* *

* The project down and component permutation steps are implemented by the * {@link #setComponentMapping(int[]) component mapping}. This is a lookup array * that specifies for each target dimension from which source dimension it is * taken. * Note, that it is not allowed to set this array such that a source component * is mapped to several target components! *

* * * @author Tobias Pietzsch */ public class MixedTransform extends AbstractMixedTransform implements Concatenable< Mixed >, PreConcatenable< Mixed > { /** * dimension of source vector. */ protected final int numSourceDimensions; /** * translation is added to the target vector after applying permutation, * projection, inversion operations. */ protected final long[] translation; /** * for each component of the target vector (before translation). should the * value be taken from a source vector component (false) or should it be * zero (true). */ protected final boolean[] zero; /** * for each component of the target vector (before translation). should the * source vector component be inverted (true). */ protected final boolean[] invert; /** * for each component of the target vector (before translation). from which * source vector component should it be taken. */ protected final int[] component; public MixedTransform( final int sourceDim, final int targetDim ) { super( targetDim ); this.numSourceDimensions = sourceDim; translation = new long[ targetDim ]; zero = new boolean[ targetDim ]; invert = new boolean[ targetDim ]; component = new int[ targetDim ]; for ( int d = 0; d < targetDim; ++d ) { if ( d < sourceDim ) { component[ d ] = d; } else { component[ d ] = 0; zero[ d ] = true; } } } @Override public int numSourceDimensions() { return numSourceDimensions; } @Override public void getTranslation( final long[] t ) { assert t.length == numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) t[ d ] = translation[ d ]; } @Override public long getTranslation( final int d ) { assert d <= numTargetDimensions; return translation[ d ]; } public void setTranslation( final long[] t ) { assert t.length == numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) translation[ d ] = t[ d ]; } public void setInverseTranslation( final long[] tinv ) { assert tinv.length == numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) translation[ d ] = -tinv[ d ]; } @Override public void getComponentZero( final boolean[] zero ) { assert zero.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { zero[ d ] = this.zero[ d ]; } } @Override public boolean getComponentZero( final int d ) { assert d <= numTargetDimensions; return zero[ d ]; } /** * Set which target dimensions are _not_ taken from source dimensions. * *

* For instance, if the transform maps 2D (x,y) coordinates to the first two * components of a 3D (x,y,z) coordinate, this will be [false, false, true] *

* * @param zero * array that says for each component of the target vector * (before translation) whether the value should be taken from a * source vector component (false) or should be set to zero * (true). */ public void setComponentZero( final boolean[] zero ) { assert zero.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { this.zero[ d ] = zero[ d ]; } } @Override public void getComponentMapping( final int[] component ) { assert component.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { component[ d ] = this.component[ d ]; } } @Override public int getComponentMapping( final int d ) { assert d <= numTargetDimensions; return component[ d ]; } /** * Set for each target dimensions from which source dimension it is taken. * *

* For instance, if the transform maps 2D (x,y) coordinates to the first two * components of a 3D (x,y,z) coordinate, this will be [0, 1, x]. Here, x * can be any value because the third target dimension does not correspond * to any source dimension, which can be realized using * {@link #setComponentZero(boolean[])}. *

* *

* Note, that it is not allowed to set the {@code component} array such that * a source component is mapped to several target components! *

* * @param component * array that says for each component of the target vector * (before translation) from which source vector component it * should be taken. */ public void setComponentMapping( final int[] component ) { assert component.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { this.component[ d ] = component[ d ]; } } @Override public void getComponentInversion( final boolean[] invert ) { assert invert.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { invert[ d ] = this.invert[ d ]; } } @Override public boolean getComponentInversion( final int d ) { assert d <= numTargetDimensions; return invert[ d ]; } /** * Set for each target component, whether the source component it is taken * from should be inverted. * *

* For instance, if rotating a 2D (x,y) coordinates by 180 degrees will map * it to (-x,-y). In this case, this will be [true, true]. *

* * @param invert * array that says for each component of the target vector * (before translation) whether the source vector component it is * taken from should be inverted (true). */ public void setComponentInversion( final boolean[] invert ) { assert invert.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { this.invert[ d ] = invert[ d ]; } } @Override public void apply( final long[] source, final long[] target ) { assert source.length >= numSourceDimensions; assert target.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { target[ d ] = translation[ d ]; if ( !zero[ d ] ) { final long v = source[ component[ d ] ]; if ( invert[ d ] ) target[ d ] -= v; else target[ d ] += v; } } } @Override public void apply( final int[] source, final int[] target ) { assert source.length >= numSourceDimensions; assert target.length >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { target[ d ] = ( int ) translation[ d ]; if ( !zero[ d ] ) { final long v = source[ component[ d ] ]; if ( invert[ d ] ) target[ d ] -= v; else target[ d ] += v; } } } @Override public void apply( final Localizable source, final Positionable target ) { assert source.numDimensions() >= numSourceDimensions; assert target.numDimensions() >= numTargetDimensions; for ( int d = 0; d < numTargetDimensions; ++d ) { long pos = translation[ d ]; if ( !zero[ d ] ) { final long v = source.getLongPosition( component[ d ] ); if ( invert[ d ] ) pos -= v; else pos += v; } target.setPosition( pos, d ); } } @Override public MixedTransform concatenate( final Mixed t ) { assert this.numSourceDimensions == t.numTargetDimensions(); final MixedTransform result = new MixedTransform( t.numSourceDimensions(), this.numTargetDimensions ); for ( int d = 0; d < result.numTargetDimensions; ++d ) { result.translation[ d ] = this.translation[ d ]; if ( this.zero[ d ] ) { result.zero[ d ] = true; result.invert[ d ] = false; result.component[ d ] = 0; } else { final int c = this.component[ d ]; final long v = t.getTranslation( c ); if ( this.invert[ d ] ) result.translation[ d ] -= v; else result.translation[ d ] += v; if ( t.getComponentZero( c ) ) { result.zero[ d ] = true; result.invert[ d ] = false; result.component[ d ] = 0; } else { result.zero[ d ] = false; result.invert[ d ] = ( this.invert[ d ] != t.getComponentInversion( c ) ); result.component[ d ] = t.getComponentMapping( c ); } } } return result; } @Override public Class< Mixed > getConcatenableClass() { return Mixed.class; } @Override public MixedTransform preConcatenate( final Mixed t ) { assert t.numSourceDimensions() == this.numTargetDimensions; final MixedTransform result = new MixedTransform( this.numSourceDimensions, t.numTargetDimensions() ); for ( int d = 0; d < result.numTargetDimensions; ++d ) { result.translation[ d ] = t.getTranslation( d ); if ( t.getComponentZero( d ) ) { result.zero[ d ] = true; result.invert[ d ] = false; result.component[ d ] = 0; } else { final int c = t.getComponentMapping( d ); final long v = this.translation[ c ]; if ( t.getComponentInversion( d ) ) result.translation[ d ] -= v; else result.translation[ d ] += v; if ( this.zero[ c ] ) { result.zero[ d ] = true; result.invert[ d ] = false; result.component[ d ] = 0; } else { result.zero[ d ] = false; result.invert[ d ] = ( t.getComponentInversion( d ) != this.invert[ c ] ); result.component[ d ] = this.component[ c ]; } } } return result; } @Override public Class< Mixed > getPreConcatenableClass() { return Mixed.class; } /** * set parameters to transform. * * @param transform */ public void set( final Mixed transform ) { assert numSourceDimensions == transform.numSourceDimensions(); assert numTargetDimensions == transform.numTargetDimensions(); transform.getTranslation( translation ); transform.getComponentZero( zero ); transform.getComponentMapping( component ); transform.getComponentInversion( invert ); } /** * Get the matrix that transforms homogeneous source points to homogeneous * target points. For testing purposes. */ @Override public double[][] getMatrix() { final double[][] mat = new double[ numTargetDimensions + 1 ][ numSourceDimensions + 1 ]; mat[ numTargetDimensions ][ numSourceDimensions ] = 1; for ( int d = 0; d < numTargetDimensions; ++d ) { mat[ d ][ numSourceDimensions ] = translation[ d ]; } for ( int d = 0; d < numTargetDimensions; ++d ) { if ( zero[ d ] == false ) { mat[ d ][ component[ d ] ] = invert[ d ] ? -1 : 1; } } return mat; } /** * Check whether the transforms has a full mapping of source to target * components (no source component is discarded). * * @return whether there is a full mapping of source to target components. */ public boolean hasFullSourceMapping() { final boolean[] sourceMapped = new boolean[ numSourceDimensions ]; for ( int d = 0; d < numTargetDimensions; ++d ) { if ( !zero[ d ] ) { sourceMapped[ component[ d ] ] = true; } } for ( int d = 0; d < numSourceDimensions; ++d ) { if ( !sourceMapped[ d ] ) { return false; } } return true; } @Override public String toString() { return "MixedTransform{" + numSourceDimensions + "->" + numTargetDimensions + ", component=" + Arrays.toString( component ) + ", invert=" + Arrays.toString( invert ) + ", zero=" + Arrays.toString( zero ) + ", translation=" + Arrays.toString( translation ) + '}'; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy