/*- * #%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; import java.util.ArrayList; import java.util.List; import java.util.Vector; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import mpicbg.spim.data.sequence.Angle; import mpicbg.spim.data.sequence.Channel; import mpicbg.spim.data.sequence.Illumination; 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.IterableInterval; import net.imglib2.RandomAccessibleInterval; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.view.Views; import spim.Threads; import spim.fiji.spimdata.SpimData2; public class FusionHelper { /** * Do not instantiate */ private FusionHelper() {} public static String getIllumName( final List< Illumination > illumsToProcess ) { String illumName = "_Ill" + illumsToProcess.get( 0 ).getName(); for ( int i = 1; i < illumsToProcess.size(); ++i ) illumName += "," + illumsToProcess.get( i ).getName(); return illumName; } public static String getAngleName( final List< Angle > anglesToProcess ) { String angleName = "_Ang" + anglesToProcess.get( 0 ).getName(); for ( int i = 1; i < anglesToProcess.size(); ++i ) angleName += "," + anglesToProcess.get( i ).getName(); return angleName; } public static final boolean intersects( final double x, final double y, final double z, final long sx, final long sy, final long sz ) { if ( x >= 0 && y >= 0 && z >= 0 && x < sx && y < sy && z < sz ) return true; else return false; } public static final ArrayList< ViewDescription > assembleInputData( final SpimData2 spimData, final TimePoint timepoint, final Channel channel, final List< ViewId > viewIdsToProcess ) { final ArrayList< ViewDescription > inputData = new ArrayList< ViewDescription >(); for ( final ViewId viewId : viewIdsToProcess ) { final ViewDescription vd = spimData.getSequenceDescription().getViewDescription( viewId.getTimePointId(), viewId.getViewSetupId() ); if ( !vd.isPresent() || vd.getTimePointId() != timepoint.getId() || vd.getViewSetup().getChannel().getId() != channel.getId() ) continue; // get the most recent model spimData.getViewRegistrations().getViewRegistration( viewId ).updateModel(); inputData.add( vd ); } return inputData; } public static < T extends RealType< T > > float[] minMax( final RandomAccessibleInterval< T > img ) { final IterableInterval< T > iterable = Views.iterable( img ); // split up into many parts for multithreading final Vector< ImagePortion > portions = FusionHelper.divideIntoPortions( iterable.size(), Threads.numThreads() * 2 ); // set up executor service final ExecutorService taskExecutor = Executors.newFixedThreadPool( Threads.numThreads() ); final ArrayList< Callable< float[] > > tasks = new ArrayList< Callable< float[] > >(); for ( final ImagePortion portion : portions ) { tasks.add( new Callable< float[] >() { @Override public float[] call() throws Exception { float min = Float.MAX_VALUE; float max = -Float.MAX_VALUE; final Cursor< T > c = iterable.cursor(); c.jumpFwd( portion.getStartPosition() ); for ( long j = 0; j < portion.getLoopSize(); ++j ) { final float v = c.next().getRealFloat(); min = Math.min( min, v ); max = Math.max( max, v ); } // min & max of this portion return new float[]{ min, max }; } }); } // run threads and combine results float min = Float.MAX_VALUE; float max = -Float.MAX_VALUE; try { // invokeAll() returns when all tasks are complete final List< Future< float[] > > futures = taskExecutor.invokeAll( tasks ); for ( final Future< float[] > future : futures ) { final float[] minmax = future.get(); min = Math.min( min, minmax[ 0 ] ); max = Math.max( max, minmax[ 1 ] ); } } catch ( final Exception e ) { IOFunctions.println( "Failed to compute min/max: " + e ); e.printStackTrace(); return null; } taskExecutor.shutdown(); return new float[]{ min, max }; } /** * Normalizes the image to the range [0...1] * * @param img - the image to normalize * @return - normalized array */ public static boolean normalizeImage( final RandomAccessibleInterval< FloatType > img ) { final float minmax[] = minMax( img ); final float min = minmax[ 0 ]; final float max = minmax[ 1 ]; return normalizeImage( img, min, max ); } /** * Normalizes the image to the range [0...1] * * @param img - the image to normalize * @param min - min value * @param max - max value * @return - normalized array */ public static boolean normalizeImage( final RandomAccessibleInterval< FloatType > img, final float min, final float max ) { final float diff = max - min; if ( Float.isNaN( diff ) || Float.isInfinite(diff) || diff == 0 ) { IOFunctions.println( "Cannot normalize image, min=" + min + " + max=" + max ); return false; } final IterableInterval< FloatType > iterable = Views.iterable( img ); // split up into many parts for multithreading final Vector< ImagePortion > portions = FusionHelper.divideIntoPortions( iterable.size(), Threads.numThreads() * 2 ); // 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< FloatType > c = iterable.cursor(); c.jumpFwd( portion.getStartPosition() ); for ( long j = 0; j < portion.getLoopSize(); ++j ) { final FloatType t = c.next(); final float norm = ( t.get() - min ) / diff; t.set( norm ); } return ""; } }); } try { // invokeAll() returns when all tasks are complete taskExecutor.invokeAll( tasks ); } catch ( final InterruptedException e ) { IOFunctions.println( "Failed to compute min/max: " + e ); e.printStackTrace(); return false; } taskExecutor.shutdown(); return true; } public static final Vector<ImagePortion> divideIntoPortions( final long imageSize, final int numPortions ) { final long threadChunkSize = imageSize / numPortions; final long threadChunkMod = imageSize % numPortions; final Vector<ImagePortion> portions = new Vector<ImagePortion>(); for ( int portionID = 0; portionID < numPortions; ++portionID ) { // move to the starting position of the current thread final long startPosition = portionID * threadChunkSize; // the last thread may has to run longer if the number of pixels cannot be divided by the number of threads final long loopSize; if ( portionID == numPortions - 1 ) loopSize = threadChunkSize + threadChunkMod; else loopSize = threadChunkSize; portions.add( new ImagePortion( startPosition, loopSize ) ); } return portions; } }