net.imglib2.display.projector.composite.CompositeXYProjector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of imglib2 Show documentation
Show all versions of imglib2 Show documentation
A multidimensional, type-agnostic image processing library.
/*
* #%L
* ImgLib2: a general-purpose, multidimensional image processing library.
* %%
* Copyright (C) 2009 - 2018 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.display.projector.composite;
import java.util.ArrayList;
import net.imglib2.Cursor;
import net.imglib2.FinalInterval;
import net.imglib2.IterableInterval;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.converter.Converter;
import net.imglib2.display.projector.AbstractProjector2D;
import net.imglib2.type.numeric.ARGBType;
/**
* Creates a composite image from across multiple dimensional positions along an
* axis (typically, but not necessarily, channels). Each dimensional position
* has its own {@link Converter}. The results of the conversions are summed into
* the final value. Positions along the axis can be individually toggled for
* inclusion in the computed composite value using the {@link #setComposite}
* methods.
*
* See XYProjector for the code upon which this class was based.
*
* @author Stephan Saalfeld
* @author Curtis Rueden
* @author Grant Harris
* @author Tobias Pietzsch
*/
public class CompositeXYProjector< A > extends AbstractProjector2D
{
private final ArrayList< Converter< A, ARGBType >> converters;
private final int dimIndex;
private final long positionCount;
private final long positionMin;
private final boolean[] composite;
protected final long[] currentPositions;
protected final Converter< A, ARGBType >[] currentConverters;
private final IterableInterval< ARGBType > target;
private final RandomAccessibleInterval< A > source;
@SuppressWarnings( "unchecked" )
public CompositeXYProjector( final RandomAccessibleInterval< A > source, final IterableInterval< ARGBType > target, final ArrayList< Converter< A, ARGBType >> converters, final int dimIndex )
{
super( source.numDimensions() );
this.source = source;
this.target = target;
this.converters = converters;
this.dimIndex = dimIndex;
// check that there is one converter per dimensional position
positionCount = dimIndex < 0 ? 1 : source.dimension( dimIndex );
positionMin = dimIndex < 0 ? 0 : source.min( dimIndex );
final int converterCount = converters.size();
if ( positionCount != converterCount ) { throw new IllegalArgumentException( "Expected " + positionCount + " converters but got " + converterCount ); }
composite = new boolean[ converterCount ];
composite[ 0 ] = true;
currentPositions = new long[ converterCount ];
currentConverters = new Converter[ converterCount ];
}
// -- CompositeXYProjector methods --
/** Toggles the given position index's inclusion in composite values. */
public void setComposite( final int index, final boolean on )
{
composite[ index ] = on;
}
/** Gets whether the given position index is included in composite values. */
public boolean isComposite( final int index )
{
return composite[ index ];
}
/**
* Toggles composite mode globally. If true, all positions along the
* dimensional axis are included in the composite; if false, the value will
* consist of only the projector's current position (i.e., non-composite
* mode).
*/
public void setComposite( final boolean on )
{
for ( int i = 0; i < composite.length; i++ )
composite[ i ] = on;
}
/** Gets whether composite mode is enabled for all positions. */
public boolean isComposite()
{
for ( int i = 0; i < composite.length; i++ )
if ( !composite[ i ] )
return false;
return true;
}
// -- Projector methods --
// private static long calls = 0;
@Override
public void map()
{
// System.out.println(" CompositeXYProjector::map() : call #"+(++calls));
for ( int d = 2; d < position.length; ++d )
min[ d ] = max[ d ] = position[ d ];
min[ 0 ] = target.min( 0 );
min[ 1 ] = target.min( 1 );
max[ 0 ] = target.max( 0 );
max[ 1 ] = target.max( 1 );
if ( dimIndex < 0 )
{
// there is only converter[0]
// use it to map the current position
final RandomAccess< A > sourceRandomAccess = source.randomAccess( new FinalInterval( min, max ) );
sourceRandomAccess.setPosition( min );
mapSingle( sourceRandomAccess, converters.get( 0 ) );
return;
}
final int size = updateCurrentArrays();
min[ dimIndex ] = max[ dimIndex ] = currentPositions[ 0 ];
for ( int i = 1; i < size; ++i )
if ( currentPositions[ i ] < min[ dimIndex ] )
min[ dimIndex ] = currentPositions[ i ];
else if ( currentPositions[ i ] > max[ dimIndex ] )
max[ dimIndex ] = currentPositions[ i ];
final RandomAccess< A > sourceRandomAccess = source.randomAccess( new FinalInterval( min, max ) );
sourceRandomAccess.setPosition( min );
if ( size == 1 )
{
// there is only one active converter: converter[0]
// use it to map the slice at currentPositions[0]
mapSingle( sourceRandomAccess, currentConverters[ 0 ] );
return;
}
final Cursor< ARGBType > targetCursor = target.localizingCursor();
final ARGBType bi = new ARGBType();
while ( targetCursor.hasNext() )
{
targetCursor.fwd();
sourceRandomAccess.setPosition( targetCursor.getLongPosition( 0 ), 0 );
sourceRandomAccess.setPosition( targetCursor.getLongPosition( 1 ), 1 );
int aSum = 0, rSum = 0, gSum = 0, bSum = 0;
for ( int i = 0; i < size; i++ )
{
sourceRandomAccess.setPosition( currentPositions[ i ], dimIndex );
currentConverters[ i ].convert( sourceRandomAccess.get(), bi );
// accumulate converted result
final int value = bi.get();
final int a = ARGBType.alpha( value );
final int r = ARGBType.red( value );
final int g = ARGBType.green( value );
final int b = ARGBType.blue( value );
aSum += a;
rSum += r;
gSum += g;
bSum += b;
}
if ( aSum > 255 )
aSum = 255;
if ( rSum > 255 )
rSum = 255;
if ( gSum > 255 )
gSum = 255;
if ( bSum > 255 )
bSum = 255;
targetCursor.get().set( ARGBType.rgba( rSum, gSum, bSum, aSum ) );
}
}
// -- Helper methods --
/**
* Walk through composite[] and store the currently active converters and
* positions (in dimension {@link #dimIndex}) to {@link #currentConverters}
* and {@link #currentPositions}.
*
* A special cases is single-position mode. The projector is in
* single-position mode iff all dimensional positions along the composited
* axis are excluded. In this case, the current position along that axis is
* used instead. The converter corresponding to the current position is
* used.
*
* @return number of positions to convert
*/
protected int updateCurrentArrays()
{
int currentSize = 0;
for ( int i = 0; i < composite.length; i++ )
if ( composite[ i ] )
++currentSize;
if ( currentSize == 0 )
{
// this is the isSingle() case.
// map the current position using the converter at that position
currentPositions[ 0 ] = position[ dimIndex ];
currentConverters[ 0 ] = converters.get( ( int ) ( position[ dimIndex ] - positionMin ) );
return 1;
}
// this is the normal case.
// fill currentPositions and currentConverters with the active
// positions and converters
int j = 0;
for ( int i = 0; i < composite.length; i++ )
if ( composite[ i ] )
{
currentPositions[ j ] = positionMin + i;
currentConverters[ j ] = converters.get( i );
++j;
}
return currentSize;
}
protected void mapSingle( final RandomAccess< A > sourceRandomAccess, final Converter< A, ARGBType > conv )
{
final Cursor< ARGBType > targetCursor = target.localizingCursor();
while ( targetCursor.hasNext() )
{
targetCursor.fwd();
sourceRandomAccess.setPosition( targetCursor.getLongPosition( 0 ), 0 );
sourceRandomAccess.setPosition( targetCursor.getLongPosition( 1 ), 1 );
conv.convert( sourceRandomAccess.get(), targetCursor.get() );
}
}
}