Commit 1c32127f authored by moon's avatar moon

Merge branch 'master' into 'master'

Fixed the multi threading bug

See merge request !3
parents 91a61715 ff18acfe
......@@ -14,7 +14,7 @@
<groupId>de.mpicbg.scf</groupId>
<artifactId>MinCostZSurface_</artifactId>
<version>1.1.4</version>
<version>1.1.5</version>
<name>MinCostZSurface_</name>
<description>
......
......@@ -6,6 +6,7 @@ import net.imagej.ops.AbstractOp;
import net.imagej.ops.Op;
import net.imglib2.*;
import net.imglib2.img.Img;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.multithreading.SimpleMultiThreading;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
......@@ -18,6 +19,8 @@ import org.scijava.plugin.Plugin;
import java.io.File;
import static de.mpicbg.scf.mincostsurface.img_utils.createOffset;
/**
* Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility
* Organization: MPI-CBG Dresden
......@@ -29,7 +32,7 @@ import java.io.File;
*
* The image chunks are made by height / numThreads. Each chunk is processed in MinCostZSurface class
*/
@Plugin(type = Op.class, menuPath = "Plugins>MinCost2ZSurfaceMT", name = "MinCost2ZSurfaceMT", headless = true, label = "MinCost2ZSurfaceMT")
@Plugin(type = Op.class, menuPath = "Plugins>MinCost2ZSurface>Multi Threads", name = "MinCost2ZSurfaceMT", headless = true, label = "MinCost2ZSurfaceMT")
public class MinCost2ZSurfaceMT_Ops<T extends RealType<T> & NativeType<T>> extends AbstractOp {
//
// should implement Map if I want to benefit the ops matching
......@@ -100,17 +103,28 @@ public class MinCost2ZSurfaceMT_Ops<T extends RealType<T> & NativeType<T>> exten
Img<T> image_cost_ds = img_utils.downsample(image_cost_orig, new float[]{downsample_factor_xy, downsample_factor_xy, downsample_factor_z});
final long[] dims = new long[image_cost_ds.numDimensions()];
final long[] lastDims = new long[image_cost_ds.numDimensions()];
image_cost_ds.dimensions(dims);
image_cost_ds.dimensions(lastDims);
if(dims[1] < numThreads || numThreads < 0) {
numThreads = (int) dims[1];
}
// System.out.println("Height: " + dims[1]);
// Setup the number of dimension in the input image except the height part
// The height will be the original height divided by the number of threads
dims[1] = image_cost_ds.dimension(1) / numThreads;
lastDims[1] = image_cost_ds.dimension(1) - dims[1] * (numThreads - 1);
FinalInterval interval = new FinalInterval(dims);
FinalInterval lastInterval = new FinalInterval(lastDims);
// Call the Multi threaded process
Img<T>[] depth_map = processMT(image_cost_ds, interval, numThreads);
// Img<T>[] depth_map = processMT(image_cost_ds, interval, numThreads);
Img<T>[] depth_map = processMT(image_cost_ds, interval, lastInterval, numThreads);
// The returned depth map array
final Img<T> depth_map1 = depth_map[0];
......@@ -132,32 +146,18 @@ public class MinCost2ZSurfaceMT_Ops<T extends RealType<T> & NativeType<T>> exten
up_map_cursor2.next().mul(1 / downsample_factor_z);
}
// ImageJFunctions.show(upsampled_depthMap1);
// ImageJFunctions.show(upsampled_depthMap2);
// ImageJFunctions.show(upsampled_depthMap1,"altitude map1");
// ImageJFunctions.show(upsampled_depthMap2,"altitude map2" );
System.out.println("processing done");
}
<T extends RealType<T> & NativeType<T>> Img<T>[] processMT(final Img<T> inputSource, final Interval interval, final int numThreads) {
<T extends RealType<T> & NativeType<T>> Img<T>[] processMT(final Img<T> inputSource, Interval interval, final Interval lastInterval, final int numThreads) {
// <T extends RealType<T> & NativeType<T>> Img<T>[] processMT(final Img<T> inputSource, final Interval interval, final int numThreads) {
// Setup the offset arrays for multi threads
final long[][] offset = new long[numThreads][inputSource.numDimensions()];
// Offset is stored by changing the height value
for (int d = 0; d < offset.length; d++) {
offset[d] = new long[inputSource.numDimensions()];
for (int i = 0; i < offset[d].length; i++) {
offset[d][i] = 0;
}
// width
// offset[d][0] = inputSource.dimension( 0 ) / numThreads * d;
// height
offset[d][1] = inputSource.dimension(1) / numThreads * d;
// depth
// offset[d][2] = 0;
}
final long[][] offset = createOffset(inputSource, numThreads);
// Initialize depth maps
final Img<T> globalDepthMap1 = inputSource.factory().create(inputSource.dimension(0), inputSource.dimension(1));
......@@ -173,8 +173,12 @@ public class MinCost2ZSurfaceMT_Ops<T extends RealType<T> & NativeType<T>> exten
// Setup the IntervalView of the original image
IntervalView<T> intervalView = Views.offset(inputSource, offset[finalI]);
// Setup the chunk for each thread
final Img<T> chunk = inputSource.factory().create(interval);
Cursor<T> cursor = chunk.cursor();
// Img<T> chunk = inputSource.factory().create(interval);
Img<T> chunk;
if(finalI == (threads.length - 1)) chunk = inputSource.factory().create(lastInterval);
else chunk = inputSource.factory().create(interval);
Cursor<T> cursor = chunk.localizingCursor();
RandomAccess<T> randomAccess = intervalView.randomAccess();
// Copy the original values to the chunk
......@@ -235,17 +239,13 @@ public class MinCost2ZSurfaceMT_Ops<T extends RealType<T> & NativeType<T>> exten
// Copy the values from the chunk to the global maps
while (cursorDepthMap1.hasNext()) {
try {
cursorDepthMap1.fwd();
randomAccess1.setPosition(cursorDepthMap1);
randomAccess1.get().setReal(cursorDepthMap1.get().getRealFloat());
cursorDepthMap2.fwd();
randomAccess2.setPosition(cursorDepthMap2);
randomAccess2.get().setReal(cursorDepthMap2.get().getRealFloat());
} catch (java.lang.ArrayIndexOutOfBoundsException e) {
continue;
}
cursorDepthMap1.fwd();
randomAccess1.setPosition(cursorDepthMap1);
randomAccess1.get().setReal(cursorDepthMap1.get().getRealFloat());
cursorDepthMap2.fwd();
randomAccess2.setPosition(cursorDepthMap2);
randomAccess2.get().setReal(cursorDepthMap2.get().getRealFloat());
}
}
};
......
......@@ -22,7 +22,7 @@ import org.scijava.plugin.Plugin;
import java.io.File;
@Plugin(type = Op.class, menuPath = "Plugins>MinCost2ZSurface", name="MinCost2ZSurface", headless = true, label="MinCost2ZSurface")
@Plugin(type = Op.class, menuPath = "Plugins>MinCost2ZSurface>Single Thread", name="MinCost2ZSurface", headless = true, label="MinCost2ZSurface")
public class MinCost2ZSurface_Ops < T extends RealType<T> & NativeType< T > > extends AbstractOp {
//
// should implement Map if I want to benefit the ops matching
......
......@@ -9,6 +9,7 @@ import net.imglib2.FinalInterval;
import net.imglib2.Interval;
import net.imglib2.RandomAccess;
import net.imglib2.img.Img;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.multithreading.SimpleMultiThreading;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
......@@ -21,6 +22,8 @@ import org.scijava.plugin.Plugin;
import java.io.File;
import static de.mpicbg.scf.mincostsurface.img_utils.createOffset;
/**
* Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility
* Organization: MPI-CBG Dresden
......@@ -30,7 +33,7 @@ import java.io.File;
* This class is not necessary as a single thread can deal with it in a fairly fast way.
* Caution: too many threads gives an error as well.
*/
@Plugin(type = Op.class, menuPath = "Plugins>MinCostZSurfaceMT", name="MinCostZSurfaceMT", headless = true, label="MinCostZSurfaceMT")
@Plugin(type = Op.class, menuPath = "Plugins>MinCostZSurface>Multi Threads", name="MinCostZSurfaceMT", headless = true, label="MinCostZSurfaceMT")
public class MinCostZSurfaceMT_Ops< T extends RealType<T> & NativeType< T >> extends AbstractOp {
//
// should implement ops if I want to benefit the matching mechanism
......@@ -107,6 +110,10 @@ public class MinCostZSurfaceMT_Ops< T extends RealType<T> & NativeType< T >> ext
image_cost_ds.dimensions(dims);
if(dims[1] < numThreads || numThreads < 0) {
numThreads = (int) dims[1];
}
// dims[0] = image_cost_ds.dimension(0);
dims[1] = image_cost_ds.dimension(1) / numThreads;
......@@ -123,7 +130,7 @@ public class MinCostZSurfaceMT_Ops< T extends RealType<T> & NativeType< T >> ext
while(up_map_cursor.hasNext())
up_map_cursor.next().mul(1/ downsample_factor_z);
//ImageJFunctions.show( upsampled_depthMap, "altitude map" );
// ImageJFunctions.show( upsampled_depthMap, "altitude map" );
......@@ -138,22 +145,7 @@ public class MinCostZSurfaceMT_Ops< T extends RealType<T> & NativeType< T >> ext
< T extends RealType< T >> Img< T > processMT(final Img< T > inputSource, final Interval interval, final int numThreads )
{
final long[][] offset = new long[ numThreads ][inputSource.numDimensions()];
for ( int d = 0; d < offset.length; d++ )
{
offset[d] = new long[inputSource.numDimensions()];
for (int i = 0; i < offset[d].length; i++) {
offset[d][i] = 0;
}
// width
// offset[d][0] = inputSource.dimension( 0 ) / numThreads * d;
// height
offset[d][1] = inputSource.dimension( 1 ) / numThreads * d;
// depth
// offset[d][2] = 0;
}
final long[][] offset = createOffset(inputSource, numThreads);
final Img< T > globalDepthMap = inputSource.factory().create(inputSource.dimension(0), inputSource.dimension(1));
......@@ -214,13 +206,9 @@ public class MinCostZSurfaceMT_Ops< T extends RealType<T> & NativeType< T >> ext
randomAccess = intervalView.randomAccess();
while(cursorDepthMap.hasNext())
{
try {
cursorDepthMap.fwd();
randomAccess.setPosition( cursorDepthMap );
randomAccess.get().setReal( cursorDepthMap.get().getRealFloat() );
} catch (java.lang.ArrayIndexOutOfBoundsException e) {
continue;
}
cursorDepthMap.fwd();
randomAccess.setPosition( cursorDepthMap );
randomAccess.get().setReal( cursorDepthMap.get().getRealFloat() );
}
}
};
......@@ -239,9 +227,8 @@ public class MinCostZSurfaceMT_Ops< T extends RealType<T> & NativeType< T >> ext
ij.ui().showUI();
// ask the user for a file to open
// final File file = ij.ui().chooseFile(null, "open");
final File file = ij.ui().chooseFile(null, "open");
final File file = new File("/Users/moon/temp/20190129_wingsdic_400nm20E_upcrawl_000-z1-z213.tif");
if (file != null) {
// load the dataset
final Dataset dataset = ij.scifio().datasetIO().open(file.getPath());
......
......@@ -23,7 +23,7 @@ import org.scijava.plugin.Plugin;
import java.io.File;
@Plugin(type = Op.class, menuPath = "Plugins>MinCostZSurface", name="MinCostZSurface", headless = true, label="MinCostZSurface")
@Plugin(type = Op.class, menuPath = "Plugins>MinCostZSurface>Single Thread", name="MinCostZSurface", headless = true, label="MinCostZSurface")
public class MinCostZSurface_Ops< T extends RealType<T> & NativeType< T > > extends AbstractOp {
//
// should implement ops if I want to benefit the matching mechanism
......
package de.mpicbg.scf.mincostsurface;
import net.imagej.Dataset;
import net.imagej.ImageJ;
import net.imagej.ops.AbstractOp;
import net.imagej.ops.Op;
import net.imagej.ops.OpService;
import net.imglib2.img.Img;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import org.scijava.ItemIO;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import java.io.File;
/**
* Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility
* Organization: MPI-CBG Dresden
* Date: October 2019
*/
@Plugin(type = Op.class, name="zMapResliceMT", headless = true, label="zMapResliceMT", visible=true, menuPath = "Plugins>Z map reslice>Multi Threads")
public class ResliceAlongZMapMT_Ops < T extends RealType<T> & NativeType< T >, U extends RealType<U>> extends AbstractOp {
@Parameter( label = "input image" )
private Img input;
@Parameter ( label = "z map" )
private Img zMap;
@Parameter ( label = "slice Above the z map" )
private int sliceAbove;
@Parameter ( label = "slice Below the z map" )
private int sliceBelow;
@Parameter ( label = "Number of Threads")
private int numThreads;
// output
@Parameter (type = ItemIO.OUTPUT)
private Img<T> outputExcerpt;
@Parameter
OpService op;
@Override
public void run() {
Img<T> input_img = (Img<T>) input;
Img<U> zMap_img = (Img<U>) zMap;
outputExcerpt = img_utils.ZSurface_reslice3(input_img, zMap_img, sliceAbove, sliceBelow, numThreads);
}
public static void main(final String... args) throws Exception {
// create the ImageJ application context with all available services
final ImageJ ij = new ImageJ();
ij.ui().showUI();
// ask the user for a file to open
final File file = ij.ui().chooseFile(null, "open");
if (file != null) {
// load the dataset
final Dataset dataset = ij.scifio().datasetIO().open(file.getPath());
// show the image
ij.ui().show(dataset);
// invoke the plugin
ij.command().run(ResliceAlongZMap_Ops.class, true);
}
}
}
......@@ -30,7 +30,7 @@ import java.io.File;
//@Plugin(type = Command.class, menuPath = "Plugins>Z map reslice")
@Plugin(type = Op.class, name="zMapReslice", headless = true, label="zMapReslice", visible=true, menuPath = "Plugins>Z map reslice")
@Plugin(type = Op.class, name="zMapReslice", headless = true, label="zMapReslice", visible=true, menuPath = "Plugins>Z map reslice>Single Thread")
public class ResliceAlongZMap_Ops < T extends NumericType<T> & NativeType< T >, U extends RealType<U> > extends AbstractOp {
......
package de.mpicbg.scf.mincostsurface;
import net.imglib2.multithreading.SimpleMultiThreading;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.NumericType;
import net.imglib2.type.numeric.RealType;
......@@ -9,6 +10,7 @@ import net.imglib2.Interval;
import net.imglib2.RandomAccess;
import net.imglib2.RealRandomAccess;
import net.imglib2.util.Intervals;
import net.imglib2.view.IntervalView;
import net.imglib2.view.Views;
import net.imglib2.algorithm.gauss3.Gauss3;
import net.imglib2.exception.IncompatibleTypeException;
......@@ -18,7 +20,6 @@ import net.imglib2.img.array.ArrayImgFactory;
import net.imglib2.interpolation.randomaccess.LanczosInterpolatorFactory;
import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory;
import net.imglib2.interpolation.randomaccess.NearestNeighborInterpolatorFactory;
//import ij.IJ;
// Todo: add the volume reslice
......@@ -231,8 +232,97 @@ public class img_utils {
return excerpt;
}
public static <T extends NumericType<T> & NativeType<T> , U extends RealType<U> >
Img<T> ZSurface_reslice3(Img<T> input, Img<U> depthMap, int sliceOnTop, int sliceBelow, int numThreads)
{
final long[][] inputOffset = createOffset(input, numThreads);
int nDim = input.numDimensions();
long[] dims = new long[nDim];
input.dimensions(dims);
long output_height = sliceOnTop + sliceBelow + 1;
final ImgFactory< T > imgFactory = new ArrayImgFactory< T >();
final Img< T > excerpt = imgFactory.create( new long[] {dims[0],dims[1], output_height} , input.firstElement().createVariable() );
long unit = dims[1] / numThreads;
final long[][] offset = createOffset(excerpt, numThreads);
final long[][] depthMapOffset = createOffset(depthMap, numThreads);
NLinearInterpolatorFactory<T> NLinterp_factory = new NLinearInterpolatorFactory<T>();
final Thread[] threads = SimpleMultiThreading.newThreads( numThreads );
for ( int i = 0; i < threads.length; i++ )
{
int finalI = i;
threads[ i ] = new Thread( "ZSurface Reslice thread " + finalI)
{
@Override
public void run()
{
IntervalView< T > excerptIntervalView = createIntervalView(excerpt, offset[finalI], unit);
RandomAccess< T > randomAccess = excerptIntervalView.randomAccess();
Cursor< T > excerpt_cursor = excerptIntervalView.cursor();
IntervalView< T > intervalView = Views.offset( input, inputOffset[finalI] );
RealRandomAccess< T > inputx_Real = Views.interpolate( Views.extendBorder( intervalView ), NLinterp_factory ).realRandomAccess();
IntervalView< U > depthMapIntervalView = Views.offset(depthMap, depthMapOffset[finalI] );
RandomAccess< U > depthMapx = depthMapIntervalView.randomAccess();
float z_map;
int[] tmp_pos = new int[nDim];
while(excerpt_cursor.hasNext())
{
excerpt_cursor.fwd();
excerpt_cursor.localize(tmp_pos);
depthMapx.setPosition(new int[] {tmp_pos[0],tmp_pos[1]});
z_map = depthMapx.get().getRealFloat();
inputx_Real.setPosition(new float[] {(float)tmp_pos[0],(float)tmp_pos[1], (float)(tmp_pos[2]-(sliceOnTop)) + z_map });
randomAccess.setPosition(excerpt_cursor);
randomAccess.get().set( inputx_Real.get() );
}
}
};
}
SimpleMultiThreading.startAndJoin( threads );
return excerpt;
}
private static <T> IntervalView<T> createIntervalView(Img<T> img, long[] offset, long unit) {
long[] max = new long[img.numDimensions()];
img.dimensions(max);
max[1] = unit;
if( (offset[1] + unit) > img.dimension(1) ) {
max[1] = img.dimension(1) - unit * (offset.length - 1);
System.out.println(max[1]);
}
return Views.offsetInterval(img, offset, max);
}
static long[][] createOffset(Img img, int numThreads) {
final long[][] offset = new long[ numThreads ][ img.numDimensions() ];
for ( int d = 0; d < offset.length; d++ )
{
offset[d] = new long[img.numDimensions()];
for (int i = 0; i < offset[d].length; i++) {
offset[d][i] = 0;
}
// height
offset[d][1] = img.dimension( 1 ) / numThreads * d;
}
return offset;
}
/**
*
* @param input a 3D image
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment