Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use new imglib2 parallelization approach #83

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
630a8cb
Add utility class to simplify the creation of test images
maarzt Dec 6, 2022
31f054a
Add unit test for class Thresholder
maarzt Dec 6, 2022
6fdf423
Add unit test for class DifferenceOfGaussian
maarzt Dec 1, 2022
481bf0f
Refactor ConnectedComponentsTest and test more methods
maarzt Dec 2, 2022
f5ff37a
Add unit tests for Dilation and Erosion
maarzt Dec 2, 2022
bfaa8bd
Add unit test for class MorphologyUtils
maarzt Dec 2, 2022
6a4f3a2
Add unit test for class ComputeMinMax
maarzt Dec 6, 2022
3184e2f
POM: pin imglib2 version to 5.13.0
maarzt Nov 10, 2021
ac3b901
Change Thresholder implementation to multi-threaded LoopBuilder
maarzt Nov 10, 2021
8397118
Update DifferenceOfGaussian to use multi-threaded LoopBuilder and img…
maarzt Nov 10, 2021
7e28716
Update ConntectedComponenets to make use of the Parallelization context.
maarzt Nov 10, 2021
791c47f
Change TensorEigenValues to use multi-threaded LoopBuilder.
maarzt Nov 10, 2021
4873a47
Change Dilation to use IterableLoopBuilder
maarzt Dec 2, 2022
f294d06
Change Erosion to use IterableLoopBuilder
maarzt Dec 2, 2022
ddba38a
Update MorphologyUtils to use Parallelization framework
maarzt Dec 2, 2022
fe79678
Update DistanceTransform to use parallelization framework
maarzt Dec 2, 2022
3dd9ecd
Chang PartialDerivative to use multi-threaded LoopBuilder
maarzt Dec 2, 2022
23ea534
Update LocalExtrema to use parallelization framework
maarzt Dec 2, 2022
7fe1eaf
Change SubpixelLocalization to use the Parallelization framework
maarzt Dec 6, 2022
5c81783
Update ComputeMinMax to use parallelization framework
maarzt Dec 2, 2022
6d4f829
Add benchmark for LocalExtrema
maarzt Dec 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ Jean-Yves Tinevez and Michael Zinsmaier.</license.copyrightOwners>

<!-- NB: Deploy releases to the SciJava Maven repository. -->
<releaseProfiles>sign,deploy-to-scijava</releaseProfiles>
<imglib2.version>5.13.0</imglib2.version>
</properties>

<repositories>
Expand Down
128 changes: 34 additions & 94 deletions src/main/java/net/imglib2/algorithm/binary/Thresholder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 );
}

}
140 changes: 51 additions & 89 deletions src/main/java/net/imglib2/algorithm/dog/DifferenceOfGaussian.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
*
Expand All @@ -66,7 +60,7 @@ public class DifferenceOfGaussian
* of sigmaLarger (where {@code sigmaLarger > sigmaSmaller}).
* <p>
* Creates an appropriate temporary image and calls
* {@link #DoG(double[], double[], RandomAccessible, RandomAccessible, RandomAccessibleInterval, ExecutorService)}
* {@link #DoG(double[], double[], RandomAccessible, RandomAccessible, RandomAccessibleInterval)}
* .
* </p>
*
Expand All @@ -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 );
}

/**
Expand All @@ -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:
*
* <p>
* {@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:
*
* <p>
* {@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 )
);
}

/**
Expand Down
Loading