/*-
* #%L
* Fiji distribution of ImageJ for the life sciences.
* %%
* Copyright (C) 2007 - 2017 Fiji developers.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package spim.process.fusion.weightedavg;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import mpicbg.spim.data.sequence.Channel;
import mpicbg.spim.data.sequence.TimePoint;
import mpicbg.spim.data.sequence.ViewDescription;
import mpicbg.spim.data.sequence.ViewId;
import mpicbg.spim.io.IOFunctions;
import net.imglib2.Cursor;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealRandomAccessible;
import net.imglib2.img.Img;
import net.imglib2.interpolation.InterpolatorFactory;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;
import spim.Threads;
import spim.fiji.spimdata.SpimData2;
import spim.process.fusion.FusionHelper;
import spim.process.fusion.ImagePortion;
import spim.process.fusion.boundingbox.BoundingBoxGUI;
public class ProcessSequential extends ProcessFusion
{
final int numSequentialViews;
public ProcessSequential(
final SpimData2 spimData,
final List< ViewId > viewIdsToProcess,
final BoundingBoxGUI bb,
final boolean useBlending,
final boolean useContentBased,
final int numSequentialViews )
{
super( spimData, viewIdsToProcess, bb, useBlending, useContentBased );
this.numSequentialViews = numSequentialViews;
}
/**
* Fuses one stack, i.e. all angles/illuminations for one timepoint and channel
*
* @param type
* @param interpolatorFactory
* @param timepoint
* @param channel
* @return
*/
@Override
public < T extends RealType< T > & NativeType< T > > Img< T > fuseStack(
final T type,
final InterpolatorFactory< T, RandomAccessible< T > > interpolatorFactory,
final TimePoint timepoint,
final Channel channel )
{
IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Reserving memory for fused image.");
// get all views that are fused
final ArrayList< ViewDescription > allInputData =
FusionHelper.assembleInputData( spimData, timepoint, channel, viewIdsToProcess );
// it can be that for a certain comination of timepoint/channel there is nothing to do
// (e.g. fuse timepoint 1 channel 1 and timepoint 2 channel 2)
if ( allInputData.size() == 0 )
return null;
// try creating the output (type needs to be there to define T)
final Img< T > fusedImg = bb.getImgFactory( type ).create( bb.getDimensions(), type );
if ( fusedImg == null )
{
IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): WeightedAverageFusion: Cannot create output image." );
return null;
}
// create the image for the weights
final Img< FloatType > weightImg = bb.getImgFactory( new FloatType() ).create( bb.getDimensions(), new FloatType() );
if ( weightImg == null )
{
IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): WeightedAverageFusion: Cannot create weight image." );
return null;
}
// we will need to run some batches until all is fused
for ( int batch = 0; batch < numBatches( allInputData.size(), numSequentialViews ); ++batch )
{
final int start = batch * numSequentialViews;
final int end = Math.min( ( batch + 1 ) * numSequentialViews, allInputData.size() );
IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): Fusing view " + start + " ... " + (end-1) + " of " + (allInputData.size()-1) );
final ArrayList< ViewDescription > inputData = new ArrayList< ViewDescription >();
for ( int i = start; i < end; ++i )
inputData.add( allInputData.get( i ) );
// same as in the paralell fusion now more or less
final ArrayList< RandomAccessibleInterval< T > > imgs = new ArrayList< RandomAccessibleInterval< T > >();
for ( int i = 0; i < inputData.size(); ++i )
{
final ViewDescription vd = inputData.get( i );
IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Requesting Img from ImgLoader (tp=" + vd.getTimePointId() + ", setup=" + vd.getViewSetupId() + ")" );
imgs.add( getImage( type, spimData, vd, false ) );
}
// get all weighting methods
final ArrayList< ArrayList< RealRandomAccessible< FloatType > > > weights = new ArrayList< ArrayList< RealRandomAccessible< FloatType > > >();
for ( int i = 0; i < inputData.size(); ++i )
weights.add( getAllWeights( imgs.get( i ), inputData.get( i ), spimData.getSequenceDescription().getImgLoader() ) );
// split up into many parts for multithreading
final Vector< ImagePortion > portions = FusionHelper.divideIntoPortions( fusedImg.size(), Threads.numThreads() * 4 );
// set up executor service
final ExecutorService taskExecutor = Executors.newFixedThreadPool( Threads.numThreads() );
final ArrayList< ProcessSequentialPortion< T > > tasks = new ArrayList< ProcessSequentialPortion< T > >();
if ( weights.get( 0 ).size() == 0 ) // no weights
{
for ( final ImagePortion portion : portions )
tasks.add( new ProcessSequentialPortion< T >( portion, imgs, interpolatorFactory, getTransforms( inputData ), fusedImg, weightImg, bb ) );
}
else if ( weights.get( 0 ).size() > 1 ) // many weights
{
for ( final ImagePortion portion : portions )
tasks.add( new ProcessSequentialPortionWeights< T >( portion, imgs, weights, interpolatorFactory, getTransforms( inputData ), fusedImg, weightImg, bb ) );
}
else // one weight
{
final ArrayList< RealRandomAccessible< FloatType > > singleWeight = new ArrayList< RealRandomAccessible< FloatType > >();
for ( int i = 0; i < inputData.size(); ++i )
singleWeight.add( weights.get( i ).get( 0 ) );
for ( final ImagePortion portion : portions )
tasks.add( new ProcessSequentialPortionWeight< T >( portion, imgs, singleWeight, interpolatorFactory, getTransforms( inputData ), fusedImg, weightImg, bb ) );
}
IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): Starting fusion process.");
try
{
// invokeAll() returns when all tasks are complete
taskExecutor.invokeAll( tasks );
}
catch ( final InterruptedException e )
{
IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): Failed to compute fusion: " + e );
e.printStackTrace();
return null;
}
taskExecutor.shutdown();
}
// compute final image from intensities and weights
mergeFinalImage( fusedImg, weightImg );
return fusedImg;
}
protected < T extends RealType< T > > void mergeFinalImage( final Img< T > img, final Img< FloatType > weights )
{
// split up into many parts for multithreading
final Vector< ImagePortion > portions = FusionHelper.divideIntoPortions( img.size(), Threads.numThreads() * 4 );
// set up executor service
final ExecutorService taskExecutor = Executors.newFixedThreadPool( Threads.numThreads() );
final ArrayList< Callable< String > > tasks = new ArrayList< Callable< String > >();
for ( final ImagePortion portion : portions )
{
tasks.add( new Callable< String >()
{
@Override
public String call() throws Exception
{
final Cursor< T > cursor = img.cursor();
final Cursor< FloatType > cursorW = weights.cursor();
cursor.jumpFwd( portion.getStartPosition() );
cursorW.jumpFwd( portion.getStartPosition() );
for ( int j = 0; j < portion.getLoopSize(); ++j )
{
final float w = cursorW.next().get();
final T type = cursor.next();
if ( w > 0 )
type.setReal( type.getRealFloat() / w );
}
return "";
}
});
}
try
{
// invokeAll() returns when all tasks are complete
taskExecutor.invokeAll( tasks );
}
catch ( final InterruptedException e )
{
IOFunctions.println( "Failed to merge final image: " + e );
e.printStackTrace();
return;
}
taskExecutor.shutdown();
}
protected int numBatches( final int numViews, final int sequentialViews )
{
return numViews / sequentialViews + Math.min( numViews % sequentialViews, 1 );
}
}