diff --git a/src/main/java/net/imglib2/algorithm/binary/Thresholder.java b/src/main/java/net/imglib2/algorithm/binary/Thresholder.java index 2e14f7ee6..2d3df79cb 100644 --- a/src/main/java/net/imglib2/algorithm/binary/Thresholder.java +++ b/src/main/java/net/imglib2/algorithm/binary/Thresholder.java @@ -33,16 +33,12 @@ */ package net.imglib2.algorithm.binary; -import java.util.Vector; +import java.util.function.BiConsumer; -import net.imglib2.Cursor; -import net.imglib2.RandomAccess; -import net.imglib2.converter.Converter; -import net.imglib2.exception.IncompatibleTypeException; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; -import net.imglib2.multithreading.Chunk; -import net.imglib2.multithreading.SimpleMultiThreading; +import net.imglib2.loops.LoopBuilder; +import net.imglib2.parallel.Parallelization; import net.imglib2.type.Type; import net.imglib2.type.logic.BitType; @@ -73,95 +69,39 @@ public class Thresholder */ public static final < T extends Type< T > & Comparable< T >> Img< BitType > threshold( final Img< T > source, final T threshold, final boolean above, final int numThreads ) { - final ImgFactory< T > factory = source.factory(); - try - { - final ImgFactory< BitType > bitFactory = factory.imgFactory( new BitType() ); - final Img< BitType > target = bitFactory.create( source ); - - final Converter< T, BitType > converter; - if ( above ) - { - converter = new Converter< T, BitType >() - { - @Override - public void convert( final T input, final BitType output ) - { - output.set( input.compareTo( threshold ) > 0 ); - } - }; - } - else - { - converter = new Converter< T, BitType >() - { - @Override - public void convert( final T input, final BitType output ) - { - output.set( input.compareTo( threshold ) < 0 ); - } - }; - } - - final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads ); - final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + return Parallelization.runWithNumThreads( numThreads, + () -> threshold( source, threshold, above ) ); + } - if ( target.iterationOrder().equals( source.iterationOrder() ) ) - { - for ( int i = 0; i < threads.length; i++ ) - { - final Chunk chunk = chunks.get( i ); - threads[ i ] = new Thread( "Thresholder thread " + i ) - { - @Override - public void run() - { - final Cursor< BitType > cursorTarget = target.cursor(); - cursorTarget.jumpFwd( chunk.getStartPosition() ); - final Cursor< T > cursorSource = source.cursor(); - cursorSource.jumpFwd( chunk.getStartPosition() ); - for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) - { - cursorTarget.fwd(); - cursorSource.fwd(); - converter.convert( cursorSource.get(), cursorTarget.get() ); - } - } - }; - } - } - else - { - for ( int i = 0; i < threads.length; i++ ) - { - final Chunk chunk = chunks.get( i ); - threads[ i ] = new Thread( "Thresholder thread " + i ) - { - @Override - public void run() - { - final Cursor< BitType > cursorTarget = target.cursor(); - cursorTarget.jumpFwd( chunk.getStartPosition() ); - final RandomAccess< T > ra = source.randomAccess( target ); - for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) - { - cursorTarget.fwd(); - ra.setPosition( cursorTarget ); - converter.convert( ra.get(), cursorTarget.get() ); - } - } - }; - } - } + /** + * Returns a new boolean {@link Img} generated by thresholding the values of + * the source image. + * + * @param source + * the image to threshold. + * @param threshold + * the threshold. + * @param above + * if {@code true}, the target value will be true for source + * values above the threshold, {@code false} otherwise. + * @return a new {@link Img} of type {@link BitType} and of same dimension + * that the source image. + */ + public static < T extends Type< T > & Comparable< T > > Img< BitType > threshold( Img< T > source, T threshold, boolean above ) + { + final ImgFactory< BitType > factory = source.factory().imgFactory( new BitType() ); + final Img< BitType > target = factory.create( source ); + final BiConsumer< T, BitType > converter = getThresholdConverter( threshold, above ); + LoopBuilder.setImages( source, target ).multiThreaded().forEachPixel( converter ); + return target; + } - SimpleMultiThreading.startAndJoin( threads ); - return target; - } - catch ( final IncompatibleTypeException e ) - { - e.printStackTrace(); - return null; - } + private static < T extends Type< T > & Comparable< T > > BiConsumer< T, BitType > getThresholdConverter( T threshold, boolean above ) + { + if ( above ) + return ( input, output ) -> output.set( input.compareTo( threshold ) > 0 ); + else + return ( input, output ) -> output.set( input.compareTo( threshold ) < 0 ); } } diff --git a/src/main/java/net/imglib2/algorithm/dog/DifferenceOfGaussian.java b/src/main/java/net/imglib2/algorithm/dog/DifferenceOfGaussian.java index ec163a7d9..9d3eaa9db 100644 --- a/src/main/java/net/imglib2/algorithm/dog/DifferenceOfGaussian.java +++ b/src/main/java/net/imglib2/algorithm/dog/DifferenceOfGaussian.java @@ -33,26 +33,20 @@ */ package net.imglib2.algorithm.dog; -import java.util.ArrayList; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; - -import net.imglib2.Cursor; -import net.imglib2.IterableInterval; -import net.imglib2.RandomAccess; import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; import net.imglib2.algorithm.gauss3.Gauss3; -import net.imglib2.exception.IncompatibleTypeException; import net.imglib2.img.Img; +import net.imglib2.loops.LoopBuilder; +import net.imglib2.parallel.Parallelization; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.NumericType; import net.imglib2.util.Util; import net.imglib2.view.IntervalView; import net.imglib2.view.Views; +import java.util.concurrent.ExecutorService; + /** * Compute Difference-of-Gaussian of a {@link RandomAccessible}. * @@ -66,7 +60,7 @@ public class DifferenceOfGaussian * of sigmaLarger (where {@code sigmaLarger > sigmaSmaller}). *
* Creates an appropriate temporary image and calls - * {@link #DoG(double[], double[], RandomAccessible, RandomAccessible, RandomAccessibleInterval, ExecutorService)} + * {@link #DoG(double[], double[], RandomAccessible, RandomAccessible, RandomAccessibleInterval)} * . *
* @@ -80,21 +74,18 @@ public class DifferenceOfGaussian * convolution). * @param dog * the Difference-of-Gaussian result image. - * @param service - * service providing threads for multi-threading */ public static < I extends NumericType< I >, T extends NumericType< T > & NativeType< T > > void DoG( final double[] sigmaSmaller, final double[] sigmaLarger, final RandomAccessible< I > input, - final RandomAccessibleInterval< T > dog, - final ExecutorService service ) + final RandomAccessibleInterval< T > dog ) { final T type = Util.getTypeFromInterval( dog ); final Img< T > g1 = Util.getArrayOrCellImgFactory( dog, type ).create( dog ); final long[] translation = new long[ dog.numDimensions() ]; dog.min( translation ); - DoG( sigmaSmaller, sigmaLarger, input, Views.translate( g1, translation ), dog, service ); + DoG( sigmaSmaller, sigmaLarger, input, Views.translate( g1, translation ), dog ); } /** @@ -115,88 +106,59 @@ public static < I extends NumericType< I >, T extends NumericType< T > & NativeT * dog result image. * @param dog * the Difference-of-Gaussian result image. - * @param service - * how many threads to use for the computation. */ public static < I extends NumericType< I >, T extends NumericType< T > & NativeType< T > > void DoG( final double[] sigmaSmaller, final double[] sigmaLarger, final RandomAccessible< I > input, final RandomAccessible< T > tmp, + final RandomAccessibleInterval< T > dog ) + { + final IntervalView< T > tmpInterval = Views.interval( tmp, dog ); + Gauss3.gauss( sigmaSmaller, input, tmpInterval ); + Gauss3.gauss( sigmaLarger, input, dog ); + LoopBuilder.setImages( dog, tmpInterval ).multiThreaded().forEachPixel( ( d, t ) -> d.sub( t ) ); + } + + /** + * @deprecated Please use: + * + *+ * {@code Parallelization.withExecutor( service ).run( () -> DoG( sigmaSmaller, sigmaLarger, input, dog ) )} + * + * @see Parallelization + */ + public static < I extends NumericType< I >, T extends NumericType< T > & NativeType< T > > void DoG( + final double[] sigmaSmaller, + final double[] sigmaLarger, + final RandomAccessible< I > input, final RandomAccessibleInterval< T > dog, final ExecutorService service ) { - final IntervalView< T > tmpInterval = Views.interval( tmp, dog ); - try - { - Gauss3.gauss( sigmaSmaller, input, tmpInterval, service ); - Gauss3.gauss( sigmaLarger, input, dog, service ); - } - catch ( final IncompatibleTypeException e ) - { - e.printStackTrace(); - } - final IterableInterval< T > dogIterable = Views.iterable( dog ); - final IterableInterval< T > tmpIterable = Views.iterable( tmpInterval ); - final long size = dogIterable.size(); - // FIXME find better heuristic? - final int numThreads = Runtime.getRuntime().availableProcessors(); - final int numTasks = numThreads <= 1 ? 1 : numThreads * 20; - final long taskSize = size / numTasks; - final ArrayList< Future< Void > > futures = new ArrayList<>(); - for ( int taskNum = 0; taskNum < numTasks; ++taskNum ) - { - final long fromIndex = taskNum * taskSize; - final long thisTaskSize = ( taskNum == numTasks - 1 ) ? size - fromIndex : taskSize; - if ( dogIterable.iterationOrder().equals( tmpIterable.iterationOrder() ) ) - futures.add( service.submit( new Callable< Void >() - { - @Override - public Void call() - { - final Cursor< T > dogCursor = dogIterable.cursor(); - final Cursor< T > tmpCursor = tmpIterable.cursor(); - dogCursor.jumpFwd( fromIndex ); - tmpCursor.jumpFwd( fromIndex ); - for ( int i = 0; i < thisTaskSize; ++i ) - dogCursor.next().sub( tmpCursor.next() ); - return null; - } - } ) ); - else - futures.add( service.submit( new Callable< Void >() - { - @Override - public Void call() - { - final Cursor< T > dogCursor = dogIterable.localizingCursor(); - final RandomAccess< T > tmpAccess = tmpInterval.randomAccess(); - dogCursor.jumpFwd( fromIndex ); - for ( int i = 0; i < thisTaskSize; ++i ) - { - final T o = dogCursor.next(); - tmpAccess.setPosition( dogCursor ); - o.sub( tmpAccess.get() ); - } - return null; - } - } ) ); - } - for ( final Future< Void > f : futures ) - { - try - { - f.get(); - } - catch ( final InterruptedException e ) - { - e.printStackTrace(); - } - catch ( final ExecutionException e ) - { - e.printStackTrace(); - } - } + Parallelization.runWithExecutor( service, + () -> DoG( sigmaSmaller, sigmaLarger, input, dog ) + ); + } + + /** + * @deprecated Please use: + * + *
+ * {@code Parallelization.withExecutor( service ). run( () -> DoG( sigmaSmaller, sigmaLarger, input, tmp, dog ) ); } + * + * @see Parallelization + */ + public static < I extends NumericType< I >, T extends NumericType< T > & NativeType< T > > void DoG( + final double[] sigmaSmaller, + final double[] sigmaLarger, + final RandomAccessible< I > input, + final RandomAccessible< T > tmp, + final RandomAccessibleInterval< T > dog, + final ExecutorService service ) + { + Parallelization.runWithExecutor( service, + () -> DoG( sigmaSmaller, sigmaLarger, input, tmp, dog ) + ); } /** diff --git a/src/main/java/net/imglib2/algorithm/gradient/PartialDerivative.java b/src/main/java/net/imglib2/algorithm/gradient/PartialDerivative.java index 343ea5da5..e4a1d3188 100644 --- a/src/main/java/net/imglib2/algorithm/gradient/PartialDerivative.java +++ b/src/main/java/net/imglib2/algorithm/gradient/PartialDerivative.java @@ -11,13 +11,13 @@ * %% * 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 @@ -34,21 +34,16 @@ package net.imglib2.algorithm.gradient; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import net.imglib2.Cursor; -import net.imglib2.FinalInterval; import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; import net.imglib2.loops.LoopBuilder; +import net.imglib2.parallel.TaskExecutor; +import net.imglib2.parallel.Parallelization; +import net.imglib2.parallel.TaskExecutors; import net.imglib2.type.numeric.NumericType; import net.imglib2.util.Intervals; -import net.imglib2.view.IntervalView; import net.imglib2.view.Views; /** @@ -62,105 +57,67 @@ public class PartialDerivative { // nice version... /** - * Compute the partial derivative (central difference approximation) of source - * in a particular dimension: - * {@code d_f( x ) = ( f( x + e ) - f( x - e ) ) / 2}, - * where {@code e} is the unit vector along that dimension. - * - * @param source - * source image, has to provide valid data in the interval of the - * gradient image plus a one pixel border in dimension. - * @param gradient - * output image - * @param dimension - * along which dimension the partial derivatives are computed + * @deprecated + * Use {@link #gradientCentralDifference(RandomAccessible, RandomAccessibleInterval, int)} + * instead. */ + @Deprecated public static < T extends NumericType< T > > void gradientCentralDifference2( final RandomAccessible< T > source, final RandomAccessibleInterval< T > gradient, final int dimension ) { - final Cursor< T > front = Views.flatIterable( Views.interval( source, Intervals.translate( gradient, 1, dimension ) ) ).cursor(); - final Cursor< T > back = Views.flatIterable( Views.interval( source, Intervals.translate( gradient, -1, dimension ) ) ).cursor(); - - for ( final T t : Views.flatIterable( gradient ) ) - { - t.set( front.next() ); - t.sub( back.next() ); - t.mul( 0.5 ); - } + gradientCentralDifference( source, gradient, dimension ); } // parallel version... /** - * Compute the partial derivative (central difference approximation) of source - * in a particular dimension: - * {@code d_f( x ) = ( f( x + e ) - f( x - e ) ) / 2}, - * where {@code e} is the unit vector along that dimension. - * - * @param source - * source image, has to provide valid data in the interval of the - * gradient image plus a one pixel border in dimension. - * @param gradient - * output image - * @param dimension - * along which dimension the partial derivatives are computed - * @param nTasks - * Number of tasks for gradient computation. - * @param es - * {@link ExecutorService} providing workers for gradient - * computation. Service is managed (created, shutdown) by caller. + * @deprecated + * Use {@link #gradientCentralDifference(RandomAccessible, RandomAccessibleInterval, int)} + * instead. + *
+ * Read {@link Parallelization} to learn how to run the method multi-threaded. Here is an example: + *
+ *
+ * {@code + * TaskExecutor taskExecutor = TaskExecutors.forExecutorServiceAndNumTasks( executorService, numTasks ); + * Parallelization.runWithExecutor( taskExecutor, () -> { + * gradientCentralDerivativeParallel( source, result, dimension ); + * } ); + * } + **/ + @Deprecated public static < T extends NumericType< T > > void gradientCentralDifferenceParallel( final RandomAccessible< T > source, - final RandomAccessibleInterval< T > gradient, + final RandomAccessibleInterval< T > result, final int dimension, final int nTasks, - final ExecutorService es ) throws InterruptedException, ExecutionException + final ExecutorService es ) { - final int nDim = source.numDimensions(); - if ( nDim < 2 ) - { - gradientCentralDifference( source, gradient, dimension ); - return; - } - - long dimensionMax = Long.MIN_VALUE; - int dimensionArgMax = -1; - - for ( int d = 0; d < nDim; ++d ) - { - final long size = gradient.dimension( d ); - if ( d != dimension && size > dimensionMax ) - { - dimensionMax = size; - dimensionArgMax = d; - } - } - - final long stepSize = Math.max( dimensionMax / nTasks, 1 ); - final long stepSizeMinusOne = stepSize - 1; - final long min = gradient.min( dimensionArgMax ); - final long max = gradient.max( dimensionArgMax ); - - final ArrayList< Callable< Void > > tasks = new ArrayList<>(); - for ( long currentMin = min, minZeroBase = 0; minZeroBase < dimensionMax; currentMin += stepSize, minZeroBase += stepSize ) - { - final long currentMax = Math.min( currentMin + stepSizeMinusOne, max ); - final long[] mins = new long[ nDim ]; - final long[] maxs = new long[ nDim ]; - gradient.min( mins ); - gradient.max( maxs ); - mins[ dimensionArgMax ] = currentMin; - maxs[ dimensionArgMax ] = currentMax; - final IntervalView< T > currentInterval = Views.interval( gradient, new FinalInterval( mins, maxs ) ); - tasks.add( () -> { - gradientCentralDifference( source, currentInterval, dimension ); - return null; - } ); - } - - final List< Future< Void > > futures = es.invokeAll( tasks ); + TaskExecutor taskExecutor = TaskExecutors.forExecutorServiceAndNumTasks( es, nTasks ); + Parallelization.runWithExecutor( taskExecutor, () -> { + gradientCentralDerivativeParallel( source, result, dimension ); + } ); + } - for ( final Future< Void > f : futures ) - f.get(); + /** + * @deprecated + * Use {@link #gradientCentralDifference(RandomAccessible, RandomAccessibleInterval, int)} + * instead. + *
+ * Read {@link Parallelization} to learn how to run the method multi-threaded. Here is an example: + *
+ *
+ * {@code + * TaskExecutor taskExecutor = TaskExecutors.forExecutorServiceAndNumTasks( executorService, numTasks ); + * Parallelization.runWithExecutor( taskExecutor, () -> { + * gradientCentralDerivativeParallel( source, result, dimension ); + * } ); + * } + *+ */ + private static
* Note, that the {@code output} image must be cleared to 0! - *
* * @param input * input image with pixels > 0 belonging to foreground. @@ -170,19 +165,20 @@ public static < T extends IntegerType< T >, L, I extends IntegerType< I > > void * @param se * structuring element to use. 8-connected or 4-connected * (respectively n-dimensional analog) + * @param service + * service providing threads for multi-threading * @return the number of connected components (that is, the highest value * occurring in the output image. */ public static < T extends IntegerType< T >, L extends IntegerType< L > > int labelAllConnectedComponents( final RandomAccessible< T > input, final RandomAccessibleInterval< L > output, - final StructuringElement se ) + final StructuringElement se, + final ExecutorService service ) { - final int numThreads = Runtime.getRuntime().availableProcessors(); - final ExecutorService service = Executors.newFixedThreadPool( numThreads ); - final int result = labelAllConnectedComponents( input, output, se, service ); - service.shutdown(); - return result; + return Parallelization.runWithExecutor( service, + () -> labelAllConnectedComponents( input, output, se ) + ); } /** @@ -192,6 +188,7 @@ public static < T extends IntegerType< T >, L extends IntegerType< L > > int lab * ** Note, that the {@code output} image must be cleared to 0! + *
* * @param input * input image with pixels > 0 belonging to foreground. @@ -200,16 +197,13 @@ public static < T extends IntegerType< T >, L extends IntegerType< L > > int lab * @param se * structuring element to use. 8-connected or 4-connected * (respectively n-dimensional analog) - * @param service - * service providing threads for multi-threading * @return the number of connected components (that is, the highest value * occurring in the output image. */ public static < T extends IntegerType< T >, L extends IntegerType< L > > int labelAllConnectedComponents( final RandomAccessible< T > input, final RandomAccessibleInterval< L > output, - final StructuringElement se, - final ExecutorService service ) + final StructuringElement se ) { final int n = output.numDimensions(); final int splitDim = n - 1; @@ -234,37 +228,14 @@ public static < T extends IntegerType< T >, L extends IntegerType< L > > int lab min[ splitDim ] += taskSize; } - final ArrayList< Future< ? > > futures = new ArrayList< Future< ? > >(); - for ( final Fragment< T, L > fragment : fragments ) - { - futures.add( service.submit( new Runnable() - { - @Override - public void run() - { - fragment.mark(); - } - } ) ); - } - getAllFutures( futures ); + Parallelization.getTaskExecutor().forEach( Arrays.asList( fragments ), Fragment::mark ); final TIntArrayList merged = mergeCanonicalLists( fragments ); for ( int i = 1; i < numTasks; ++i ) fragments[ i ].linkToPreviousFragment( fragments[ i - 1 ], merged ); final int numComponents = splitCanonicalLists( fragments, merged ); - for ( final Fragment< T, L > fragment : fragments ) - { - futures.add( service.submit( new Runnable() - { - @Override - public void run() - { - fragment.relabel(); - } - } ) ); - } - getAllFutures( futures ); + Parallelization.getTaskExecutor().forEach( Arrays.asList( fragments ), Fragment::relabel ); return numComponents; } @@ -481,26 +452,6 @@ private static < T extends IntegerType< T >, L extends IntegerType< L > > int sp return nextLabel - 1; } - private static void getAllFutures( final List< Future< ? > > futures ) - { - for ( final Future< ? > future : futures ) - { - try - { - future.get(); - } - catch ( final InterruptedException e ) - { - e.printStackTrace(); - } - catch ( final ExecutionException e ) - { - e.printStackTrace(); - } - } - futures.clear(); - } - private static interface CollectNeighborLabels< L extends IntegerType< L > > { public void collect( RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ); diff --git a/src/main/java/net/imglib2/algorithm/linalg/eigen/TensorEigenValues.java b/src/main/java/net/imglib2/algorithm/linalg/eigen/TensorEigenValues.java index 8d3ed143a..3cfc45e20 100644 --- a/src/main/java/net/imglib2/algorithm/linalg/eigen/TensorEigenValues.java +++ b/src/main/java/net/imglib2/algorithm/linalg/eigen/TensorEigenValues.java @@ -34,24 +34,19 @@ package net.imglib2.algorithm.linalg.eigen; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import net.imglib2.Cursor; -import net.imglib2.FinalInterval; import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; +import net.imglib2.loops.LoopBuilder; +import net.imglib2.parallel.Parallelization; +import net.imglib2.parallel.TaskExecutors; import net.imglib2.type.numeric.ComplexType; import net.imglib2.type.numeric.RealType; -import net.imglib2.view.IntervalView; import net.imglib2.view.Views; -import net.imglib2.view.composite.NumericComposite; -import net.imglib2.view.composite.RealComposite; +import net.imglib2.view.composite.CompositeIntervalView; +import net.imglib2.view.composite.GenericComposite; /** * @@ -286,71 +281,10 @@ public static < T extends RealType< T >, U extends ComplexType< U > > RandomAcce final ExecutorService es ) { - assert nTasks > 0: "Passed nTasks < 1"; - - final int tensorDims = tensor.numDimensions(); - - long dimensionMax = Long.MIN_VALUE; - int dimensionArgMax = -1; - - for ( int d = 0; d < tensorDims - 1; ++d ) - { - final long size = tensor.dimension( d ); - if ( size > dimensionMax ) - { - dimensionMax = size; - dimensionArgMax = d; - } - } - - final long stepSize = Math.max( dimensionMax / nTasks, 1 ); - final long stepSizeMinusOne = stepSize - 1; - final long max = dimensionMax - 1; - - final ArrayList< Callable< RandomAccessibleInterval< U > > > tasks = new ArrayList<>(); - for ( long currentMin = 0; currentMin < dimensionMax; currentMin += stepSize ) - { - final long currentMax = Math.min( currentMin + stepSizeMinusOne, max ); - final long[] minT = new long[ tensorDims ]; - final long[] maxT = new long[ tensorDims ]; - final long[] minE = new long[ tensorDims ]; - final long[] maxE = new long[ tensorDims ]; - tensor.min( minT ); - tensor.max( maxT ); - eigenvalues.min( minE ); - eigenvalues.max( maxE ); - minE[ dimensionArgMax ] = minT[ dimensionArgMax ] = currentMin; - maxE[ dimensionArgMax ] = maxT[ dimensionArgMax ] = currentMax; - final IntervalView< T > currentTensor = Views.interval( tensor, new FinalInterval( minT, maxT ) ); - final IntervalView< U > currentEigenvalues = Views.interval( eigenvalues, new FinalInterval( minE, maxE ) ); - tasks.add( () -> calculateEigenValuesImpl( currentTensor, currentEigenvalues, ev.copy() ) ); - } - - - try - { - final List< Future< RandomAccessibleInterval< U > > > futures = es.invokeAll( tasks ); - for ( final Future< RandomAccessibleInterval< U > > f : futures ) - try - { - f.get(); - } - catch ( final ExecutionException e ) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - catch ( final InterruptedException e ) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - return eigenvalues; - - + assert nTasks > 0 : "Passed nTasks < 1"; + return Parallelization.runWithExecutor( TaskExecutors.forExecutorServiceAndNumTasks( es, nTasks ), + () -> calculateEigenValues( tensor, eigenvalues, ev ) ); } private static < T extends RealType< T >, U extends ComplexType< U > > RandomAccessibleInterval< U > calculateEigenValuesImpl( @@ -358,10 +292,15 @@ private static < T extends RealType< T >, U extends ComplexType< U > > RandomAcc final RandomAccessibleInterval< U > eigenvalues, final EigenValues< T, U > ev ) { - final Cursor< RealComposite< T > > m = Views.iterable( Views.collapseReal( tensor ) ).cursor(); - final Cursor< NumericComposite< U > > e = Views.iterable( Views.collapseNumeric( eigenvalues ) ).cursor(); - while ( m.hasNext() ) - ev.compute( m.next(), e.next() ); + RandomAccessibleInterval< ? extends GenericComposite< T > > tensorVectors = Views.collapse( tensor ); + CompositeIntervalView< U, ? extends GenericComposite< U > > eigenvaluesVectors = Views.collapse( eigenvalues ); + LoopBuilder.setImages( tensorVectors, eigenvaluesVectors ) + .multiThreaded() + .forEachChunk( chunk -> { + EigenValues< T, U > copy = ev.copy(); + chunk.forEachPixel( copy::compute ); + return null; + } ); return eigenvalues; } diff --git a/src/main/java/net/imglib2/algorithm/localextrema/LocalExtrema.java b/src/main/java/net/imglib2/algorithm/localextrema/LocalExtrema.java index f2b1bce5e..af8694fd3 100644 --- a/src/main/java/net/imglib2/algorithm/localextrema/LocalExtrema.java +++ b/src/main/java/net/imglib2/algorithm/localextrema/LocalExtrema.java @@ -35,6 +35,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -42,20 +43,22 @@ import java.util.concurrent.Future; import java.util.stream.IntStream; import java.util.stream.LongStream; - -import net.imglib2.Cursor; -import net.imglib2.FinalInterval; import net.imglib2.Interval; import net.imglib2.Localizable; import net.imglib2.Point; +import net.imglib2.RandomAccess; import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; import net.imglib2.Sampler; import net.imglib2.algorithm.neighborhood.Neighborhood; import net.imglib2.algorithm.neighborhood.RectangleShape; import net.imglib2.algorithm.neighborhood.Shape; +import net.imglib2.converter.readwrite.WriteConvertedRandomAccessible; +import net.imglib2.loops.LoopBuilder; +import net.imglib2.parallel.Parallelization; +import net.imglib2.parallel.TaskExecutor; +import net.imglib2.parallel.TaskExecutors; import net.imglib2.util.ConstantUtils; -import net.imglib2.util.Intervals; import net.imglib2.util.ValuePair; import net.imglib2.view.IntervalView; import net.imglib2.view.Views; @@ -306,7 +309,7 @@ public static < P, T > List< P > findLocalExtrema( * @param numTasks * Number of tasks for parallel execution * @param splitDim - * Dimension along which input should be split for parallization + * ignored * @return {@link List} of extrema * @throws ExecutionException * @throws InterruptedException @@ -320,38 +323,8 @@ public static < P, T > List< P > findLocalExtrema( final int numTasks, final int splitDim ) throws InterruptedException, ExecutionException { - - final long[] min = Intervals.minAsLongArray( interval ); - final long[] max = Intervals.maxAsLongArray( interval ); - - final long splitDimSize = interval.dimension( splitDim ); - final long splitDimMax = max[ splitDim ]; - final long splitDimMin = min[ splitDim ]; - final long taskSize = Math.max( splitDimSize / numTasks, 1 ); - - final ArrayList< Callable< List< P > > > tasks = new ArrayList<>(); - - for ( long start = splitDimMin, stop = splitDimMin + taskSize - 1; start <= splitDimMax; start += taskSize, stop += taskSize ) - { - final long s = start; - // need max here instead of dimension for constructor of - // FinalInterval - final long S = Math.min( stop, splitDimMax ); - tasks.add( () -> { - final long[] localMin = min.clone(); - final long[] localMax = max.clone(); - localMin[ splitDim ] = s; - localMax[ splitDim ] = S; - return findLocalExtrema( source, new FinalInterval( localMin, localMax ), localNeighborhoodCheck, shape ); - } ); - } - - final ArrayList< P > extrema = new ArrayList<>(); - final List< Future< List< P > > > futures = service.invokeAll( tasks ); - for ( final Future< List< P > > f : futures ) - extrema.addAll( f.get() ); - return extrema; - + TaskExecutor taskExecutor = TaskExecutors.forExecutorServiceAndNumTasks( service, numTasks ); + return Parallelization.runWithExecutor( taskExecutor, () -> findLocalExtrema( source, interval, localNeighborhoodCheck, shape ) ); } /** @@ -470,22 +443,30 @@ public static < P, T > List< P > findLocalExtrema( final LocalNeighborhoodCheck< P, T > localNeighborhoodCheck, final Shape shape ) { + WriteConvertedRandomAccessible< T, RandomAccess< T > > randomAccessible = new WriteConvertedRandomAccessible<>( source, sampler -> (RandomAccess< T >) sampler ); + RandomAccessibleInterval< RandomAccess< T > > centers = Views.interval( randomAccessible, interval); + RandomAccessibleInterval< Neighborhood< T > > neighborhoods = Views.interval( shape.neighborhoodsRandomAccessible( source ), interval ); + List< List< P > > extremas = LoopBuilder.setImages( centers, neighborhoods ).multiThreaded().forEachChunk( chunk -> { + List< P > extrema = new ArrayList<>(); + chunk.forEachPixel( ( center, neighborhood ) -> { + P p = localNeighborhoodCheck.check( center, neighborhood ); + if ( p != null ) + extrema.add( p ); + } ); + return extrema; + } ); + return concatenate( extremas ); + } - final IntervalView< T > sourceInterval = Views.interval( source, interval ); - - final ArrayList< P > extrema = new ArrayList<>(); - - final Cursor< T > center = Views.flatIterable( sourceInterval ).cursor(); - for ( final Neighborhood< T > neighborhood : shape.neighborhoods( sourceInterval ) ) - { - center.fwd(); - final P p = localNeighborhoodCheck.check( center, neighborhood ); - if ( p != null ) - extrema.add( p ); - } - - return extrema; - + private static < P > List concatenate( List
* A List {@link RefinedPeak} for the given list of {@link Localizable} is
* computed by, for each peak, fitting a quadratic function to the image and
@@ -69,7 +74,7 @@
* repeated at the corresponding integer coordinates. This is repeated to
* convergence, for a maximum number of iterations, or until the integer
* coordinates move out of the valid image.
- *
+ *
* @author Stephan Preibisch
* @author Tobias Pietzsch
*/
@@ -94,8 +99,6 @@ public SubpixelLocalization( final int numDimensions )
// principally one can move in any dimension
allowedToMoveInDim = new boolean[ numDimensions ];
Arrays.fill( allowedToMoveInDim, true );
-
- numThreads = Runtime.getRuntime().availableProcessors();
}
public void setAllowMaximaTolerance( final boolean allowMaximaTolerance )
@@ -165,14 +168,14 @@ public boolean getReturnInvalidPeaks()
public int getNumThreads()
{
- return numThreads;
+ return numThreads == 0 ? Parallelization.getTaskExecutor().getParallelism() : numThreads;
}
/**
* Refine a set of peaks to subpixel coordinates. Calls
* {@link #refinePeaks(List, RandomAccessible, Interval, boolean, int, boolean, float, boolean[], int)}
* with the parameters set to this object.
- *
+ *
* @param peaks
* List of integer peaks.
* @param img
@@ -185,7 +188,13 @@ public int getNumThreads()
*/
public ArrayList< RefinedPeak< P > > process( final List< P > peaks, final RandomAccessible< T > img, final Interval validInterval )
{
- return refinePeaks( peaks, img, validInterval, returnInvalidPeaks, maxNumMoves, allowMaximaTolerance, maximaTolerance, allowedToMoveInDim, numThreads );
+ if ( numThreads != 0 )
+ return Parallelization.runWithNumThreads( numThreads,
+ () -> refinePeaks( peaks, img, validInterval, returnInvalidPeaks, maxNumMoves,
+ allowMaximaTolerance, maximaTolerance, allowedToMoveInDim ) );
+ else
+ return refinePeaks( peaks, img, validInterval, returnInvalidPeaks, maxNumMoves,
+ allowMaximaTolerance, maximaTolerance, allowedToMoveInDim );
}
/**
@@ -198,7 +207,7 @@ public ArrayList< RefinedPeak< P > > process( final List< P > peaks, final Rando
* fit is repeated at the corresponding integer coordinates. This is
* repeated to convergence, for a maximum number of iterations, or until the
* integer coordinates move out of the valid image.
- *
+ *
* @param peaks
* List of integer peaks.
* @param img
@@ -232,21 +241,13 @@ public static < T extends RealType< T >, P extends Localizable > ArrayList< Refi
final int maxNumMoves, final boolean allowMaximaTolerance, final float maximaTolerance, final boolean[] allowedToMoveInDim,
final int numThreads )
{
- final int numPeaks = peaks.size();
- final int numTasks = numThreads <= 1 ? 1 : ( int ) Math.min( numPeaks, numThreads * 20 );
- final ExecutorService ex = Executors.newFixedThreadPool( numThreads );
-
- // run
- final ArrayList< RefinedPeak< P > > allRefinedPeaks = refinePeaks(peaks, img, validInterval, returnInvalidPeaks, maxNumMoves, allowMaximaTolerance, maximaTolerance, allowedToMoveInDim, numTasks, ex);
-
- // shutdown
- ex.shutdown();
-
- return allRefinedPeaks;
+ return Parallelization.runWithNumThreads( numThreads,
+ () -> refinePeaks( peaks, img, validInterval, returnInvalidPeaks, maxNumMoves, allowMaximaTolerance, maximaTolerance,
+ allowedToMoveInDim ) );
}
/**
- * Refine a set of peaks to subpixel coordinates. Multi-threaded version.
+ * Refine a set of peaks to subpixel coordinates. Single-threaded version.
*
* A List {@link RefinedPeak} for the given list of {@link Localizable} is
* computed by, for each peak, fitting a quadratic function to the image and
@@ -255,7 +256,7 @@ public static < T extends RealType< T >, P extends Localizable > ArrayList< Refi
* fit is repeated at the corresponding integer coordinates. This is
* repeated to convergence, for a maximum number of iterations, or until the
* integer coordinates move out of the valid image.
- *
+ *
* @param peaks
* List of integer peaks.
* @param img
@@ -280,91 +281,44 @@ public static < T extends RealType< T >, P extends Localizable > ArrayList< Refi
* @param allowedToMoveInDim
* specifies, per dimension, whether the base location is allowed
* to be moved in the iterative procedure.
- * @param numTasks
- * How many tasks to use for the computation.
- * @param ex
- * The executor for the computation.
* @return refined list of peaks.
*/
public static < T extends RealType< T >, P extends Localizable > ArrayList< RefinedPeak< P > > refinePeaks(
final List< P > peaks, final RandomAccessible< T > img, final Interval validInterval, final boolean returnInvalidPeaks,
- final int maxNumMoves, final boolean allowMaximaTolerance, final float maximaTolerance, final boolean[] allowedToMoveInDim,
- final int numTasks, final ExecutorService ex )
+ final int maxNumMoves, final boolean allowMaximaTolerance, final float maximaTolerance, final boolean[] allowedToMoveInDim )
{
final int numPeaks = peaks.size();
- final ArrayList< RefinedPeak< P > > allRefinedPeaks = new ArrayList<>( numPeaks );
if ( numPeaks == 0 )
- return allRefinedPeaks;
+ return new ArrayList<>();
- final int taskSize = numPeaks / numTasks;
- final List< Callable< ArrayList< RefinedPeak< P > > > > tasks = new ArrayList<>( taskSize );
- for ( int taskNum = 0; taskNum < numTasks; ++taskNum )
- {
- final int fromIndex = taskNum * taskSize;
- final int toIndex = ( taskNum == numTasks - 1 ) ? numPeaks : fromIndex + taskSize;
+ TaskExecutor taskExecutor = Parallelization.getTaskExecutor();
- tasks.add( () -> refinePeaks(
- peaks.subList( fromIndex, toIndex ),
- img, validInterval, returnInvalidPeaks, maxNumMoves, allowMaximaTolerance, maximaTolerance, allowedToMoveInDim ) );
- }
+ List< Interval > chunks = IntervalChunks.chunkInterval( new FinalInterval( numPeaks ), taskExecutor.suggestNumberOfTasks() );
- try
- {
- final List< Future< ArrayList< RefinedPeak< P > > > > futures = ex.invokeAll( tasks );
- for ( final Future< ArrayList< RefinedPeak< P > > > future : futures )
- allRefinedPeaks.addAll( future.get() );
- }
- catch ( final InterruptedException | ExecutionException e )
- {
- e.printStackTrace();
- }
+ List< ArrayList< RefinedPeak< P > > > result = taskExecutor.forEachApply( chunks, chunk ->
+ refinePeaksChunk(
+ peaks.subList( ( int ) chunk.min( 0 ), ( int ) chunk.max( 0 ) + 1 ),
+ img, validInterval, returnInvalidPeaks, maxNumMoves, allowMaximaTolerance,
+ maximaTolerance, allowedToMoveInDim )
+ );
- return allRefinedPeaks;
+ return concatenate( result );
}
- /**
- * Refine a set of peaks to subpixel coordinates. Single-threaded version.
- *
- * A List {@link RefinedPeak} for the given list of {@link Localizable} is
- * computed by, for each peak, fitting a quadratic function to the image and
- * computing the subpixel coordinates of the extremum. This is an iterative
- * procedure. If the extremum is shifted more than 0.5 in one or more the
- * fit is repeated at the corresponding integer coordinates. This is
- * repeated to convergence, for a maximum number of iterations, or until the
- * integer coordinates move out of the valid image.
- *
- * @param peaks
- * List of integer peaks.
- * @param img
- * Pixel values.
- * @param validInterval
- * In which interval the {@code img} contains valid pixels.
- * If null, an infinite {@code img} is assumed. Integer
- * peaks must lie within a 1-pixel border of this interval.
- * @param returnInvalidPeaks
- * Whether (invalid) {@link RefinedPeak} should be created for
- * peaks where the fitting procedure did not converge.
- * @param maxNumMoves
- * maximum number of iterations for each peak.
- * @param allowMaximaTolerance
- * If we allow an increasing maxima tolerance we will not change
- * the base position that easily. Sometimes it simply jumps from
- * left to right and back, because it is 4.51 (i.e. goto 5), then
- * 4.49 (i.e. goto 4) Then we say, ok, lets keep the base
- * position even if the subpixel location is 0.6...
- * @param maximaTolerance
- * By how much to increase the tolerance per iteration.
- * @param allowedToMoveInDim
- * specifies, per dimension, whether the base location is allowed
- * to be moved in the iterative procedure.
- * @return refined list of peaks.
- */
- public static < T extends RealType< T >, P extends Localizable > ArrayList< RefinedPeak< P > > refinePeaks(
+ private static < P > ArrayList< P > concatenate( List< ? extends List< ? extends P > > lists )
+ {
+ int size = lists.stream().mapToInt( List::size ).sum();
+ ArrayList< P > result = new ArrayList<>( size );
+ lists.forEach( result::addAll );
+ return result;
+ }
+
+ public static < T extends RealType< T >, P extends Localizable > ArrayList< RefinedPeak< P > > refinePeaksChunk(
final List< P > peaks, final RandomAccessible< T > img, final Interval validInterval, final boolean returnInvalidPeaks,
final int maxNumMoves, final boolean allowMaximaTolerance, final float maximaTolerance, final boolean[] allowedToMoveInDim )
{
- final ArrayList< RefinedPeak< P >> refinedPeaks = new ArrayList< RefinedPeak< P > >();
+ final ArrayList< RefinedPeak< P > > refinedPeaks = new ArrayList< RefinedPeak< P > >();
final int n = img.numDimensions();
@@ -471,7 +425,7 @@ else if ( returnInvalidPeaks )
/**
* Estimate subpixel {@code offset} of extremum of quadratic function
* fitted at {@code p}.
- *
+ *
* @param p
* integer position at which to fit quadratic.
* @param access
diff --git a/src/main/java/net/imglib2/algorithm/morphology/Dilation.java b/src/main/java/net/imglib2/algorithm/morphology/Dilation.java
index 947f93a83..cbdaa7897 100644
--- a/src/main/java/net/imglib2/algorithm/morphology/Dilation.java
+++ b/src/main/java/net/imglib2/algorithm/morphology/Dilation.java
@@ -34,24 +34,23 @@
package net.imglib2.algorithm.morphology;
import java.util.List;
-import java.util.Vector;
+import java.util.function.BiConsumer;
-import net.imglib2.Cursor;
import net.imglib2.FinalDimensions;
import net.imglib2.Interval;
import net.imglib2.IterableInterval;
-import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.loops.IterableLoopBuilder;
import net.imglib2.algorithm.neighborhood.Neighborhood;
import net.imglib2.algorithm.neighborhood.Shape;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
-import net.imglib2.multithreading.Chunk;
-import net.imglib2.multithreading.SimpleMultiThreading;
+import net.imglib2.parallel.Parallelization;
import net.imglib2.type.Type;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.numeric.RealType;
+import net.imglib2.util.Cast;
import net.imglib2.util.Util;
import net.imglib2.view.ExtendedRandomAccessibleInterval;
import net.imglib2.view.IntervalView;
@@ -59,6 +58,7 @@
public class Dilation
{
+
/**
* Performs the dilation morphological operation, on a {@link RealType}
* {@link Img} using a list of {@link Shape}s as a flat structuring element.
@@ -449,122 +449,38 @@ public static < T extends RealType< T >> void dilate( final RandomAccessible< T
*/
public static < T extends Type< T > & Comparable< T > > void dilate( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T minVal, int numThreads )
{
- numThreads = Math.max( 1, numThreads );
-
- /*
- * Prepare iteration.
- */
-
- final RandomAccessible< Neighborhood< T >> accessible = strel.neighborhoodsRandomAccessible( source );
-
- /*
- * Multithread
- */
-
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
-
-
- final Object tmp = minVal;
- if ( tmp instanceof BitType )
- {
- /*
- * Optimization for BitType
- */
+ final RandomAccessible< Neighborhood< T > > neighborhoods = strel.neighborhoodsRandomAccessible( source );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IterableLoopBuilder.setImages( target, neighborhoods ).multithreaded().forEachChunk( chunk -> {
+ chunk.forEachPixel( getDilateAction( minVal ) );
+ return null;
+ } );
+ } );
+ }
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology dilate thread " + i )
- {
- @Override
- public void run()
+ private static < T extends Type< T > & Comparable< T > > BiConsumer< T, Neighborhood< T > > getDilateAction( T minVal )
+ {
+ if ( minVal instanceof BitType )
+ return Cast.unchecked( (BiConsumer< BitType, Neighborhood< BitType > > ) ( t, neighborhood ) -> {
+ for ( BitType val1 : neighborhood )
+ if ( val1.get() )
{
- final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target );
- final Object tmp2 = target.cursor();
- @SuppressWarnings( "unchecked" )
- final Cursor< BitType > cursorDilated = ( Cursor< BitType > ) tmp2;
- cursorDilated.jumpFwd( chunk.getStartPosition() );
-
- for ( long steps = 0; steps < chunk.getLoopSize(); steps++ )
- {
- cursorDilated.fwd();
- randomAccess.setPosition( cursorDilated );
- final Neighborhood< T > neighborhood = randomAccess.get();
- final Object tmp3 = neighborhood.cursor();
- @SuppressWarnings( "unchecked" )
- final Cursor< BitType > nc = ( Cursor< BitType > ) tmp3;
-
- while ( nc.hasNext() )
- {
- nc.fwd();
- final BitType val = nc.get();
- if ( val.get() )
- {
- cursorDilated.get().set( true );
- break;
- }
- }
- }
-
+ t.set( true );
+ return;
}
- };
- }
- }
+ t.set( false );
+ } );
else
{
- /*
- * All other comparable type.
- */
-
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology dilate thread " + i )
- {
- @Override
- public void run()
- {
- final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target );
- final Cursor< T > cursorDilated = target.cursor();
- cursorDilated.jumpFwd( chunk.getStartPosition() );
-
- final T max = MorphologyUtils.createVariable( source, target );
- for ( long steps = 0; steps < chunk.getLoopSize(); steps++ )
- {
- cursorDilated.fwd();
- randomAccess.setPosition( cursorDilated );
- final Neighborhood< T > neighborhood = randomAccess.get();
- final Cursor< T > nc = neighborhood.cursor();
-
- /*
- * Look for max in the neighborhood.
- */
-
- max.set( minVal );
- while ( nc.hasNext() )
- {
- nc.fwd();
- final T val = nc.get();
- // We need only Comparable to do this:
- if ( val.compareTo( max ) > 0 )
- {
- max.set( val );
- }
- }
- cursorDilated.get().set( max );
- }
-
- }
- };
- }
+ T max = minVal.copy();
+ return ( t, neighborhood ) -> {
+ max.set( minVal );
+ for ( T val : neighborhood )
+ if ( val.compareTo( max ) > 0 )
+ max.set( val );
+ t.set( max );
+ };
}
-
- /*
- * Launch calculation
- */
-
- SimpleMultiThreading.startAndJoin( threads );
}
/**
diff --git a/src/main/java/net/imglib2/algorithm/morphology/Erosion.java b/src/main/java/net/imglib2/algorithm/morphology/Erosion.java
index aced24af1..d1c0b9294 100644
--- a/src/main/java/net/imglib2/algorithm/morphology/Erosion.java
+++ b/src/main/java/net/imglib2/algorithm/morphology/Erosion.java
@@ -34,24 +34,23 @@
package net.imglib2.algorithm.morphology;
import java.util.List;
-import java.util.Vector;
+import java.util.function.BiConsumer;
-import net.imglib2.Cursor;
import net.imglib2.FinalDimensions;
import net.imglib2.Interval;
import net.imglib2.IterableInterval;
-import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.loops.IterableLoopBuilder;
import net.imglib2.algorithm.neighborhood.Neighborhood;
import net.imglib2.algorithm.neighborhood.Shape;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
-import net.imglib2.multithreading.Chunk;
-import net.imglib2.multithreading.SimpleMultiThreading;
+import net.imglib2.parallel.Parallelization;
import net.imglib2.type.Type;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.numeric.RealType;
+import net.imglib2.util.Cast;
import net.imglib2.util.Util;
import net.imglib2.view.ExtendedRandomAccessibleInterval;
import net.imglib2.view.IntervalView;
@@ -449,122 +448,38 @@ public static < T extends RealType< T >> void erode( final RandomAccessible< T >
*/
public static < T extends Type< T > & Comparable< T > > void erode( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T maxVal, int numThreads )
{
- numThreads = Math.max( 1, numThreads );
-
- /*
- * Prepare iteration.
- */
-
- final RandomAccessible< Neighborhood< T >> accessible = strel.neighborhoodsRandomAccessible( source );
-
- /*
- * Multithread
- */
-
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
-
- final Object tmp = maxVal;
- if ( tmp instanceof BitType )
- {
- /*
- * Optimization for BitType
- */
+ final RandomAccessible< Neighborhood< T >> neighborhoods = strel.neighborhoodsRandomAccessible( source );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IterableLoopBuilder.setImages( target, neighborhoods ).multithreaded().forEachChunk( chunk -> {
+ chunk.forEachPixel( getErodeAction( maxVal ) );
+ return null;
+ } );
+ } );
+ }
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology erode thread " + i )
- {
- @Override
- public void run()
+ private static < T extends Type< T > & Comparable< T > > BiConsumer< T, Neighborhood< T > > getErodeAction( T maxVal )
+ {
+ if ( maxVal instanceof BitType )
+ return Cast.unchecked( ( BiConsumer< BitType, Neighborhood< BitType > > ) ( t, neighborhood ) -> {
+ for ( BitType val1 : neighborhood )
+ if ( ! val1.get() )
{
- final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target );
- final Object tmp2 = target.cursor();
- @SuppressWarnings( "unchecked" )
- final Cursor< BitType > cursorTarget = ( Cursor< BitType > ) tmp2;
- cursorTarget.jumpFwd( chunk.getStartPosition() );
-
- for ( long steps = 0; steps < chunk.getLoopSize(); steps++ )
- {
- cursorTarget.fwd();
- randomAccess.setPosition( cursorTarget );
- final Object tmp3 = randomAccess.get();
- @SuppressWarnings( "unchecked" )
- final Neighborhood< BitType > neighborhood = (net.imglib2.algorithm.neighborhood.Neighborhood< BitType > ) tmp3;
- final Cursor< BitType > nc = neighborhood.cursor();
-
- cursorTarget.get().set( true );
- while ( nc.hasNext() )
- {
- nc.fwd();
- final BitType val = nc.get();
- if ( !val.get() )
- {
- cursorTarget.get().set( false );
- break;
- }
- }
- }
-
+ t.set( false );
+ return;
}
- };
- }
- }
+ t.set( true );
+ } );
else
{
- /*
- * All other comparable type.
- */
-
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology erode thread " + i )
- {
- @Override
- public void run()
- {
- final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target );
- final Cursor< T > cursorTarget = target.cursor();
- cursorTarget.jumpFwd( chunk.getStartPosition() );
-
- final T max = MorphologyUtils.createVariable( source, target );
- for ( long steps = 0; steps < chunk.getLoopSize(); steps++ )
- {
- cursorTarget.fwd();
- randomAccess.setPosition( cursorTarget );
- final Neighborhood< T > neighborhood = randomAccess.get();
- final Cursor< T > nc = neighborhood.cursor();
-
- /*
- * Look for max in the neighborhood.
- */
-
- max.set( maxVal );
- while ( nc.hasNext() )
- {
- nc.fwd();
- final T val = nc.get();
- // We need only Comparable to do this:
- if ( val.compareTo( max ) < 0 )
- {
- max.set( val );
- }
- }
- cursorTarget.get().set( max );
- }
-
- }
- };
- }
+ T min = maxVal.copy();
+ return ( t, neighborhood ) -> {
+ min.set( maxVal );
+ for ( T val : neighborhood )
+ if ( val.compareTo( min ) < 0 )
+ min.set( val );
+ t.set( min );
+ };
}
-
- /*
- * Launch calculation
- */
-
- SimpleMultiThreading.startAndJoin( threads );
}
/**
diff --git a/src/main/java/net/imglib2/algorithm/morphology/MorphologyUtils.java b/src/main/java/net/imglib2/algorithm/morphology/MorphologyUtils.java
index 82d7c83ba..656d6b4eb 100644
--- a/src/main/java/net/imglib2/algorithm/morphology/MorphologyUtils.java
+++ b/src/main/java/net/imglib2/algorithm/morphology/MorphologyUtils.java
@@ -33,15 +33,13 @@
*/
package net.imglib2.algorithm.morphology;
-import java.util.Vector;
-
-import net.imglib2.Cursor;
import net.imglib2.EuclideanSpace;
import net.imglib2.Interval;
import net.imglib2.IterableInterval;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.loops.IterableLoopBuilder;
import net.imglib2.algorithm.neighborhood.Neighborhood;
import net.imglib2.algorithm.neighborhood.Shape;
import net.imglib2.img.Img;
@@ -49,12 +47,11 @@
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.img.array.ArrayRandomAccess;
import net.imglib2.img.basictypeaccess.array.LongArray;
-import net.imglib2.multithreading.Chunk;
-import net.imglib2.multithreading.SimpleMultiThreading;
+import net.imglib2.loops.LoopBuilder;
+import net.imglib2.parallel.Parallelization;
import net.imglib2.type.Type;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.operators.Sub;
-import net.imglib2.util.Intervals;
import net.imglib2.util.Util;
import net.imglib2.view.IntervalView;
import net.imglib2.view.Views;
@@ -66,17 +63,15 @@ public class MorphologyUtils
* Static util to compute the final image dimensions and required offset
* when performing a full dilation with the specified strel.
*
- * @param source
- * the source image.
- * @param strel
- * the strel to use for dilation.
+ * @param source the source image.
+ * @param strel the strel to use for dilation.
* @return a 2-elements {@code long[][]}:
- * > lists )
+ {
+ if(lists.size() == 1)
+ return lists.get( 0 );
+ int size = lists.stream().mapToInt( List::size ).sum();
+ List< P > result = new ArrayList<>( size );
+ for ( List< P > list : lists )
+ result.addAll( list );
+ return result;
}
/**
diff --git a/src/main/java/net/imglib2/algorithm/localextrema/SubpixelLocalization.java b/src/main/java/net/imglib2/algorithm/localextrema/SubpixelLocalization.java
index a2168f244..93e497655 100644
--- a/src/main/java/net/imglib2/algorithm/localextrema/SubpixelLocalization.java
+++ b/src/main/java/net/imglib2/algorithm/localextrema/SubpixelLocalization.java
@@ -45,13 +45,18 @@
import Jama.LUDecomposition;
import Jama.Matrix;
+
import net.imglib2.Interval;
+import net.imglib2.FinalInterval;
import net.imglib2.Localizable;
import net.imglib2.Point;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessible;
import net.imglib2.RealPoint;
import net.imglib2.RealPositionable;
+import net.imglib2.loops.IntervalChunks;
+import net.imglib2.parallel.Parallelization;
+import net.imglib2.parallel.TaskExecutor;
import net.imglib2.type.numeric.RealType;
import net.imglib2.util.Intervals;
@@ -60,7 +65,7 @@
* {@link #refinePeaks(List, RandomAccessible, Interval, boolean, int, boolean, float, boolean[], int)}
* method to do this, but this has a lot of parameters. Therefore, this class
* can also be instantiated to encapsulate the parameter settings.
- *
+ *
*
- *
+ *
+ *
*/
public static final < T > long[][] computeTargetImageDimensionsAndOffset( final Interval source, final Shape strel )
{
@@ -291,61 +286,16 @@ private static final void appendSingleSlice( final RandomAccess< BitType > ra, f
static < T extends Type< T > > void copy( final IterableInterval< T > source, final RandomAccessible< T > target, final int numThreads )
{
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( source.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology copy thread " + i )
- {
- @Override
- public void run()
- {
- final Cursor< T > sourceCursor = source.localizingCursor();
- sourceCursor.jumpFwd( chunk.getStartPosition() );
- final RandomAccess< T > targetRandomAccess = target.randomAccess();
-
- for ( long step = 0; step < chunk.getLoopSize(); step++ )
- {
- sourceCursor.fwd();
- targetRandomAccess.setPosition( sourceCursor );
- targetRandomAccess.get().set( sourceCursor.get() );
- }
- }
- };
- }
-
- SimpleMultiThreading.startAndJoin( threads );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IterableLoopBuilder.setImages( source, target ).multithreaded().forEachPixel( ( s, t ) -> t.set( s ) );
+ } );
}
static < T extends Type< T > > void copy2( final RandomAccessible< T > source, final IterableInterval< T > target, final int numThreads )
{
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology copy2 thread " + i )
- {
- @Override
- public void run()
- {
- final Cursor< T > targetCursor = target.localizingCursor();
- targetCursor.jumpFwd( chunk.getStartPosition() );
- final RandomAccess< T > sourceRandomAccess = source.randomAccess();
-
- // iterate over the input cursor
- for ( long step = 0; step < chunk.getLoopSize(); step++ )
- {
- targetCursor.fwd();
- sourceRandomAccess.setPosition( targetCursor );
- targetCursor.get().set( sourceRandomAccess.get() );
- }
- }
- };
- }
-
- SimpleMultiThreading.startAndJoin( threads );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IterableLoopBuilder.setImages( target, source ).multithreaded().forEachPixel( ( t, s ) -> t.set( s ) );
+ } );
}
static < T extends Type< T > > Img< T > copyCropped( final Img< T > largeSource, final Interval interval, final int numThreads )
@@ -356,32 +306,8 @@ static < T extends Type< T > > Img< T > copyCropped( final Img< T > largeSource,
offset[ d ] = ( largeSource.dimension( d ) - interval.dimension( d ) ) / 2;
}
final Img< T > create = largeSource.factory().create( interval );
-
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( create.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology copyCropped thread " + i )
- {
- @Override
- public void run()
- {
- final IntervalView< T > intervalView = Views.offset( largeSource, offset );
- final Cursor< T > cursor = create.cursor();
- cursor.jumpFwd( chunk.getStartPosition() );
- final RandomAccess< T > randomAccess = intervalView.randomAccess();
- for ( long step = 0; step < chunk.getLoopSize(); step++ )
- {
- cursor.fwd();
- randomAccess.setPosition( cursor );
- cursor.get().set( randomAccess.get() );
- }
- }
- };
- }
-
- SimpleMultiThreading.startAndJoin( threads );
+ final RandomAccessibleInterval< T > intervalView = Views.translateInverse( largeSource, offset );
+ copy2( intervalView, create, numThreads );
return create;
}
@@ -393,7 +319,7 @@ public void run()
* @param interval
* @return type instance
*/
- static < T extends Type< T >> T createVariable( final RandomAccessible< T > accessible, final Interval interval )
+ static < T extends Type< T > > T createVariable( final RandomAccessible< T > accessible, final Interval interval )
{
final RandomAccess< T > a = accessible.randomAccess();
interval.min( a );
@@ -405,7 +331,7 @@ public static final Neighborhood< BitType > getNeighborhood( final Shape shape,
final int numDims = space.numDimensions();
final long[] dimensions = Util.getArrayFromValue( 1l, numDims );
final ArrayImg< BitType, LongArray > img = ArrayImgs.bits( dimensions );
- final IterableInterval< Neighborhood< BitType >> neighborhoods = shape.neighborhoods( img );
+ final IterableInterval< Neighborhood< BitType > > neighborhoods = shape.neighborhoods( img );
final Neighborhood< BitType > neighborhood = neighborhoods.cursor().next();
return neighborhood;
}
@@ -418,12 +344,10 @@ public static final Neighborhood< BitType > getNeighborhood( final Shape shape,
* This method only prints the first 3 dimensions of the structuring
* element. Dimensions above 3 are skipped.
*
- * @param shape
- * the structuring element to print.
- * @param dimensionality
- * the dimensionality to cast it over. This is required as
- * {@link Shape} does not carry a dimensionality, and we need one
- * to generate a neighborhood to iterate.
+ * @param shape the structuring element to print.
+ * @param dimensionality the dimensionality to cast it over. This is required as
+ * {@link Shape} does not carry a dimensionality, and we need one
+ * to generate a neighborhood to iterate.
* @return a string representation of the structuring element.
*/
public static final String printNeighborhood( final Shape shape, final int dimensionality )
@@ -473,219 +397,88 @@ else if ( neighborhood.numDimensions() > 0 )
/**
* Does A = A - B. Writes the results in A.
*
- * @param A
- * A
- * @param B
- * B
+ * @param A A
+ * @param B B
* @param numThreads
*/
static < T extends Sub< T > > void subAAB( final RandomAccessible< T > A, final IterableInterval< T > B, final int numThreads )
{
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( B.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
-
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology subAAB thread " + i )
- {
- @Override
- public void run()
- {
- final Cursor< T > Bcursor = B.localizingCursor();
- Bcursor.jumpFwd( chunk.getStartPosition() );
- final RandomAccess< T > Ara = A.randomAccess();
-
- for ( long step = 0; step < chunk.getLoopSize(); step++ )
- {
- Bcursor.fwd();
- Ara.setPosition( Bcursor );
- Ara.get().sub( Bcursor.get() );
- }
- }
- };
- }
-
- SimpleMultiThreading.startAndJoin( threads );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IterableLoopBuilder.setImages( B, A ).multithreaded().forEachPixel( ( b, a ) -> a.sub( b ) );
+ } );
}
-
/**
* Does A = A - B. Writes the results in A.
*
- * @param A
- * A
- * @param B
- * B
+ * @param A A
+ * @param B B
* @param numThreads
*/
static < T extends Sub< T > > void subAAB2( final IterableInterval< T > A, final RandomAccessible< T > B, final int numThreads )
{
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( A.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
-
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology subAAB2 thread " + i )
- {
- @Override
- public void run()
- {
- final Cursor< T > Acursor = A.localizingCursor();
- Acursor.jumpFwd( chunk.getStartPosition() );
- final RandomAccess< T > Bra = B.randomAccess(); // LOL
-
- for ( long step = 0; step < chunk.getLoopSize(); step++ )
- {
- Acursor.fwd();
- Bra.setPosition( Acursor );
- Acursor.get().sub( Bra.get() );
- }
- }
- };
- }
-
- SimpleMultiThreading.startAndJoin( threads );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IterableLoopBuilder.setImages( A, B ).multithreaded().forEachPixel( ( a, b ) -> a.sub( b ) );
+ } );
}
-
/**
* Does A = B - A. Writes the results in A.
*
- * @param source
- * A
- * @param target
- * B
+ * @param A A
+ * @param B B
* @param numThreads
*/
- static < T extends Sub< T > & Type< T >> void subABA( final RandomAccessible< T > source, final IterableInterval< T > target, final int numThreads )
+ static < T extends Sub< T > & Type< T > > void subABA( final RandomAccessible< T > A, final IterableInterval< T > B, final int numThreads )
{
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
-
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology subABA thread " + i )
- {
- @Override
- public void run()
- {
- final T tmp = createVariable( source, target );
- final Cursor< T > targetCursor = target.localizingCursor();
- targetCursor.jumpFwd( chunk.getStartPosition() );
- final RandomAccess< T > sourceRandomAccess = source.randomAccess();
-
- for ( long step = 0; step < chunk.getLoopSize(); step++ )
- {
- targetCursor.fwd();
- sourceRandomAccess.setPosition( targetCursor );
-
- tmp.set( targetCursor.get() );
- tmp.sub( sourceRandomAccess.get() );
-
- sourceRandomAccess.get().set( tmp );
- }
- }
- };
- }
-
- SimpleMultiThreading.startAndJoin( threads );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IterableLoopBuilder.setImages( B, A ).multithreaded().forEachChunk( chunk -> {
+ T tmp = createVariable( A, B );
+ chunk.forEachPixel( ( b, a ) -> {
+ tmp.set( b );
+ tmp.sub( a );
+ a.set( tmp );
+ } );
+ return null;
+ } );
+ } );
}
/**
- * Does A = B - A. Writes the results in A.
+ * Does B = B - A. Writes the results in B.
*
- * @param source
- * A
- * @param target
- * B
+ * @param A A
+ * @param B B
* @param numThreads
*/
- static < T extends Sub< T > & Type< T >> void subABA2( final RandomAccessibleInterval< T > source, final RandomAccessible< T > target, final int numThreads )
+ static < T extends Sub< T > & Type< T > > void subABA2( final RandomAccessibleInterval< T > A, final RandomAccessible< T > B, final int numThreads )
{
- final long size = Intervals.numElements( source );
- final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( size, numThreads );
- final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
-
- for ( int i = 0; i < threads.length; i++ )
- {
- final Chunk chunk = chunks.get( i );
- threads[ i ] = new Thread( "Morphology subABA2 thread " + i )
- {
- @Override
- public void run()
- {
- final T tmp = createVariable( target, source );
- final Cursor< T > sourceCursor = Views.iterable( source ).localizingCursor();
- sourceCursor.jumpFwd( chunk.getStartPosition() );
- final RandomAccess< T > targetRandomAccess = target.randomAccess( source );
-
- for ( long step = 0; step < chunk.getLoopSize(); step++ )
- {
-
- }
- while ( sourceCursor.hasNext() )
- {
- sourceCursor.fwd();
- targetRandomAccess.setPosition( sourceCursor );
-
- tmp.set( targetRandomAccess.get() );
- tmp.sub( sourceCursor.get() );
-
- targetRandomAccess.get().set( tmp );
- }
- }
- };
- }
-
- SimpleMultiThreading.startAndJoin( threads );
+ Parallelization.runWithNumThreads( numThreads, () -> {
+ IntervalView