diff --git a/src/main/java/fr/pasteur/ida/zellige/steps/selection/util/Binning.java b/src/main/java/fr/pasteur/ida/zellige/steps/selection/util/Binning.java index ae6be221fe734d110c3142861ce58d74b7f242d4..97b1e5e60e9c2c64cccb8b345ab3a766c0baecdc 100644 --- a/src/main/java/fr/pasteur/ida/zellige/steps/selection/util/Binning.java +++ b/src/main/java/fr/pasteur/ida/zellige/steps/selection/util/Binning.java @@ -1,17 +1,14 @@ package fr.pasteur.ida.zellige.steps.selection.util; -import io.scif.img.ImgOpener; +import io.scif.img.IO; import net.imagej.ImageJ; -import net.imglib2.Cursor; -import net.imglib2.RandomAccess; -import net.imglib2.RandomAccessibleInterval; +import net.imglib2.*; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; -import net.imglib2.type.numeric.real.FloatType; -import net.imglib2.view.IntervalView; +import net.imglib2.util.Util; import net.imglib2.view.Views; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,9 +20,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static fr.pasteur.ida.zellige.steps.Utils.setPosition; -import static fr.pasteur.ida.zellige.steps.Utils.setPositionAndGet; -import static fr.pasteur.ida.zellige.steps.selection.pretreatment.PretreatmentParameters.getBin; @SuppressWarnings( "unchecked" ) public class Binning< T extends RealType< T > & NativeType< T > > @@ -42,44 +36,56 @@ public class Binning< T extends RealType< T > & NativeType< T > > /** - * Reduces the size of a stack by binning groups of pixels of user-specified square sizes (bin). The resulting pixel is obtained by taking the upper left pixel of the group. - * The image is rescaled if the bin value is not a multiple of the image dimensions by adding lines and/or columns. The process is parallelized according to the number of available processors (1/4). + * Reduces the size of a stack by binning groups of pixels of user-specified square sizes (bin). The resulting pixel + * is obtained by taking the upper left pixel of the group. + * The process is parallelized according to the number of available processors (1/4). * - * @param <T> the input type - * @param input the image to binned - * @param bin the bin size + * @param <T> the input type + * @param input the image to binned + * @param bin the bin size * @return the binned input image */ - public static < T extends RealType< T > & NativeType< T > > Img< T > binning( Img <T> input, int bin ) + public static < T extends RealType< T > & NativeType< T > > Img< T > + run( final RandomAccessibleInterval< T > input, int bin ) { - if (bin == 1) + if ( bin == 1 ) { - return input; + return null; } LOGGER.debug( "Staring process...." ); double startTime = System.currentTimeMillis(); - Binning< T > binning = new Binning<>( resize( input, input.factory(), bin ), bin ); - final int threads = Runtime.getRuntime().availableProcessors() / 4; //TODO Optimize the number of threads + final int threads = Runtime.getRuntime().availableProcessors() / 2; LOGGER.info( "Numbers of processors: {}", threads ); - final List< Integer > chunks = new ArrayList<>(); - for ( int i = 0; i < input.dimension( 2 ); i++ ) + int nbSlices = ( int ) input.dimension( input.numDimensions() - 1 ); + + Binning< T > binning = new Binning<>( input, bin ); + final List< Integer > chunks = new ArrayList<>( nbSlices ); + for ( int i = 0; i < nbSlices; i++ ) { chunks.add( i ); // size equals to number of slices } final List< Runnable > runnables = chunks.stream() - .map( chunk -> new Binning2D<>( Views.hyperSlice( input, 2, chunk ), Views.hyperSlice( binning.getOutput(), 2, chunk ), bin ) ) + .map( chunk -> new Binning2D<>( + Views.hyperSlice( input, 2, chunk ), + Views.hyperSlice( binning.getOutput(), 2, chunk ), + bin ) ) .collect( Collectors.toList() ); final ExecutorService executorService = Executors.newFixedThreadPool( threads ); runnables.forEach( executorService::submit ); + for ( final Runnable r : runnables ) + { + executorService.submit( r ); + LOGGER.debug( "One task executed" ); + } executorService.shutdown(); try { if ( executorService.awaitTermination( 300, TimeUnit.SECONDS ) ) { double endTime = System.currentTimeMillis(); - LOGGER.debug( "Image pretreated with a binning of {} by {}.", getBin(), getBin() ); - LOGGER.debug( "Process completed in {}s" , (endTime-startTime)/1000); + LOGGER.debug( "Image pretreated with a binning of {} by {}.", bin, bin ); + LOGGER.debug( "Process completed in {}s", ( endTime - startTime ) / 1000 ); return binning.getOutput(); } else @@ -90,35 +96,46 @@ public class Binning< T extends RealType< T > & NativeType< T > > } catch ( InterruptedException e ) { - throw new RuntimeException( e ); } - } + public static int nb = 0; + + + /** * @param input the image to binned * @param bin the bin size */ - public Binning( Img< T > input, int bin ) + public Binning( RandomAccessibleInterval< T > input, int bin ) { - int[] dimensions = new int[]{ - // X and Y dimensions are divided by the bin value, the other dimensions are the same - ( int ) input.dimension( 0 ) / ( bin ), + nb++; + int x = ( int ) input.dimension( 0 ) / ( bin ); + int y = ( int ) input.dimension( 1 ) / ( bin ); + int z = ( int ) input.dimension( 2 ); + + // X and Y dimensions are divided by the bin value, the other dimensions are the same + + //output = input.factory().create( dimensions ); + ImgFactory< T > factory = Util.getSuitableImgFactory( new FinalDimensions( ( int ) input.dimension( 0 ) / ( bin ), ( int ) input.dimension( 1 ) / ( bin ), - ( int ) input.dimension( 2 ) }; - output = input.factory().create( dimensions ); + ( int ) input.dimension( 2 ) ), input.getType() ); + output = factory.create( x, y, z ); } + /** + * Static class * Bins a 2D image. + * * @param <T> the image type */ - private static final class Binning2D< T extends RealType< T > & NativeType< T > > implements Runnable + private static class Binning2D< T extends RealType< T > & NativeType< T > > implements Runnable { - private final IntervalView< T > inputSlice; - private final IntervalView< T > outputSlice; + private final RandomAccessibleInterval< T > inputSlice; + private final RandomAccessibleInterval< T > outputSlice; private final int bin; /** @@ -128,93 +145,54 @@ public class Binning< T extends RealType< T > & NativeType< T > > * @param outputSlice the output binned image * @param bin the bin size */ - public Binning2D( IntervalView< T > inputSlice, IntervalView< T > outputSlice, int bin ) + public Binning2D( RandomAccessibleInterval< T > inputSlice, RandomAccessibleInterval< T > outputSlice, int bin ) { this.inputSlice = inputSlice; this.outputSlice = outputSlice; this.bin = bin; } - /** * Binning process. */ @Override public void run() { - RandomAccess< T > ra = inputSlice.randomAccess(); - Cursor< T > c = outputSlice.cursor(); - - for ( int y = 0; y < inputSlice.dimension( 1 ); y = y + bin ) - + try { - for ( int x = 0; x < inputSlice.dimension( 0 ); x = x + bin ) - { - c.fwd(); - c.get().set( setPositionAndGet( ra, x, y ) ); - } - } - LOGGER.info( "Binning : Section processed" ); - } - } - - - /** - * Rescales a 3D image according to the bin value. - * - * @param <T> the input type - * @param input the image to binned - * @param factory the image factory - * @param bin the bin size - * @return the input or the rescaled input if necessary - */ - public static < T extends RealType< T > & NativeType< T > > Img< T > resize( RandomAccessibleInterval< T > input, ImgFactory< T > factory, int bin ) - { - Img< T > output; - int moduloX = ( int ) ( input.dimension( 0 ) % bin ); - int moduloY = ( int ) ( input.dimension( 1 ) % bin ); + LOGGER.debug( "Starting to bin a section !" ); - if ( moduloX == 0 && moduloY == 0 ) - { - output = factory.create(input); - return output; - } - else - { - int dimX = ( int ) input.dimension( 0 ) + ( bin - moduloX ); - int dimY = ( int ) input.dimension( 1 ) + ( bin - moduloY ); - output = factory.create( dimX, dimY, input.dimension( input.numDimensions() - 1 ) ); - RandomAccess< T > inputAccess = input.randomAccess(); - RandomAccess< T > outputAccess = input.randomAccess(); - for ( int z = 0; z < input.dimension( input.numDimensions() - 1 ) ; z++ ) - { - for ( int y = 0; y < input.dimension( 1 ); y++ ) + RandomAccess< T > ra = inputSlice.randomAccess( inputSlice ); + RandomAccess< T > raOut = outputSlice.randomAccess(); + for ( int y = 0; y < inputSlice.dimension( 1 ); y = y + bin ) { - for ( int x = 0; x < input.dimension( 0 ); x++ ) + for ( int x = 0; x < inputSlice.dimension( 0 ); x = x + bin ) { - setPosition( outputAccess, x, y, z ); - outputAccess.get().set( setPositionAndGet( inputAccess, x, y, z ) ); + raOut.setPositionAndGet( x / bin, y / bin ).set( ra.setPositionAndGet( x, y ) ); } } - LOGGER.info( " The input image has been rescaled to fit the binning process." ); + LOGGER.info( "Section processed" ); + } + catch ( Exception e ) + { + throw new RuntimeException( e ); } - return output; } - } - public static void main( String[] args ) + + public static < T extends RealType< T > & NativeType< T > > void main( String[] args ) throws InterruptedException { ImageJ ij = new ImageJ(); ij.launch( args ); - final ImgOpener io = new ImgOpener(); - final Img< FloatType > kernel = ( Img< FloatType > ) io.openImgs( "doc/Scan1_volume_crop.tif" ).get( 0 ); - ImageJFunctions.show( kernel, "original" ); - - Img< FloatType > output = Binning.binning( kernel, 7 ); +// Thread.sleep( 20000 ); + //final ImgOpener io = new ImgOpener(); + // final Img< FloatType > kernel = ( Img< FloatType > ) io.openImgs( "doc/Scan1_volume.tif" ).get( 0 ); + final Img< T > stackImage = ( Img< T > ) IO.openAll( "doc/Scan1_volume_crop.tif" ).get( 0 ).getImg(); // TODO Handling larger images +// ImageJFunctions.show( stackImage, "original" ); + Img< T > output = Binning.run( stackImage, 2 ); assert output != null; - ImageJFunctions.show( output, "output " ); - + ImageJFunctions.show(output, "binned"); } }