ImageJ Forum
@@ -168,7 +182,37 @@
-
+
+
+ net.imglib2
+ imglib2
+
+
+ net.imglib2
+ imglib2-algorithm
+
+
+ net.imglib2
+ imglib2-algorithm-gpl
+
+
+ net.imglib2
+ imglib2-ij
+
+
+ net.imglib2
+ imglib2-roi
+
+
+
+ math.geom2d
+ javaGeom
+ 0.11.1
+
+
+ sc.fiji
+ bigdataviewer-vistools
+
diff --git a/removeSpotGUI/CovistoRemovePanel.java b/removeSpotGUI/CovistoRemovePanel.java
new file mode 100644
index 0000000..aa16658
--- /dev/null
+++ b/removeSpotGUI/CovistoRemovePanel.java
@@ -0,0 +1,50 @@
+package removeSpotGUI;
+
+import java.awt.GridBagConstraints;
+import java.awt.Label;
+import java.awt.TextField;
+
+import javax.swing.JPanel;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.TitledBorder;
+
+public class CovistoRemovePanel {
+
+
+
+
+ public static JPanel RemovePanel = new JPanel();
+ public static Label removeText = new Label("Distance Threshold = ", Label.CENTER);
+
+ public static TextField inputFieldSpot;
+ public static double distthreshold = 10;
+
+ public static JPanel RemoveselectPanel() {
+
+ layoutManager.Setlayout.LayoutSetter(RemovePanel);
+
+ inputFieldSpot = new TextField(5);
+ inputFieldSpot.setText(Double.toString(distthreshold));
+
+
+ Border removeborder = new CompoundBorder(new TitledBorder("Spurios Spot removal"), new EmptyBorder(layoutManager.Setlayout.c.insets));
+
+
+ RemovePanel.add(removeText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
+ GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0));
+
+ RemovePanel.add(inputFieldSpot, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
+ GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0));
+
+
+ RemovePanel.setBorder(removeborder);
+
+ return RemovePanel;
+
+ }
+
+
+
+}
diff --git a/scrollbar/Utility.java b/scrollbar/Utility.java
new file mode 100644
index 0000000..6c52301
--- /dev/null
+++ b/scrollbar/Utility.java
@@ -0,0 +1,95 @@
+package scrollbar;
+
+import javax.swing.JProgressBar;
+
+public class Utility {
+
+
+
+ public static float computeValueFromScrollbarPosition(final int scrollbarPosition, final float min,
+ final float max, final int scrollbarSize) {
+ return min + (scrollbarPosition / (float) scrollbarSize) * (max - min);
+ }
+
+ public static float computeIntValueFromScrollbarPosition(final int scrollbarPosition, final float min,
+ final float max, final int scrollbarSize) {
+ return min + (scrollbarPosition / (max)) * (max - min);
+ }
+
+ public static int computeScrollbarPositionFromValue(final float sigma, final float min, final float max,
+ final int scrollbarSize) {
+ return Math.round(((sigma - min) / (max - min)) * scrollbarSize);
+ }
+
+ public static int computeIntScrollbarPositionFromValue(final float thirdDimensionslider, final float min,
+ final float max, final int scrollbarSize) {
+ return Math.round(((thirdDimensionslider - min) / (max - min)) * max);
+ }
+
+ public static float computeSigma2(final float sigma1, final int sensitivity) {
+ final float k = (float) computeK(sensitivity);
+ final float[] sigma = computeSigma(k, sigma1);
+
+ return sigma[1];
+ }
+ public static double computeK( final float stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); }
+ public static double computeK( final int stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); }
+ public static float computeKWeight( final float k ) { return 1.0f / (k - 1.0f); }
+ public static float[] computeSigma( final float k, final float initialSigma )
+ {
+ final float[] sigma = new float[ 2 ];
+
+ sigma[ 0 ] = initialSigma;
+ sigma[ 1 ] = sigma[ 0 ] * k;
+
+ return sigma;
+ }
+
+ public static void SetProgressBar(JProgressBar jpb, double percent) {
+
+ jpb.setValue((int) Math.round(percent));
+ jpb.setOpaque(true);
+ jpb.setStringPainted(true);
+ jpb.setString("Finding MT ends");
+
+ }
+
+
+ public static void SetProgressBar(JProgressBar jpb, double percent, String message) {
+
+ jpb.setValue((int) Math.round(percent));
+ jpb.setOpaque(true);
+ jpb.setStringPainted(true);
+ jpb.setString(message);
+
+ }
+
+ public static void SetProgressBar(JProgressBar jpb) {
+ jpb.setValue(0);
+ jpb.setIndeterminate(true);
+ jpb.setOpaque(true);
+ jpb.setStringPainted(true);
+ jpb.setString("Pre-processing Image");
+
+ }
+
+ public static void SetProgressBarTime(JProgressBar jpb, double percent, int framenumber, int thirdDimsize) {
+
+ jpb.setValue((int) percent);
+ jpb.setOpaque(true);
+ jpb.setStringPainted(true);
+ jpb.setString("Time point = " + framenumber + "/" + thirdDimsize);
+
+ }
+
+ public static void SetProgressBarTime(JProgressBar jpb, double percent, int framenumber, int thirdDimsize,
+ String message) {
+
+ jpb.setValue((int) percent);
+ jpb.setOpaque(true);
+ jpb.setStringPainted(true);
+ jpb.setString(message + "= " + framenumber + "/" + thirdDimsize);
+
+ }
+
+}
diff --git a/src/main/java/analyzers/EdgeAnalyzer.java b/src/main/java/analyzers/EdgeAnalyzer.java
new file mode 100644
index 0000000..699f676
--- /dev/null
+++ b/src/main/java/analyzers/EdgeAnalyzer.java
@@ -0,0 +1,52 @@
+
+package analyzers;
+
+import java.util.Collection;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+
+import linkers.Model3D;
+import net.imglib2.algorithm.Benchmark;
+import net.imglib2.algorithm.MultiThreaded;
+import utility.FeatureAnalyzer3D;
+
+/**
+ * Interface for analyzers that can compute scalar numerical features for an
+ * edge of a TrackMate model. An edge, or a link, is the single link that exists
+ * between two spots after tracking.
+ *
+ * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com>
+ */
+public interface EdgeAnalyzer extends Benchmark, FeatureAnalyzer3D, MultiThreaded
+{
+
+ /**
+ * Scores a collection of link between two spots. The results must be stored
+ * in the {@link fiji.plugin.trackmate.FeatureModel}.
+ *
+ * Note: ideally concrete implementation should work in a multi-threaded
+ * fashion for performance reason, when possible.
+ *
+ * @param edges
+ * the collection of edges whose features are to be calculated.
+ * @param model
+ * the {@link Model} they belong to.
+ */
+ public void process( final Collection< DefaultWeightedEdge > edges, Model3D model );
+
+ /**
+ * Returns true
if this analyzer is a local analyzer. That is:
+ * a modification that affects only one edge requires the edge features to
+ * be re-calculated only for this edge. If false
, any model
+ * modification involving an edge will trigger a recalculation over the
+ * whole track this edge belong to.
+ *
+ * Example of local edge feature: the edge length (distance between the two
+ * spots). This one does not depend on other edge values.
+ *
+ * Example of non-local edge feature: the local curvature of the trajectory,
+ * which depends on the neighbor edges.
+ */
+ public boolean isLocal();
+
+}
diff --git a/src/main/java/analyzers/TrackAnalyzer.java b/src/main/java/analyzers/TrackAnalyzer.java
new file mode 100644
index 0000000..f55ebf7
--- /dev/null
+++ b/src/main/java/analyzers/TrackAnalyzer.java
@@ -0,0 +1,66 @@
+package analyzers;
+
+import java.util.Collection;
+
+import linkers.Model3D;
+import net.imglib2.algorithm.Benchmark;
+import net.imglib2.algorithm.MultiThreaded;
+import utility.FeatureAnalyzer3D;
+
+
+/**
+ * Mother interface for the classes that can compute the feature of tracks.
+ * Target tracks are given through a {@link Model} and their IDs. Therefore and
+ * ideally, concrete implementations can be made stateless.
+ *
+ * Note: ideally concrete implementation should work in a multi-threaded fashion
+ * for performance reason, when possible.
+ *
+ * For {@link TrackAnalyzer}s, there is a mechanism intended to maintain the
+ * model integrity against manual, small changes. Something as simple as
+ * removing a spot in the middle of a track will generate two new tracks, which
+ * will invalidate all feature values for the old track. Analyzers are notified
+ * of such events, so that they can recompute track features after the change.
+ *
+ * A simple way would be to recompute all track features at once, but this might
+ * be too long and overkill for changes that do not affect all tracks
+ * (e.g. adding a lonely spot, or a new track is likely not to affect all
+ * tracks in some case).
+ *
+ * So the {@link #process(Collection, Model)} will be called selectively on new
+ * or modified tracks every time a change happens. It will be called from the
+ * {@link Model} after a {@link Model#endUpdate()}, before any listener gets
+ * notified.
+ *
+ * @author Jean-Yves Tinevez
+ */
+public interface TrackAnalyzer extends Benchmark, FeatureAnalyzer3D, MultiThreaded
+{
+
+ /**
+ * Compute the features of the track whose ID is given.
+ *
+ * @param trackIDs
+ * the IDs of the track whose features are to be calculated.
+ * @param model
+ * the {@link Model} from which actual tracks are to be
+ * retrieved.
+ */
+ public void process( final Collection< Integer > trackIDs, final Model3D model );
+
+ /**
+ * Returns true
if this analyzer is a local analyzer. That is:
+ * a modification that affects only one track requires the track features to
+ * be re-calculated only for this track. If false
, any model
+ * modification involving edges will trigger a recalculation over all the
+ * visible tracks of the model.
+ *
+ * Example of a local track feature: the number of spots in a track. It does
+ * not depend on the number of spots in other tracks.
+ *
+ * Example of a non-local track feature: the rank of the track sorted by its
+ * number of spots, compared to other tracks.
+ */
+ public boolean isLocal();
+
+}
diff --git a/src/main/java/batchMode/ExecuteBatch.java b/src/main/java/batchMode/ExecuteBatch.java
index 7680a97..75cb417 100644
--- a/src/main/java/batchMode/ExecuteBatch.java
+++ b/src/main/java/batchMode/ExecuteBatch.java
@@ -44,7 +44,6 @@
import net.imglib2.util.Pair;
import net.imglib2.util.ValuePair;
import net.imglib2.view.Views;
-import pluginTools.ComputeAngles;
import pluginTools.ComputeCurvature;
import pluginTools.InteractiveSimpleEllipseFit;
import pluginTools.InteractiveSimpleEllipseFit.ValueChange;
diff --git a/src/main/java/comboSliderTextbox/SliderBoxGUI.java b/src/main/java/comboSliderTextbox/SliderBoxGUI.java
new file mode 100644
index 0000000..a0c3fc5
--- /dev/null
+++ b/src/main/java/comboSliderTextbox/SliderBoxGUI.java
@@ -0,0 +1,66 @@
+package comboSliderTextbox;
+
+import java.awt.GridBagConstraints;
+import java.awt.Label;
+import java.awt.Scrollbar;
+import java.awt.TextField;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollBar;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.TitledBorder;
+
+
+public class SliderBoxGUI {
+
+
+ final String string;
+ final float valueDimension;
+ final float valueMax;
+ final int scrollbarSize;
+ final JScrollBar valueslider;
+ final TextField inputFieldvalue;
+ final Label valueText;
+
+ public SliderBoxGUI(final String string, JScrollBar valueslider, TextField inputFieldvalue, Label valueText, final int scrollbarSize, final float valueDimension, final float valueMax) {
+
+ this.string = string;
+ this.scrollbarSize = scrollbarSize;
+ this.valueDimension = valueDimension;
+ this.valueMax = valueMax;
+ this.valueslider = valueslider;
+ this.inputFieldvalue = inputFieldvalue;
+ this.valueText = valueText;
+ }
+
+
+
+ public JPanel BuildDisplay() {
+
+
+ JPanel combosliderbox = new JPanel();
+
+ layoutManager.Setlayout.LayoutSetter(combosliderbox);
+
+
+
+ combosliderbox.add(valueText, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST,
+ GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0));
+
+ combosliderbox.add(valueslider, new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0, GridBagConstraints.WEST,
+ GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0));
+
+ combosliderbox.add(inputFieldvalue, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.WEST,
+ GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0));
+
+
+
+ return combosliderbox;
+
+
+
+ }
+
+}
diff --git a/src/main/java/common3D/BinaryCreation.java b/src/main/java/common3D/BinaryCreation.java
new file mode 100644
index 0000000..dee9dd1
--- /dev/null
+++ b/src/main/java/common3D/BinaryCreation.java
@@ -0,0 +1,103 @@
+package common3D;
+
+import java.util.ArrayList;
+
+import dogGUI.CovistoDogPanel;
+import ij.gui.Roi;
+import interactivePreprocessing.InteractiveMethods;
+import net.imglib2.Cursor;
+import net.imglib2.Point;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.algorithm.region.hypersphere.HyperSphere;
+import net.imglib2.img.Img;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.numeric.RealType;
+import net.imglib2.type.numeric.real.FloatType;
+import net.imglib2.view.Views;
+import utility.PreRoiobject;
+import visualization.DrawCircles;
+import net.imglib2.algorithm.region.Circles;
+public class BinaryCreation {
+
+
+ public static & NativeType> void CreateBinary(final InteractiveMethods parent, RandomAccessibleInterval inputimg, RandomAccessibleInterval outimg, int z, int t) {
+
+
+ ArrayList pointlist = parent.AllEvents.get(z);
+
+ for (double[] currentpoint : pointlist) {
+
+ Point point = new Point(2);
+ point.setPosition(new long[] {(long)currentpoint[0], (long)currentpoint[1]});
+ Circles.add(Views.expandZero(outimg, new long[] {(int)CovistoDogPanel.distthreshold + 1, (int)CovistoDogPanel.distthreshold + 1}), point, (int)CovistoDogPanel.distthreshold, new BitType(true));
+
+
+ }
+
+
+ }
+
+
+
+ public static & NativeType> void CreateBinaryDots(final InteractiveMethods parent, RandomAccessibleInterval inputimg, RandomAccessibleInterval outimg, int z, int t) {
+
+ Cursor incursor = Views.iterable(inputimg).localizingCursor();
+ RandomAccess outran = outimg.randomAccess();
+
+
+
+
+ while (incursor.hasNext()) {
+
+ incursor.fwd();
+ outran.setPosition(incursor);
+
+
+ ArrayList pointlist = parent.AllEvents.get(z);
+
+ for (double[] currentpoint : pointlist) {
+
+ if ( incursor.getIntPosition(0) == (int)Math.round(currentpoint[0]) && incursor.getIntPosition(1) == (int)Math.round(currentpoint[1]) ) {
+
+
+ HyperSphere< BitType > hyperSphere = new HyperSphere( outimg, incursor, 2 );
+
+ // set every value inside the sphere to 1
+ for ( BitType value : hyperSphere )
+ value.setOne();
+
+
+ }
+
+ }
+
+ }
+
+
+ }
+
+public static & NativeType> void CreateBinaryRoi(final InteractiveMethods parent, RandomAccessibleInterval inputimg, RandomAccessibleInterval outimg, final ArrayList Rois, int z, int t) {
+
+ Cursor incursor = Views.iterable(inputimg).localizingCursor();
+ RandomAccess outran = outimg.randomAccess();
+
+ while (incursor.hasNext()) {
+
+ incursor.fwd();
+ outran.setPosition(incursor);
+
+ for (Roi currentroi : Rois) {
+
+ if (currentroi.contains(incursor.getIntPosition(0), incursor.getIntPosition(1))) {
+
+ outran.get().setOne();
+
+ }
+
+ }
+
+ }
+}
+}
\ No newline at end of file
diff --git a/src/main/java/common3D/CommonWater.java b/src/main/java/common3D/CommonWater.java
new file mode 100644
index 0000000..b17b93c
--- /dev/null
+++ b/src/main/java/common3D/CommonWater.java
@@ -0,0 +1,139 @@
+package common3D;
+
+import distanceTransform.DistWatershed;
+import distanceTransform.WatershedBinary;
+import interactivePreprocessing.InteractiveMethods;
+import interactivePreprocessing.InteractiveMethods.ValueChange;
+import net.imglib2.Cursor;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.array.ArrayImgFactory;
+import net.imglib2.img.display.imagej.ImageJFunctions;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.numeric.integer.IntType;
+import net.imglib2.type.numeric.real.FloatType;
+import net.imglib2.view.Views;
+import preProcessing.GetLocalmaxminMT;
+import preProcessing.GlobalThresholding;
+import timeGUI.CovistoTimeselectPanel;
+import watershedGUI.CovistoWatershedPanel;
+import zGUI.CovistoZselectPanel;
+
+public class CommonWater {
+
+ public static void Watershed( final InteractiveMethods parent,RandomAccessibleInterval newimg,RandomAccessibleInterval bitimg,RandomAccessibleInterval intimg, int t, int z ) {
+
+
+
+
+ CovistoZselectPanel.thirdDimension = z;
+ CovistoTimeselectPanel.fourthDimension = t;
+
+ parent.CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg, z, CovistoZselectPanel.thirdDimensionSize, t,
+ CovistoTimeselectPanel.fourthDimensionSize);
+ parent.updatePreview(ValueChange.THIRDDIMmouse);
+
+ RandomAccessibleInterval currentbitimg = utility.CovistoSlicer.getCurrentView(bitimg, z, CovistoZselectPanel.thirdDimensionSize, t,
+ CovistoTimeselectPanel.fourthDimensionSize);
+
+ RandomAccessibleInterval currentintimg = utility.CovistoSlicer.getCurrentView(intimg, z, CovistoZselectPanel.thirdDimensionSize, t,
+ CovistoTimeselectPanel.fourthDimensionSize);
+
+ RandomAccessibleInterval currentnewimg = utility.CovistoSlicer.getCurrentView(newimg, z, CovistoZselectPanel.thirdDimensionSize, t,
+ CovistoTimeselectPanel.fourthDimensionSize);
+
+ processSlice(parent, parent.CurrentView, currentintimg, currentbitimg, currentnewimg);
+
+
+
+
+
+
+
+ }
+ public static void processSlice(final InteractiveMethods parent, RandomAccessibleInterval< FloatType > slice, RandomAccessibleInterval< IntType > intoutputslice, RandomAccessibleInterval< BitType > bitoutputslice,
+ RandomAccessibleInterval< FloatType > distslice) {
+
+
+ RandomAccessibleInterval bitimg = new ArrayImgFactory().create(slice, new BitType());
+
+
+
+ if(parent.autothreshwater) {
+ CovistoWatershedPanel.thresholdWater = (float) ( GlobalThresholding.AutomaticThresholding(slice));
+ CovistoWatershedPanel.thresholdWaterslider.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoWatershedPanel.thresholdWater, CovistoWatershedPanel.thresholdMinWater,
+ CovistoWatershedPanel.thresholdMaxWater, CovistoWatershedPanel.scrollbarSize));
+ CovistoWatershedPanel.watertext.setText(CovistoWatershedPanel.waterstring + " = " + CovistoWatershedPanel.thresholdWater );
+ CovistoWatershedPanel.thresholdWaterslider.validate();
+ CovistoWatershedPanel.thresholdWaterslider.repaint();
+ }
+ GetLocalmaxminMT.ThresholdingMTBit(slice, bitimg, CovistoWatershedPanel.thresholdWater);
+
+ RandomAccessibleInterval waterint = null;
+ RandomAccessibleInterval distwater = null;
+
+
+ if (CovistoWatershedPanel.disttransform) {
+ DistWatershed WaterafterDisttransform = new DistWatershed(parent, slice, bitimg,
+ parent.jpb, parent.apply3D);
+
+ WaterafterDisttransform.execute();
+ waterint = WaterafterDisttransform.getResult();
+ distwater = WaterafterDisttransform.getDistanceTransformedimg();
+
+ }
+ else {
+
+ WatershedBinary Wateronly = new WatershedBinary(bitimg);
+ Wateronly.process();
+ waterint = Wateronly.getResult();
+
+ }
+
+ Cursor< BitType > bitcursor = Views.iterable(bitoutputslice).localizingCursor();
+ Cursor< IntType > intcursor = Views.iterable(intoutputslice).localizingCursor();
+
+
+
+ RandomAccess ranac = bitimg.randomAccess();
+ RandomAccess intranac = waterint.randomAccess();
+
+
+ while(bitcursor.hasNext()) {
+
+ bitcursor.fwd();
+
+ ranac.setPosition(bitcursor);
+
+ bitcursor.get().set(ranac.get());
+
+
+ }
+
+ while(intcursor.hasNext()) {
+
+ intcursor.fwd();
+
+ intranac.setPosition(intcursor);
+
+ intcursor.get().set(intranac.get());
+
+
+ }
+ if(distwater!=null) {
+ Cursor< FloatType > distcursor = Views.iterable(distslice).localizingCursor();
+ RandomAccess distranac = distwater.randomAccess();
+ while(distcursor.hasNext()) {
+
+ distcursor.fwd();
+
+ distranac.setPosition(distcursor);
+
+ distcursor.get().set(distranac.get());
+
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/costMatrix/CostFunction.java b/src/main/java/costMatrix/CostFunction.java
new file mode 100644
index 0000000..ebcdbaa
--- /dev/null
+++ b/src/main/java/costMatrix/CostFunction.java
@@ -0,0 +1,32 @@
+package costMatrix;
+
+/**
+ * Interface representing a function that can calculate the cost to link a
+ * source object to a target object.
+ *
+ * @author Jean-Yves Tinevez - 2014
+ *
+ * @param
+ * the type of the sources.
+ * @param
+ * the type of the targets.
+ */
+public interface CostFunction< K, J >
+{
+
+ /**
+ * Returns the cost to link two objects.
+ *
+ * @param source
+ * the source object.
+ * @param target
+ * the target object.
+ * @return the cost as a double.
+ */
+ public double linkingCost( K source, J target );
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/costMatrix/CostMatrixCreator.java b/src/main/java/costMatrix/CostMatrixCreator.java
new file mode 100644
index 0000000..bdc5084
--- /dev/null
+++ b/src/main/java/costMatrix/CostMatrixCreator.java
@@ -0,0 +1,63 @@
+package costMatrix;
+
+
+import java.util.List;
+
+import net.imglib2.algorithm.Benchmark;
+import net.imglib2.algorithm.OutputAlgorithm;
+
+/**
+ * Interface for function that can generate a {@link SparseCostMatrix} from
+ * assignment candidates.
+ *
+ * @author Jean-Yves Tinevez
+ *
+ */
+public interface CostMatrixCreator< K extends Comparable< K >, J extends Comparable< J >> extends Benchmark, OutputAlgorithm< SparseCostMatrix >
+{
+
+ /**
+ * Returns the list of sources in the generated cost matrix.
+ *
+ * @return the list of object, such that sourceList.get( i )
is
+ * the source corresponding to the row i
in the
+ * generated cost matrix.
+ * @see #getTargetList()
+ * @see #getResult()
+ */
+ public List< K > getSourceList();
+
+ /**
+ * Returns the list of targets in the generated cost matrix.
+ *
+ * @return the list of objects, such that targetList.get( j )
+ * is the target corresponding to the column j
in the
+ * generated cost matrix.
+ * @see #getSourceList()
+ * @see #getResult()
+ */
+ public List< J > getTargetList();
+
+ /**
+ * Returns the value of the no-linking alternative cost for the specified
+ * source.
+ *
+ * @param source
+ * the source object.
+ * @return the alternative cost. Belongs to the list returned by
+ * {@link #getSourceList()}.
+ */
+ public double getAlternativeCostForSource( K source );
+
+ /**
+ * Returns the value of the no-linking alternative cost for the specified
+ * target.
+ *
+ * @param target
+ * the target object. Belongs to the list returned by
+ * {@link #getTargetList()}.
+ * @return the alternative cost.
+ */
+ public double getAlternativeCostForTarget( J target );
+
+}
diff --git a/src/main/java/costMatrix/DefaultCostMatrixCreator.java b/src/main/java/costMatrix/DefaultCostMatrixCreator.java
new file mode 100644
index 0000000..40b0a82
--- /dev/null
+++ b/src/main/java/costMatrix/DefaultCostMatrixCreator.java
@@ -0,0 +1,265 @@
+package costMatrix;
+
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+import net.imglib2.util.Util;
+
+/**
+ * A {@link CostMatrixCreator} that build a cost matrix from 3 lists containing
+ * the sources, the targets and the associated costs.
+ *
+ * @author Jean-Yves Tinevez - 2014
+ *
+ * @param
+ */
+public class DefaultCostMatrixCreator< K extends Comparable< K >, J extends Comparable< J > > implements CostMatrixCreator< K, J >
+{
+
+ private static final String BASE_ERROR_MESSAGE = "[DefaultCostMatrixCreator] ";
+
+ private SparseCostMatrix scm;
+
+ private ArrayList< K > uniqueRows;
+
+ private ArrayList< J > uniqueCols;
+
+ private long processingTime;
+
+ private String errorMessage;
+
+ private double alternativeCost;
+
+ private final List< K > rows;
+
+ private final List< J > cols;
+
+ private final double[] costs;
+
+ private final double alternativeCostFactor;
+
+ private final double percentile;
+
+ public DefaultCostMatrixCreator( final List< K > rows, final List< J > cols, final double[] costs, final double alternativeCostFactor, final double percentile )
+ {
+ this.rows = rows;
+ this.cols = cols;
+ this.costs = costs;
+ this.alternativeCostFactor = alternativeCostFactor;
+ this.percentile = percentile;
+ }
+
+ @Override
+ public long getProcessingTime()
+ {
+ return processingTime;
+ }
+
+ @Override
+ public SparseCostMatrix getResult()
+ {
+ return scm;
+ }
+
+ @Override
+ public boolean checkInput()
+ {
+ if ( rows == null || rows.isEmpty() )
+ {
+ errorMessage = BASE_ERROR_MESSAGE + "The row list is null or empty.";
+ return false;
+ }
+ if ( rows.size() != cols.size() ) {
+ errorMessage = BASE_ERROR_MESSAGE +"Row and column lists do not have the same number of elements. Found " + rows.size() + " and " + cols.size() + "." ;
+ return false;
+ }
+ if ( rows.size() != costs.length )
+ {
+ errorMessage = BASE_ERROR_MESSAGE + "Row list and cost array do not have the same number of elements. Found " + rows.size() + " and " + costs.length + ".";
+ return false;
+ }
+ if ( alternativeCostFactor <= 0 )
+ {
+ errorMessage = BASE_ERROR_MESSAGE + "The alternative cost factor must be greater than 0. Was: " + alternativeCostFactor + ".";
+ return false;
+ }
+ if ( percentile < 0 || percentile > 1 )
+ {
+ errorMessage = BASE_ERROR_MESSAGE + "The percentile must no be smaller than 0 or greater than 1. Was: " + percentile;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean process()
+ {
+ uniqueRows = new ArrayList< K >( new HashSet< K >( rows ) );
+ Collections.sort( uniqueRows );
+ uniqueCols = new ArrayList< J >( new HashSet< J >( cols ) );
+ Collections.sort( uniqueCols );
+
+ final List< Assignment > assignments = new ArrayList< Assignment >( costs.length );
+ for ( int i = 0; i < costs.length; i++ )
+ {
+ final K rowObj = rows.get( i );
+ final J colObj = cols.get( i );
+ final int r = Collections.binarySearch( uniqueRows, rowObj );
+ final int c = Collections.binarySearch( uniqueCols, colObj );
+ assignments.add( new Assignment( r, c, costs[ i ] ) );
+ }
+ Collections.sort( assignments );
+
+ // Test we do not have duplicates.
+ Assignment previousAssgn = assignments.get( 0 );
+ for ( int i = 1; i < assignments.size(); i++ )
+ {
+ final Assignment assgn = assignments.get( i );
+ if ( assgn.equals( previousAssgn ) )
+ {
+ errorMessage = BASE_ERROR_MESSAGE + "Found duplicate assignment at index: " + assgn + ".";
+ return false;
+ }
+ previousAssgn = assgn;
+ }
+
+ final int nRows = uniqueRows.size();
+ final int nCols = uniqueCols.size();
+ final int[] kk = new int[ costs.length ];
+ final int[] number = new int[ nRows ];
+ final double[] cc = new double[ costs.length ];
+
+ Assignment a = assignments.get( 0 );
+ kk[ 0 ] = a.c;
+ cc[ 0 ] = a.cost;
+ int currentRow = a.r;
+ int nOfEl = 0;
+ for ( int i = 1; i < assignments.size(); i++ )
+ {
+ a = assignments.get( i );
+
+ kk[ i ] = a.c;
+ cc[ i ] = a.cost;
+ nOfEl++;
+
+ if ( a.r != currentRow )
+ {
+ number[ currentRow ] = nOfEl;
+ nOfEl = 0;
+ currentRow = a.r;
+ }
+ }
+ number[ currentRow ] = nOfEl + 1;
+
+ scm = new SparseCostMatrix( cc, kk, number, nCols );
+
+ alternativeCost = computeAlternativeCosts();
+
+ return true;
+ }
+
+ protected double computeAlternativeCosts()
+ {
+ if ( percentile == 1 ) { return alternativeCostFactor * Util.max( costs ); }
+ return alternativeCostFactor * Util.percentile( costs, percentile );
+ }
+
+ @Override
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ @Override
+ public List< K > getSourceList()
+ {
+ return uniqueRows;
+ }
+
+ @Override
+ public List< J > getTargetList()
+ {
+ return uniqueCols;
+ }
+
+
+ @Override
+ public double getAlternativeCostForSource( final K source )
+ {
+ return alternativeCost;
+ }
+
+ @Override
+ public double getAlternativeCostForTarget( final J target )
+ {
+ return alternativeCost;
+ }
+
+ public final static class Assignment implements Comparable< Assignment >
+ {
+ private final int r;
+
+ private final int c;
+
+ private final double cost;
+
+ public Assignment( final int r, final int c, final double cost )
+ {
+ this.r = r;
+ this.c = c;
+ this.cost = cost;
+ }
+
+ @Override
+ public int compareTo( final Assignment o )
+ {
+ if ( r == o.r ) { return c - o.c; }
+ return r - o.r;
+ }
+
+ @Override
+ public boolean equals( final Object obj )
+ {
+ if ( obj instanceof Assignment )
+ {
+ final Assignment o = ( Assignment ) obj;
+ return r == o.r && c == o.c;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 23;
+ hash = hash * 31 + r;
+ hash = hash * 31 + c;
+ return hash;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Assignment r = " + r + ", c = " + c + ", cost = " + cost;
+ }
+
+ public int getC()
+ {
+ return c;
+ }
+
+ public int getR()
+ {
+ return r;
+ }
+
+ public double getCost()
+ {
+ return cost;
+ }
+ }
+}
diff --git a/src/main/java/costMatrix/IntensityDiffCostFunction.java b/src/main/java/costMatrix/IntensityDiffCostFunction.java
new file mode 100644
index 0000000..f95cff6
--- /dev/null
+++ b/src/main/java/costMatrix/IntensityDiffCostFunction.java
@@ -0,0 +1,19 @@
+package costMatrix;
+
+import utility.PreRoiobject;
+
+public class IntensityDiffCostFunction implements CostFunction< PreRoiobject, PreRoiobject >
+ {
+
+
+
+
+ @Override
+ public double linkingCost( final PreRoiobject source, final PreRoiobject target )
+ {
+ return source.IntensityDistanceTo(target );
+ }
+
+
+
+}
diff --git a/src/main/java/costMatrix/JaqamanLinkingCostMatrixCreator.java b/src/main/java/costMatrix/JaqamanLinkingCostMatrixCreator.java
new file mode 100644
index 0000000..213805c
--- /dev/null
+++ b/src/main/java/costMatrix/JaqamanLinkingCostMatrixCreator.java
@@ -0,0 +1,189 @@
+package costMatrix;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import costMatrix.SparseCostMatrix;
+import costMatrix.LAPJV;
+
+/**
+ * A {@link CostMatrixCreator} that can generate a cost matrix from a list of
+ * sources, a list of targets and a {@link CostFunction} that can generate a
+ * cost for any combination.
+ *
+ * @author Jean-Yves Tinevez - 2014
+ *
+ * @param
+ * @param
+ */
+public class JaqamanLinkingCostMatrixCreator< K extends Comparable< K >, J extends Comparable< J >> implements CostMatrixCreator< K, J >
+{
+
+ private static final String BASE_ERROR_MSG = "[JaqamanLinkingCostMatrixCreator] ";
+
+ private final Iterable< K > sources;
+
+ private final Iterable< J > targets;
+
+ private final CostFunction< K, J > costFunction;
+
+ private SparseCostMatrix scm;
+
+ private long processingTime;
+
+ private String errorMessage;
+
+ private final double costThreshold;
+
+ private List< K > sourceList;
+
+ private List< J > targetList;
+
+ private double alternativeCost;
+
+ private final double alternativeCostFactor;
+
+ private final double percentile;
+
+ public JaqamanLinkingCostMatrixCreator( final Iterable< K > sources, final Iterable< J > targets, final CostFunction< K, J > costFunction, final double costThreshold, final double alternativeCostFactor, final double percentile )
+ {
+ this.sources = sources;
+ this.targets = targets;
+ this.costFunction = costFunction;
+ this.costThreshold = costThreshold;
+ this.alternativeCostFactor = alternativeCostFactor;
+ this.percentile = percentile;
+ }
+
+ @Override
+ public boolean checkInput()
+ {
+ if ( null == sources || !sources.iterator().hasNext() )
+ {
+ errorMessage = BASE_ERROR_MSG + "The source list is empty or null.";
+ return false;
+ }
+ if ( null == targets || !targets.iterator().hasNext() )
+ {
+ errorMessage = BASE_ERROR_MSG + "The target list is empty or null.";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean process()
+ {
+ final long start = System.currentTimeMillis();
+
+ final List< K > accSources = new ArrayList< K >();
+ final List< J > accTargets = new ArrayList< J >();
+ final ResizableDoubleArray costs = new ResizableDoubleArray();
+
+ for ( final K source : sources )
+ {
+ for ( final J target : targets )
+ {
+
+ final double cost = costFunction.linkingCost( source, target );
+
+ if ( cost < costThreshold )
+ {
+ accSources.add( source );
+ accTargets.add( target );
+ costs.add( cost );
+ }
+ }
+ }
+ costs.trimToSize();
+
+ /*
+ * Check if accepted source or target lists are empty and deal with it.
+ */
+
+ if ( accSources.isEmpty() || accTargets.isEmpty() )
+ {
+
+ sourceList = Collections.emptyList();
+ targetList = Collections.emptyList();
+ alternativeCost = Double.NaN;
+ scm = null;
+ /*
+ * CAREFUL! We return null if no acceptable links are found.
+ */
+ }
+ else
+ {
+
+ final DefaultCostMatrixCreator< K, J > cmCreator = new DefaultCostMatrixCreator< K, J >( accSources, accTargets, costs.data, alternativeCostFactor, percentile );
+ if ( !cmCreator.checkInput() || !cmCreator.process() )
+ {
+ errorMessage = cmCreator.getErrorMessage();
+ return false;
+ }
+
+ scm = cmCreator.getResult();
+ sourceList = cmCreator.getSourceList();
+ targetList = cmCreator.getTargetList();
+ alternativeCost = cmCreator.computeAlternativeCosts();
+ }
+
+
+ final long end = System.currentTimeMillis();
+ processingTime = end - start;
+ return true;
+ }
+
+ @Override
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * Returns the cost matrix generated.
+ *
+ * Careful, it can be null
if not acceptable costs have been
+ * found for the specified configuration. In that case, the lists returned
+ * by {@link #getSourceList()} and {@link #getTargetList()} are empty.
+ *
+ * @return a new {@link SparseCostMatrix} or null
.
+ */
+ @Override
+ public SparseCostMatrix getResult()
+ {
+ return scm;
+ }
+
+ @Override
+ public List< K > getSourceList()
+ {
+ return sourceList;
+ }
+
+ @Override
+ public List< J > getTargetList()
+ {
+ return targetList;
+ }
+
+ @Override
+ public long getProcessingTime()
+ {
+ return processingTime;
+ }
+
+ @Override
+ public double getAlternativeCostForSource( final K source )
+ {
+ return alternativeCost;
+ }
+
+ @Override
+ public double getAlternativeCostForTarget( final J target )
+ {
+ return alternativeCost;
+ }
+
+}
diff --git a/src/main/java/costMatrix/LAPJV.java b/src/main/java/costMatrix/LAPJV.java
new file mode 100644
index 0000000..5d59965
--- /dev/null
+++ b/src/main/java/costMatrix/LAPJV.java
@@ -0,0 +1,525 @@
+package costMatrix;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import net.imglib2.algorithm.Benchmark;
+import net.imglib2.algorithm.OutputAlgorithm;
+import net.imglib2.util.Util;
+
+/**
+ * Implements the Jonker-Volgenant algorithm for linear assignment problems,
+ * tailored for sparse cost matrices.
+ *
+ * We rely on the {@link SparseCostMatrix} class to represent these costs. The
+ * implementation itself is an unlikely mix between:
+ *
+ * - my (JYT) limited understanding of the original Volgemant paper (
+ *
Volgenant. Linear and semi-assignment problems:
+ * A core oriented approach. Computers & Operations Research (1996) vol. 23 (10)
+ * pp. 917-932
);
+ * - my limited understanding of Lee Kamensky python implementation of the
+ * same algorithm in python using numpy for the CellProfilter project;
+ * - Johannes java implementation of the algorithm for non-sparse matrices.
+ *
+ *
+ * Computation time performance degrades significantly compared to the
+ * non-sparse version. Benchmarks show that when increasing the density from
+ * 0.1% to 70%, the computation time increased by a factor ranging from 1.5 to 7
+ * compared to the non-sparse version. For a given density, the comparison
+ * depends very weakly on the matrix size.
+ *
+ *
+ * @author Jean-Yves Tinevez - 2014
+ * @author Johannes Schindelin
+ * @see A. Volgenant
+ * "Linear and semi-assignment problems: A core oriented approach"
+ */
+public class LAPJV implements OutputAlgorithm< int[] >, Benchmark
+{
+
+ private static final String BASE_ERROR_MESSAGE = "[JonkerVolgenantSparseAlgorithm] ";
+
+ private int[] output;
+
+ private String errorMessage;
+
+ private long processingTime;
+
+ private final SparseCostMatrix cm;
+
+ /**
+ * Instantiates a new Jonker-Volgenant algorithm for the specified sparse
+ * cost matrix.
+ *
+ * @param cm
+ * the cost matrix of the linear assignment problem to solve.
+ */
+ public LAPJV( final SparseCostMatrix cm )
+ {
+ this.cm = cm;
+ }
+
+ @Override
+ public boolean process()
+ {
+ final long start = System.currentTimeMillis();
+
+ final int[] x = new int[ cm.nRows ];
+ final int[] y = new int[ cm.nCols ];
+ final double[] v = new double[ cm.nCols ];
+
+ final int[] col = new int[ cm.nCols ];
+ for ( int j = 0; j < col.length; j++ )
+ {
+ col[ j ] = j;
+ }
+
+ /*
+ * Column reduction
+ */
+
+ Arrays.fill( v, Double.MAX_VALUE );
+ for ( int i = 0; i < cm.nRows; i++ )
+ {
+ for ( int k = cm.start[ i ]; k < cm.start[ i ] + cm.number[ i ]; k++ )
+ {
+ final int j = cm.kk[ k ];
+ if ( cm.cc[ k ] < v[ j ] )
+ {
+ v[ j ] = cm.cc[ k ];
+ y[ j ] = i + 1;
+ }
+ }
+ }
+
+ for ( int j = cm.nCols - 1; j >= 0; j-- )
+ {
+ final int i = y[ j ] - 1;
+ if ( x[ i ] == 0 )
+ {
+ x[ i ] = j + 1;
+ }
+ else
+ {
+ if ( x[ i ] > 0 )
+ {
+ x[ i ] = -x[ i ];
+ }
+ y[ j ] = 0;
+ }
+ }
+
+ /*
+ * Reduction transfer.
+ */
+
+ int f = 0;
+ final int[] free = new int[ cm.nRows ];
+ for ( int i = 0; i < cm.nRows; i++ )
+ {
+ if ( x[ i ] == 0 )
+ {
+ // unassigned row in free-array
+ free[ f++ ] = i;
+ continue;
+ }
+ else if ( x[ i ] < 0 )
+ {
+ // no reduction transfer possible
+ x[ i ] = -x[ i ];
+ }
+ else
+ {
+ // reduction transfer from assigned row
+ final int j1 = x[ i ] - 1;
+ double min = Double.MAX_VALUE;
+ for ( int k = cm.start[ i ]; k < cm.start[ i ] + cm.number[ i ]; k++ )
+ {
+ final int j = cm.kk[ k ];
+ if ( j != j1 )
+ {
+ if ( cm.cc[ k ] - v[ j ] < min )
+ {
+ min = cm.cc[ k ] - v[ j ];
+ }
+ }
+ }
+ v[ j1 ] -= min;
+ }
+ }
+
+ if ( f == 0 ) { return true; }
+
+ /*
+ * Augmenting row reduction.
+ */
+
+ for ( int count = 0; count < 2; count++ )
+ {
+ int k = 0;
+ final int f0 = f;
+ f = 0;
+ while ( k < f0 )
+ {
+ final int i = free[ k++ ];
+ double v0 = Double.MAX_VALUE;
+ int j0 = 0, j1 = -1;
+ double vj = Double.MAX_VALUE;
+ for ( int kj = cm.start[ i ]; kj < cm.start[ i ] + cm.number[ i ]; kj++ )
+ {
+ final int j = cm.kk[ kj ];
+ final double h = cm.cc[ kj ] - v[ j ];
+ if ( h < vj )
+ {
+ if ( h > v0 )
+ {
+ vj = h;
+ j1 = j;
+ }
+ else
+ {
+ vj = v0;
+ v0 = h;
+ j1 = j0;
+ j0 = j;
+ }
+ }
+ }
+ int i0 = y[ j0 ] - 1;
+ if ( v0 < vj )
+ {
+ v[ j0 ] -= vj - v0;
+ }
+ else
+ {
+ if ( i0 >= 0 )
+ {
+ j0 = j1;
+ i0 = y[ j1 ] - 1;
+ }
+ }
+ if ( i0 >= 0 )
+ {
+ if ( v0 < vj )
+ {
+ free[ --k ] = i0;
+ }
+ else
+ {
+ free[ f++ ] = i0;
+ }
+ }
+ x[ i ] = j0 + 1;
+ y[ j0 ] = i + 1;
+ }
+ }
+
+ /*
+ * Augmentation.
+ */
+
+ final int f0 = f;
+ final double[] d = new double[ cm.nCols ];
+ final int[] pred = new int[ cm.nCols ];
+ for ( f = 0; f < f0; f++ )
+ {
+ final int i1 = free[ f ];
+ int low = 0, up = 0;
+ // initialize d- and pred-array
+ Arrays.fill( d, Double.MAX_VALUE );
+ for ( int k = cm.start[ i1 ]; k < cm.start[ i1 ] + cm.number[ i1 ]; k++ )
+ {
+ final int j = cm.kk[ k ];
+ d[ j ] = cm.cc[ k ] - v[ j ];
+ pred[ j ] = i1;
+ }
+ int last;
+ int i, j = -1;
+ double min = Double.MAX_VALUE;
+ LOOP: do
+ {
+ // find new columns with new value for minimum d
+ // unnecessary, even if it is in the paper:
+ // if (up == low)
+ {
+ last = low;
+ min = d[ col[ up++ ] ];
+ for ( int k = up; k < cm.nCols; k++ )
+ {
+ j = col[ k ];
+ final double h = d[ j ];
+ if ( h <= min )
+ {
+ if ( h < min )
+ {
+ up = low;
+ min = h;
+ }
+ col[ k ] = col[ up ];
+ col[ up++ ] = j;
+ }
+ }
+ for ( int h = low; h < up; h++ )
+ {
+ j = col[ h ];
+ if ( y[ j ] == 0 )
+ {
+ break LOOP;
+ }
+ }
+ }
+ // scan a row
+ do
+ {
+ final int j1 = col[ low++ ];
+ i = y[ j1 ] - 1;
+
+ final int kj1 = Arrays.binarySearch( cm.kk, cm.start[ i ], cm.start[ i ] + cm.number[ i ], j1 );
+ if ( kj1 < 0 )
+ {
+ continue;
+ }
+
+ final double u1 = cm.cc[ kj1 ] - v[ j1 ] - min;
+ for ( int k = up; k < cm.nCols; k++ )
+ {
+ j = col[ k ];
+ final int kj = Arrays.binarySearch( cm.kk, cm.start[ i ], cm.start[ i ] + cm.number[ i ], j );
+ if ( kj < 0 )
+ {
+ continue;
+ }
+
+ final double h = cm.cc[ kj ] - v[ j ] - u1;
+ if ( h < d[ j ] )
+ {
+ d[ j ] = h;
+ pred[ j ] = i;
+ if ( h == min )
+ {
+ if ( y[ j ] == 0 )
+ {
+ break LOOP;
+ }
+ col[ k ] = col[ up ];
+ col[ up++ ] = j;
+ }
+ }
+ }
+ }
+ while ( low != up );
+ }
+ while ( low == up );
+
+ // updating of column pieces
+ for ( int k = 0; k < last; k++ )
+ {
+ final int j0 = col[ k ];
+ v[ j0 ] += d[ j0 ] - min;
+ }
+
+ // augmentation
+ do
+ {
+ i = pred[ j ];
+ y[ j ] = i + 1;
+ final int k = j;
+ j = x[ i ] - 1;
+ x[ i ] = k + 1;
+ }
+ while ( i1 != i );
+ }
+
+ /*
+ * Terminate and prepare outputs.
+ */
+
+ this.output = new int[ x.length ];
+ for ( int i = 0; i < x.length; i++ )
+ {
+ output[ i ] = x[ i ] - 1;
+ }
+
+ final long end = System.currentTimeMillis();
+ processingTime = end - start;
+ return true;
+ }
+
+ /*
+ * ALGORITHM METHODS
+ */
+
+ @Override
+ public boolean checkInput()
+ {
+ if ( cm.nRows > cm.nCols )
+ {
+ errorMessage = BASE_ERROR_MESSAGE + "This solver converges only if the cost matrix has more rows than column. Found " + cm.nRows + " rows and " + cm.nCols + " columns.";
+ return false;
+ }
+ final double minCost = Util.min( cm.cc );
+ if ( minCost < 0 )
+ {
+ errorMessage = BASE_ERROR_MESSAGE + "This solver only accept positive costs. Found " + minCost + ".";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ @Override
+ public long getProcessingTime()
+ {
+ return processingTime;
+ }
+
+ /**
+ * Returns JVS results as row assignments. The row i
is
+ * associated to the column x[i]
in the cost matrix.
+ *
+ * @return the row assignments as an int[]
array. This array is
+ * re-instantiated upon calling {@link #process()}.
+ */
+ @Override
+ public int[] getResult()
+ {
+ return output;
+ }
+
+ public String resultToString()
+ {
+ return resultToString( Collections.emptyList(), Collections.emptyList() );
+ }
+
+ public String resultToString( final List< ? > rows, final List< ? > cols )
+ {
+ if ( null == output ) { return "Not solved yet. Process the algorithm prior to calling this method."; }
+
+ final String[] colNames = new String[ cm.nCols ];
+ // default names
+ for ( int j = 0; j < colNames.length; j++ )
+ {
+ colNames[ j ] = "" + j;
+ }
+ final String[] rowNames = new String[ cm.nRows ];
+ for ( int i = 0; i < rowNames.length; i++ )
+ {
+ rowNames[ i ] = "" + i;
+ }
+
+ for ( int j = 0; j < cols.size(); j++ )
+ {
+ final Object col = cols.get( j );
+ if ( null != col )
+ {
+ final String str = col.toString();
+ colNames[ j ] = str;
+ }
+ }
+
+ int colWidth = -1;
+ for ( final String str : colNames )
+ {
+ if ( str.length() > colWidth )
+ {
+ colWidth = str.length();
+ }
+
+ }
+ colWidth = colWidth + 1;
+ colWidth = Math.max( colWidth, 7 );
+
+ final Set< String > unassignedColNames = new HashSet< String >( Arrays.asList( colNames ) );
+
+ for ( int i = 0; i < rows.size(); i++ )
+ {
+ final Object row = rows.get( i );
+ if ( null != row )
+ {
+ final String str = row.toString();
+ rowNames[ i ] = str;
+ }
+ }
+
+ int rowWidth = -1;
+ for ( final String str : rowNames )
+ {
+ if ( str.length() > rowWidth )
+ {
+ rowWidth = str.length();
+ }
+
+ }
+ rowWidth = rowWidth + 1;
+ rowWidth = Math.max( 7, rowWidth );
+
+ final StringBuilder str = new StringBuilder();
+ final double totalCost = cm.totalAssignmentCost( output );
+ final int digits = ( int ) ( Math.log10( totalCost ) + 2 );
+
+ str.append( String.format( "Optimal assignment with total cost = %" + digits + ".1f:\n", totalCost ) );
+ for ( int i = 0; i < output.length; i++ )
+ {
+ final int j = output[ i ];
+ final double cost = cm.get( i, j, Double.POSITIVE_INFINITY );
+
+ {
+ for ( int k = 0; k < ( rowWidth - rowNames[ i ].length() ); k++ )
+ {
+ str.append( ' ' );
+ }
+ str.append( rowNames[ i ] );
+ }
+ str.append( " → " );
+ {
+ str.append( colNames[ j ] );
+ unassignedColNames.remove( colNames[ j ] );
+ for ( int k = 0; k < ( colWidth - colNames[ j ].length() ); k++ )
+ {
+ str.append( ' ' );
+ }
+ }
+ str.append( String.format( " cost = %" + digits + ".1f\n", cost ) );
+ }
+ if ( cm.nCols > cm.nRows )
+ {
+ str.append( "Unassigned columns:\n" );
+ for ( final String ucn : unassignedColNames )
+ {
+ {
+ for ( int k = 0; k < rowWidth / 2; k++ )
+ {
+ str.append( ' ' );
+ }
+ str.append( 'ø' );
+ for ( int k = 0; k < rowWidth - rowWidth / 2 - 1; k++ )
+ {
+ str.append( ' ' );
+ }
+ }
+ str.append( " → " );
+ {
+ str.append( ucn );
+ for ( int k = 0; k < ( colWidth - ucn.length() ); k++ )
+ {
+ str.append( ' ' );
+ }
+ }
+ str.append( '\n' );
+ }
+ }
+
+ return str.toString();
+ }
+}
diff --git a/src/main/java/costMatrix/PixelRatioCostFunction.java b/src/main/java/costMatrix/PixelRatioCostFunction.java
new file mode 100644
index 0000000..59923e4
--- /dev/null
+++ b/src/main/java/costMatrix/PixelRatioCostFunction.java
@@ -0,0 +1,28 @@
+package costMatrix;
+
+import utility.PreRoiobject;
+
+public class PixelRatioCostFunction implements CostFunction< PreRoiobject, PreRoiobject > {
+
+
+
+ /**
+ * Implementation of various cost functions
+ *
+ *
+ */
+
+
+ @Override
+ public double linkingCost( final PreRoiobject source, final PreRoiobject target )
+ {
+ return source.numberofPixelsRatioTo(target);
+ }
+
+
+
+
+
+
+
+}
diff --git a/src/main/java/costMatrix/PixelratiowDistCostFunction.java b/src/main/java/costMatrix/PixelratiowDistCostFunction.java
new file mode 100644
index 0000000..8f7ba8f
--- /dev/null
+++ b/src/main/java/costMatrix/PixelratiowDistCostFunction.java
@@ -0,0 +1,47 @@
+package costMatrix;
+
+import utility.PreRoiobject;
+import utility.ThreeDRoiobject;
+
+public class PixelratiowDistCostFunction implements CostFunction< ThreeDRoiobject, ThreeDRoiobject >
+ {
+
+
+ // Alpha is the weightage given to distance and Beta is the weightage given to the ratio of pixels
+ public final double beta;
+ public final double alpha;
+
+
+
+
+ public double getAlpha(){
+
+ return alpha;
+ }
+
+
+ public double getBeta(){
+
+ return beta;
+ }
+
+ public PixelratiowDistCostFunction (double alpha, double beta){
+
+ this.alpha = alpha;
+ this.beta = beta;
+
+ }
+
+
+ @Override
+ public double linkingCost( final ThreeDRoiobject source, final ThreeDRoiobject target )
+ {
+ return source.NormalizedPixelratioandDistanceTo(target, alpha, beta);
+ }
+
+
+
+
+
+
+}
diff --git a/src/main/java/costMatrix/ResizableDoubleArray.java b/src/main/java/costMatrix/ResizableDoubleArray.java
new file mode 100644
index 0000000..4cfc72f
--- /dev/null
+++ b/src/main/java/costMatrix/ResizableDoubleArray.java
@@ -0,0 +1,101 @@
+package costMatrix;
+
+
+import java.util.Arrays;
+
+public class ResizableDoubleArray
+{
+
+ /*
+ * PUBLIC FIELDS
+ */
+
+ public double[] data;
+
+
+ public int size;
+
+ /*
+ * CONSTRUCTORS
+ */
+
+ public ResizableDoubleArray( final double[] data )
+ {
+ this.data = data;
+ this.size = data.length;
+ }
+
+
+ public ResizableDoubleArray( final int initialCapacity )
+ {
+ this.data = new double[ initialCapacity ];
+ this.size = 0;
+ }
+
+ /**
+ * Creates an empty ResizableIntArray with the a initial capacity of 10.
+ */
+ public ResizableDoubleArray()
+ {
+ this( 10 );
+ }
+
+ /*
+ * METHODS
+ */
+
+ public void trimToSize()
+ {
+ final int oldCapacity = data.length;
+ if ( size < oldCapacity )
+ {
+ data = Arrays.copyOf( data, size );
+ }
+ }
+
+ public void ensureCapacity( final int minCapacity )
+ {
+ final int oldCapacity = data.length;
+ if ( minCapacity > oldCapacity )
+ {
+ // The heuristics of ArrayList
+ int newCapacity = ( oldCapacity * 3 ) / 2 + 1;
+ if ( newCapacity < minCapacity )
+ {
+ newCapacity = minCapacity;
+ }
+ data = Arrays.copyOf( data, newCapacity );
+ }
+ }
+
+ /**
+ * Returns true if this list contains no elements.
+ *
+ * @return true if this list contains no elements
+ */
+ public boolean isEmpty()
+ {
+ return size == 0;
+ }
+
+ public void add( final double val )
+ {
+ ensureCapacity( size + 1 );
+ data[ size ] = val;
+ size++;
+ }
+
+ @Override
+ public String toString()
+ {
+ if ( isEmpty() ) { return "()"; }
+ final StringBuilder str = new StringBuilder();
+ str.append( '(' );
+ for ( int i = 0; i < size - 1; i++ )
+ {
+ str.append( data[ i ] + ", " );
+ }
+ str.append( data[ size - 1 ] + "), size = " + size );
+ return str.toString();
+ }
+}
diff --git a/src/main/java/costMatrix/SparseCostMatrix.java b/src/main/java/costMatrix/SparseCostMatrix.java
new file mode 100644
index 0000000..5ac0781
--- /dev/null
+++ b/src/main/java/costMatrix/SparseCostMatrix.java
@@ -0,0 +1,530 @@
+package costMatrix;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class to represent a sparse cost matrix.
+ *
+ * This class aims at representing in a memory-efficient way a possibly
+ * rectangular double matrix used in linear assignment problems (LAP). It is
+ * useful when the number of sources (rows of the matrix) and the number of
+ * targets (columns of the matrix) are large, but only a fraction of the costs
+ * are not infinite (assignment forbidden). In this case, infinite cost can be
+ * omitted and it is implicitly understood that missing values of the matrix
+ * represent infinite costs. This situation is very common in single-particle
+ * tracking for Life-Sciences, and this class is especially designed for these
+ * problems.
+ *
+ * This class does not do much. It just stores the arrays (described below) that
+ * represent the matrix. It is the caller responsibility to ensure they are
+ * properly arranged and that they represent the desired cost matrix. The arrays
+ * are accessible via default
visibility.
+ *
+ * This matrix follow the row compressed storage convention, taken from the
+ * Volgenant paper: Volgenant. Linear and semi-assignment problems: A core
+ * oriented approach. Computers & Operations Research (1996) vol. 23 (10) pp.
+ * 917-932
+ *
+ * @author Jean-Yves Tinevez - 2014
+ */
+public class SparseCostMatrix
+{
+
+ /**
+ * The linear array of non-infinite costs.
+ */
+ final double[] cc;
+
+ /**
+ * The linear array storing the column index of each non infinite-cost.
+ * Column indices are stored adjacently row by row, and for each row, are
+ * stored in ascending order.
+ */
+ final int[] kk;
+
+ /**
+ * The array of the number of non-infinite costs for each row.
+ */
+ final int[] number;
+
+ /**
+ * The number of rows in the cost matrix.
+ */
+ final int nRows;
+
+ /**
+ * The number of columns in the cost matrix.
+ */
+ final int nCols;
+
+ /**
+ * The number of non-infinite costs in the matrix.
+ */
+ final int cardinality;
+
+ /**
+ * The array of indices in {@link #kk} the column index where a row stats.
+ */
+ final int[] start;
+
+ /**
+ * Instantiate a new sparse cost matrix. The caller must provide 3 arrays:
+ *
+ * -
cc
, the double[]
array containing all the
+ * non-infinite costs.
+ * -
kk
, an int[]
array of the same length that
+ * cc
, and that contains the columns of the cost.
+ *
+ * These two arrays must be arranged row by row, starting with the first
+ * one. And in each row, the columns must be sorted in increasing order (to
+ * facilitate index search). Also, each row must have at least one
+ * non-infinte cost. If not, an {@link IllegalArgumentException} is thrown.
+ *
+ * -
number
an int[]
array, with one element per
+ * row, that contains the number of non infinite cost for a row.
+ *
+ *
+ * @param cc
+ * the cost array.
+ * @param kk
+ * the column index of each cost.
+ * @param number
+ * the number of element for each row.
+ * @throws IllegalArgumentException
+ * if the cost and column arrays are not of the same size, if
+ * the column array is not sorted row by row, of if one row has
+ * 0 non-infinite costs.
+ */
+ public SparseCostMatrix( final double[] cc, final int[] kk, final int[] number, final int nCols )
+ {
+ this.cc = cc;
+ this.kk = kk;
+ this.number = number;
+ this.nCols = nCols;
+
+ // Check sizes
+ if (cc.length != kk.length) {
+ throw new IllegalArgumentException( "Cost and column indices arrays must have the same length. Found " + cc.length + " and " + kk.length + "." );
+ }
+
+ this.cardinality = cc.length;
+ this.nRows = number.length;
+ // loop on each row
+ this.start = new int[ nRows ];
+ if ( nRows > 0 )
+ {
+ start[ 0 ] = 0;
+ }
+ for ( int i = 1; i < nRows; i++ )
+ {
+ if ( number[ i ] == 0 ) { throw new IllegalArgumentException( "All the rows must have at least one cost. Row " + i + " have none." ); }
+ start[ i ] = start[ i - 1 ] + number[ i - 1 ];
+ }
+
+ final int[] colHistogram = new int[ nCols ];
+ for ( int i = 0; i < nRows; i++ )
+ {
+ // Iterate through each column
+ int previousK = -1;
+ for ( int j = start[ i ]; j < start[ i ] + number[ i ]; j++ )
+ {
+ final int k = kk[ j ];
+ if ( k >= nCols ) { throw new IllegalArgumentException( "At line " + i + ", the column indices array contains a column index (" + k + ") that is larger than or equal to the declared number of column (" + nCols + ")." ); }
+ colHistogram[ k ]++;
+ if ( k <= previousK ) { throw new IllegalArgumentException( "The column indices array must be sorted within each row. The column elements at line " + i + " are not properly sorted." ); }
+ previousK = k;
+ }
+ }
+
+ // Check that each column have at least one assignment
+ for ( int j = 0; j < colHistogram.length; j++ )
+ {
+ if ( colHistogram[ j ] == 0 ) { throw new IllegalArgumentException( "All the columns must have at least one cost. The column " + j + " has none." ); }
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return toString( Collections.EMPTY_LIST, Collections.EMPTY_LIST );
+ }
+
+ public String toString( final List< ? > rows, final List< ? > columns )
+ {
+ final String[] colNames = new String[ nCols ];
+ // default names
+ for ( int j = 0; j < colNames.length; j++ )
+ {
+ colNames[ j ] = "" + j;
+ }
+ final String[] rowNames = new String[ nRows ];
+ for ( int i = 0; i < rowNames.length; i++ )
+ {
+ rowNames[ i ] = "" + i;
+ }
+
+ for ( int j = 0; j < columns.size(); j++ )
+ {
+ final Object col = columns.get( j );
+ if ( null != col )
+ {
+ final String str = col.toString();
+ colNames[ j ] = str;
+ }
+ }
+
+ int colWidth = -1;
+ for ( final String str : colNames )
+ {
+ if ( str.length() > colWidth )
+ {
+ colWidth = str.length();
+ }
+
+ }
+ colWidth = colWidth + 1;
+ colWidth = Math.max( colWidth, 7 );
+
+ for ( int i = 0; i < rows.size(); i++ )
+ {
+ final Object row = rows.get( i );
+ if ( null != row )
+ {
+ final String str = row.toString();
+ rowNames[ i ] = str;
+ }
+ }
+
+ int rowWidth = -1;
+ for ( final String str : rowNames )
+ {
+ if ( str.length() > rowWidth )
+ {
+ rowWidth = str.length();
+ }
+
+ }
+ rowWidth = rowWidth + 1;
+ rowWidth = Math.max( 7, rowWidth );
+
+ final StringBuilder str = new StringBuilder();
+ str.append( super.toString() + '\n' );
+ str.append( " " + nRows + " × " + nCols + " matrix with " + cardinality + " non-null elements. " );
+ str.append( String.format( "Density = %.2f%%.\n", ( double ) cardinality / ( nRows * nCols ) * 100d ) );
+
+ for ( int i = 0; i < rowWidth; i++ )
+ {
+ str.append( ' ' );
+ }
+ str.append( '|' );
+ for ( int c = 0; c < nCols; c++ )
+ {
+ for ( int i = 0; i < ( colWidth - colNames[ c ].length() ); i++ )
+ {
+ str.append( ' ' );
+ }
+ str.append( colNames[ c ] );
+ }
+ str.append( '\n' );
+
+ for ( int i = 0; i < rowWidth; i++ )
+ {
+ str.append( '_' );
+ }
+ str.append( '|' );
+ final char[] line = new char[ colWidth * nCols ];
+ Arrays.fill( line, '_' );
+ str.append( line );
+ str.append( '\n' );
+
+ for ( int r = 0; r < nRows; r++ )
+ {
+ str.append( rowNames[ r ] );
+ for ( int i = 0; i < rowWidth - rowNames[ r ].length(); i++ )
+ {
+ str.append( ' ' );
+ }
+ str.append( '|' );
+
+ final StringBuilder rowStr = new StringBuilder();
+ final char[] spaces = new char[ colWidth * nCols ];
+ Arrays.fill( spaces, ' ' );
+ rowStr.append( spaces );
+
+ for ( int k = start[ r ]; k < start[ r ] + number[ r ]; k++ )
+ {
+ final int col = kk[ k ];
+ final double cost = cc[ k ];
+ rowStr.replace( col * colWidth, ( col + 1 ) * colWidth, String.format( "% " + colWidth + ".1f", cost ) );
+ }
+ rowStr.append( '\n' );
+ str.append( rowStr.toString() );
+ }
+
+ return str.toString();
+ }
+
+ /**
+ * Computes the total cost for an assignment specified by row. It is
+ * supposed that row i
is assigned to column
+ * rowAssignment[i]
.
+ *
+ * @param rowAssignment
+ * the assignment, specified by row.
+ * @return the total cost for this assignment.
+ */
+ public double totalAssignmentCost( final int[] rowAssignment )
+ {
+ double sum = 0;
+ for ( int i = 0; i < rowAssignment.length; i++ )
+ {
+ final int j = rowAssignment[ i ];
+ final int kj = Arrays.binarySearch( kk, start[ i ], start[ i ] + number[ i ], j );
+ sum += cc[ kj ];
+ }
+ return sum;
+ }
+
+ /**
+ * Creates and returns a new double[][]
matrix representing a
+ * non-sparse version of this cost matrix. Missing costs are replace by
+ * {@link Double#MAX_VALUE}.
+ *
+ * @return a new double[][]
+ */
+ public double[][] toFullMatrix()
+ {
+ final double[][] cm = new double[ nRows ][ nCols ];
+ for ( final double[] ds : cm )
+ {
+ Arrays.fill( ds, Double.MAX_VALUE );
+ }
+
+ for ( int r = 0; r < nRows; r++ )
+ {
+ for ( int k = start[ r ]; k < start[ r ] + number[ r ]; k++ )
+ {
+ final int c = kk[ k ];
+ final double cost = cc[ k ];
+ cm[ r ][ c ] = cost;
+ }
+ }
+
+ return cm;
+ }
+
+ /**
+ * Returns the value stored by this matrix at the specified row and column.
+ * If a value is not present in the sparse matrix, the specified missing
+ * value is returned.
+ *
+ * @param i
+ * the row.
+ * @param j
+ * the column.
+ * @param missingValue
+ * what to return if the sparse matrix does not store a value at
+ * the specified row and column.
+ * @return the value.
+ */
+ public final double get( final int i, final int j, final double missingValue )
+ {
+ final int k = Arrays.binarySearch( kk, start[ i ], start[ i ] + number[ i ], j );
+ if ( k < 0 )
+ {
+ return missingValue;
+ }
+ else
+ {
+ return cc[ k ];
+ }
+ }
+
+ /**
+ * Exposes the array of all the non-infinite costs.
+ *
+ * @return the costs.
+ */
+ public double[] getCosts()
+ {
+ return cc;
+ }
+
+ public int getNCols()
+ {
+ return nCols;
+ }
+
+ public int getNRows()
+ {
+ return nRows;
+ }
+
+ /**
+ * Returns the vertical concatenation of this matrix with the specified one.
+ * So that if this matrix is A and the specified matrix is B, you get
+ *
+ *
+ * -----
+ * | A |
+ * | B |
+ * -----
+ *
+ *
+ * @param B
+ * the matrix to concatenate this matrix with
+ * @return a new sparse matrix.
+ * @throws IllegalArgumentException
+ * if B does not have the same number of columns as this matrix.
+ */
+ public final SparseCostMatrix vcat( final SparseCostMatrix B )
+ {
+ if ( nCols != B.nCols ) { throw new IllegalArgumentException( "Matrices A & B do not have the same number of columns. Found " + nCols + " and " + B.nCols + " respectively." ); }
+
+ final double[] cc2 = new double[ cardinality + B.cardinality ];
+ final int[] kk2 = new int[ cardinality + B.cardinality ];
+ final int[] number2 = new int[ nRows + B.nRows ];
+
+ // Append A
+ System.arraycopy( kk, 0, kk2, 0, cardinality );
+ System.arraycopy( cc, 0, cc2, 0, cardinality );
+ System.arraycopy( number, 0, number2, 0, nRows );
+
+ // Append B
+ System.arraycopy( B.kk, 0, kk2, cardinality, B.cardinality );
+ System.arraycopy( B.cc, 0, cc2, cardinality, B.cardinality );
+ System.arraycopy( B.number, 0, number2, nRows, B.nRows );
+
+ return new SparseCostMatrix( cc2, kk2, number2, nCols );
+ }
+
+ /**
+ * Returns the horizontal concatenation of this matrix with the specified
+ * one. So that if this matrix is A and the specified matrix is B, you get
+ *
+ *
+ * -------
+ * | A B |
+ * -------
+ *
+ *
+ * @param B
+ * the matrix to concatenate this matrix with
+ * @return a new sparse matrix.
+ * @throws IllegalArgumentException
+ * if B does not have the same number of rows as this matrix.
+ */
+ public final SparseCostMatrix hcat( final SparseCostMatrix B )
+ {
+ if ( nRows != B.nRows ) { throw new IllegalArgumentException( "Matrices A & B do not have the same number of rows. Found " + nRows + " and " + B.nRows + " respectively." ); }
+
+ final double[] cc2 = new double[ cardinality + B.cardinality ];
+ final int[] kk2 = new int[ cardinality + B.cardinality ];
+ final int[] number2 = new int[ nRows ];
+
+ // Append line by line
+ int Aindex = 0;
+ int Bindex = 0;
+ int Cindex = 0;
+ for ( int i = 0; i < nRows; i++ )
+ {
+ // A
+ System.arraycopy( cc, Aindex, cc2, Cindex, number[ i ] );
+ System.arraycopy( kk, Aindex, kk2, Cindex, number[ i ] );
+ Aindex += number[ i ];
+ Cindex += number[ i ];
+
+ // B
+ System.arraycopy( B.cc, Bindex, cc2, Cindex, B.number[ i ] );
+ // For the columns, we need to increment them by A.nCols
+ for ( int j = 0; j < B.number[ i ]; j++ )
+ {
+ kk2[ Cindex + j ] = B.kk[ Bindex + j ] + nCols;
+ }
+ Bindex += B.number[ i ];
+ Cindex += B.number[ i ];
+
+ // number
+ number2[ i ] = number[ i ] + B.number[ i ];
+ }
+
+ return new SparseCostMatrix( cc2, kk2, number2, nCols + B.nCols );
+ }
+
+ /**
+ * Returns the transpose of this matrix.
+ *
+ * @return a new sparse matrix.
+ */
+ public final SparseCostMatrix transpose()
+ {
+ // Build column histogram, which will give the transposed number
+ final int[] number2 = new int[ nCols ];
+ for ( final int j : kk )
+ {
+ number2[ j ]++;
+ }
+
+ // Prepare column & cost storage.
+ final int[][] cols = new int[ nCols ][];
+ final double[][] costs = new double[ nCols ][];
+ for ( int j = 0; j < cols.length; j++ )
+ {
+ cols[ j ] = new int[ number2[ j ] ];
+ costs[ j ] = new double[ number2[ j ] ];
+ }
+
+ // Parse source column array and store at what line they happen. Add to
+ // cost arrays.
+ int currentLine = 0;
+ int previousJ = -1;
+ int walked = 0;
+ final int[] colIndex = new int[ nCols ];
+ for ( int k = 0; k < cardinality; k++ )
+ {
+ final int j = kk[ k ];
+ final double c = cc[ k ];
+
+ // Determine whether we changed line.
+ if ( j <= previousJ || walked >= number[ currentLine ] )
+ {
+ currentLine++;
+ walked = 0;
+ }
+ walked++;
+ previousJ = j;
+
+ cols[ j ][ colIndex[ j ] ] = currentLine;
+ costs[ j ][ colIndex[ j ] ] = c;
+
+ colIndex[ j ]++;
+ }
+
+ // Concatenate
+ final double[] cc2 = new double[ cardinality ];
+ final int[] kk2 = new int[ cardinality ];
+ int index = 0;
+ for ( int i = 0; i < cols.length; i++ )
+ {
+ System.arraycopy( cols[ i ], 0, kk2, index, number2[ i ] );
+ System.arraycopy( costs[ i ], 0, cc2, index, number2[ i ] );
+ index += number2[ i ];
+ }
+ return new SparseCostMatrix( cc2, kk2, number2, nRows );
+ }
+
+ /**
+ * Replace all the non-infinite values of this matrix by the specified
+ * value.
+ *
+ * @param value
+ * the value to write in this matrix.
+ */
+ public void fillWith( final double value )
+ {
+ Arrays.fill( cc, value );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/costMatrix/SquareDistCostFunction.java b/src/main/java/costMatrix/SquareDistCostFunction.java
new file mode 100644
index 0000000..fc347cf
--- /dev/null
+++ b/src/main/java/costMatrix/SquareDistCostFunction.java
@@ -0,0 +1,26 @@
+package costMatrix;
+
+import utility.PreRoiobject;
+
+/**
+ * Implementation of various cost functions
+ *
+ *
+ */
+
+// Cost function base don minimizing the squared distances
+
+public class SquareDistCostFunction implements CostFunction< PreRoiobject, PreRoiobject >
+{
+
+ @Override
+ public double linkingCost( final PreRoiobject source, final PreRoiobject target )
+ {
+ return source.squareDistanceTo(target );
+ }
+
+
+
+
+
+}
diff --git a/src/main/java/curvatureUtils/PointExtractor.java b/src/main/java/curvatureUtils/PointExtractor.java
index 1afeb98..855f65b 100644
--- a/src/main/java/curvatureUtils/PointExtractor.java
+++ b/src/main/java/curvatureUtils/PointExtractor.java
@@ -6,7 +6,6 @@
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.poi.poifs.property.Parent;
import curvatureFinder.LineProfileCircle;
import ellipsoidDetector.Distance;
diff --git a/src/main/java/distanceTransform/CreateBinary.java b/src/main/java/distanceTransform/CreateBinary.java
new file mode 100644
index 0000000..4fffc93
--- /dev/null
+++ b/src/main/java/distanceTransform/CreateBinary.java
@@ -0,0 +1,65 @@
+package distanceTransform;
+
+import net.imglib2.Cursor;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.ImgFactory;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.numeric.real.FloatType;
+import net.imglib2.view.Views;
+import preProcessing.GlobalThresholding;
+
+public class CreateBinary {
+
+ public static RandomAccessibleInterval CreateBinaryImage(RandomAccessibleInterval inputimage,
+ final FloatType threshold) {
+
+ final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputimage, new BitType());
+ RandomAccessibleInterval binaryimage = factory.create(inputimage, new BitType());
+
+ Cursor cursor = Views.iterable(inputimage).localizingCursor();
+ RandomAccess ranac = binaryimage.randomAccess();
+
+ while (cursor.hasNext()) {
+
+ cursor.fwd();
+
+ ranac.setPosition(cursor);
+
+ if (cursor.get().compareTo(threshold) >= 1)
+ ranac.get().setOne();
+ else
+ ranac.get().setZero();
+
+ }
+
+ return binaryimage;
+ }
+
+ public static RandomAccessibleInterval CreateAutoBinaryImage(RandomAccessibleInterval inputimage) {
+
+ final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputimage, new BitType());
+ RandomAccessibleInterval binaryimage = factory.create(inputimage, new BitType());
+
+ Cursor cursor = Views.iterable(inputimage).localizingCursor();
+ RandomAccess ranac = binaryimage.randomAccess();
+
+ Float threshold = GlobalThresholding.AutomaticThresholding(inputimage);
+
+ while (cursor.hasNext()) {
+
+ cursor.fwd();
+
+ ranac.setPosition(cursor);
+
+ if (cursor.get().get() >= (threshold))
+ ranac.get().setOne();
+ else
+ ranac.get().setZero();
+
+ }
+
+ return binaryimage;
+ }
+
+}
diff --git a/src/main/java/distanceTransform/CreateDistanceTransform.java b/src/main/java/distanceTransform/CreateDistanceTransform.java
new file mode 100644
index 0000000..1f8216c
--- /dev/null
+++ b/src/main/java/distanceTransform/CreateDistanceTransform.java
@@ -0,0 +1,164 @@
+package distanceTransform;
+
+import java.util.Iterator;
+
+import net.imglib2.Cursor;
+import net.imglib2.KDTree;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.RealPoint;
+import net.imglib2.RealPointSampleList;
+import net.imglib2.algorithm.BenchmarkAlgorithm;
+import net.imglib2.algorithm.OutputAlgorithm;
+import net.imglib2.algorithm.labeling.AllConnectedComponents;
+import net.imglib2.algorithm.labeling.Watershed;
+import net.imglib2.img.ImgFactory;
+import net.imglib2.img.array.ArrayImgFactory;
+import net.imglib2.labeling.DefaultROIStrategyFactory;
+import net.imglib2.labeling.Labeling;
+import net.imglib2.labeling.LabelingROIStrategy;
+import net.imglib2.labeling.NativeImgLabeling;
+import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree;
+import net.imglib2.roi.labeling.ImgLabeling;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.numeric.integer.IntType;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import net.imglib2.type.numeric.real.FloatType;
+import net.imglib2.util.Util;
+import net.imglib2.view.Views;
+
+
+
+
+ public class CreateDistanceTransform > extends BenchmarkAlgorithm
+ implements OutputAlgorithm > {
+
+ private static final String BASE_ERROR_MSG = "[WatershedDistimg] ";
+ private final RandomAccessibleInterval source;
+
+ private final RandomAccessibleInterval bitimg;
+ RandomAccessibleInterval distimg;
+ /**
+ * Do watershedding after doing distance transformation on the biimg
+ * provided by the user using a user set threshold value.
+ *
+ * @param source
+ * The image to be watershedded.
+ * @param bitimg
+ * The image used to compute distance transform and seeds for watershedding.
+ */
+ public CreateDistanceTransform(final RandomAccessibleInterval source, final RandomAccessibleInterval bitimg){
+
+ this.source = source;
+ this.bitimg = bitimg;
+ }
+
+
+
+ @Override
+ public boolean checkInput() {
+ if (source.numDimensions() > 2) {
+ errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D, make slices of your stack . Got "
+ + source.numDimensions() + "D.";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean process() {
+
+ // Perform the distance transform
+ final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new FloatType());
+ distimg = factory.create(source, new FloatType());
+
+ DistanceTransformImage(source, distimg);
+
+
+ return true;
+ }
+
+ @Override
+ public RandomAccessibleInterval getResult() {
+
+ return distimg;
+ }
+
+
+
+ /***
+ *
+ * Do the distance transform of the input image using the bit image
+ * provided.
+ *
+ * @param inputimg
+ * The pre-processed input image as RandomAccessibleInterval
+ * @param outimg
+ * The distance transormed image having the same dimensions as
+ * the input image.
+ * @param invtype
+ * Straight: The intensity value is set to the distance, gives
+ * white on black background. Inverse: The intensity is set to
+ * the negative of the distance, gives black on white background.
+ */
+
+ private void DistanceTransformImage(RandomAccessibleInterval inputimg,
+ RandomAccessibleInterval outimg) {
+ int n = inputimg.numDimensions();
+
+ // make an empty list
+ final RealPointSampleList list = new RealPointSampleList(n);
+
+ // cursor on the binary image
+ final Cursor cursor = Views.iterable(bitimg).localizingCursor();
+
+ // for every pixel that is 1, make a new RealPoint at that location
+ while (cursor.hasNext())
+ if (cursor.next().getInteger() == 1)
+ list.add(new RealPoint(cursor), cursor.get());
+
+ // build the KD-Tree from the list of points that == 1
+ final KDTree tree = new KDTree(list);
+
+ // Instantiate a nearest neighbor search on the tree (does not modifiy
+ // the tree, just uses it)
+ final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree);
+
+ // randomaccess on the output
+ final RandomAccess ranac = outimg.randomAccess();
+
+ // reset cursor for the input (or make a new one)
+ cursor.reset();
+
+ // for every pixel of the binary image
+ while (cursor.hasNext()) {
+ cursor.fwd();
+
+ // set the randomaccess to the same location
+ ranac.setPosition(cursor);
+
+ // if value == 0, look for the nearest 1-valued pixel
+ if (cursor.get().getInteger() == 0) {
+ // search the nearest 1 to the location of the cursor (the
+ // current 0)
+ search.search(cursor);
+
+ // get the distance (the previous call could return that, this
+ // for generality that it is two calls)
+
+
+ ranac.get().setReal(search.getDistance());
+
+ } else {
+ // if value == 1, no need to search
+ ranac.get().setZero();
+ }
+ }
+
+ }
+
+
+
+
+}
diff --git a/src/main/java/distanceTransform/CreateWatershed.java b/src/main/java/distanceTransform/CreateWatershed.java
new file mode 100644
index 0000000..a50bef7
--- /dev/null
+++ b/src/main/java/distanceTransform/CreateWatershed.java
@@ -0,0 +1,263 @@
+package distanceTransform;
+
+import java.util.Iterator;
+
+import javax.swing.JProgressBar;
+
+import interactivePreprocessing.InteractiveMethods;
+import net.imglib2.Cursor;
+import net.imglib2.KDTree;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.RealPoint;
+import net.imglib2.RealPointSampleList;
+import net.imglib2.algorithm.BenchmarkAlgorithm;
+import net.imglib2.algorithm.OutputAlgorithm;
+import net.imglib2.algorithm.labeling.AllConnectedComponents;
+import net.imglib2.algorithm.labeling.Watershed;
+import net.imglib2.img.ImgFactory;
+import net.imglib2.img.array.ArrayImgFactory;
+import net.imglib2.labeling.DefaultROIStrategyFactory;
+import net.imglib2.labeling.Labeling;
+import net.imglib2.labeling.LabelingROIStrategy;
+import net.imglib2.labeling.NativeImgLabeling;
+import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree;
+import net.imglib2.roi.labeling.ImgLabeling;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.numeric.integer.IntType;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import net.imglib2.util.Util;
+import net.imglib2.view.Views;
+
+
+
+@SuppressWarnings("deprecation")
+ public class CreateWatershed > extends BenchmarkAlgorithm
+ implements OutputAlgorithm > {
+
+ private static final String BASE_ERROR_MSG = "[WatershedDistimg] ";
+ private final RandomAccessibleInterval source;
+
+ private final RandomAccessibleInterval bitimg;
+ public final JProgressBar jpb;
+ public final InteractiveMethods parent;
+ private RandomAccessibleInterval watershedimage;
+ RandomAccessibleInterval distimg;
+ /**
+ * Do watershedding after doing distance transformation on the biimg
+ * provided by the user using a user set threshold value.
+ *
+ * @param source
+ * The image to be watershedded.
+ * @param bitimg
+ * The image used to compute distance transform and seeds for watershedding.
+ */
+ public CreateWatershed(final InteractiveMethods parent, final RandomAccessibleInterval source, final RandomAccessibleInterval bitimg, final JProgressBar jpb){
+
+ this.parent = parent;
+ this.source = source;
+ this.bitimg = bitimg;
+ this.jpb = jpb;
+
+ }
+
+
+
+ @Override
+ public boolean checkInput() {
+ if (source.numDimensions() > 2) {
+ errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D, make slices of your stack . Got "
+ + source.numDimensions() + "D.";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean process() {
+
+ // Perform the distance transform
+ final T type = source.randomAccess().get().createVariable();
+ final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new UnsignedByteType());
+ distimg = factory.create(source, new UnsignedByteType());
+ utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, "Doing Distance Transformed Watershedding, Please Wait...");
+ parent.panelFirst.validate();
+ parent.panelFirst.repaint();
+ DistanceTransformImage(source, distimg);
+
+ // Prepare seed image for watershedding
+ NativeImgLabeling oldseedLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(source, new IntType()));
+ oldseedLabeling = PrepareSeedImage(source);
+ // Do watershedding on the distance transformed image
+
+ NativeImgLabeling outputLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(source, new IntType()));
+
+ outputLabeling = GetlabeledImage(distimg, oldseedLabeling);
+
+ watershedimage = outputLabeling.getStorageImg();
+
+
+ return true;
+ }
+
+ @Override
+ public RandomAccessibleInterval getResult() {
+
+ return watershedimage;
+ }
+ public RandomAccessibleInterval getDistanceTransformedimg() {
+
+ return distimg;
+ }
+
+
+ /***
+ *
+ * Do the distance transform of the input image using the bit image
+ * provided.
+ *
+ * @param inputimg
+ * The pre-processed input image as RandomAccessibleInterval
+ * @param outimg
+ * The distance transormed image having the same dimensions as
+ * the input image.
+ * @param invtype
+ * Straight: The intensity value is set to the distance, gives
+ * white on black background. Inverse: The intensity is set to
+ * the negative of the distance, gives black on white background.
+ */
+
+ private void DistanceTransformImage(RandomAccessibleInterval inputimg,
+ RandomAccessibleInterval outimg) {
+ int n = inputimg.numDimensions();
+
+ // make an empty list
+ final RealPointSampleList list = new RealPointSampleList(n);
+
+ // cursor on the binary image
+ final Cursor cursor = Views.iterable(bitimg).localizingCursor();
+
+ // for every pixel that is 1, make a new RealPoint at that location
+ while (cursor.hasNext())
+ if (cursor.next().getInteger() == 1)
+ list.add(new RealPoint(cursor), cursor.get());
+
+ // build the KD-Tree from the list of points that == 1
+ final KDTree tree = new KDTree(list);
+
+ // Instantiate a nearest neighbor search on the tree (does not modifiy
+ // the tree, just uses it)
+ final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree);
+
+ // randomaccess on the output
+ final RandomAccess ranac = outimg.randomAccess();
+
+ // reset cursor for the input (or make a new one)
+ cursor.reset();
+
+ // for every pixel of the binary image
+ while (cursor.hasNext()) {
+ cursor.fwd();
+
+ // set the randomaccess to the same location
+ ranac.setPosition(cursor);
+
+ // if value == 0, look for the nearest 1-valued pixel
+ if (cursor.get().getInteger() == 0) {
+ // search the nearest 1 to the location of the cursor (the
+ // current 0)
+ search.search(cursor);
+
+ // get the distance (the previous call could return that, this
+ // for generality that it is two calls)
+
+
+ ranac.get().setReal(search.getDistance());
+
+ } else {
+ // if value == 1, no need to search
+ ranac.get().setZero();
+ }
+ }
+
+ }
+
+ private NativeImgLabeling PrepareSeedImage(RandomAccessibleInterval inputimg) {
+
+ // New Labeling type
+ final ImgLabeling seedLabeling = new ImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ // Old Labeling type
+ final NativeImgLabeling oldseedLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ // The label generator for both new and old type
+ final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0);
+
+
+
+ // Getting unique labelled image (old version)
+ AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, bitimg, labelGenerator,
+ AllConnectedComponents.getStructuringElement(inputimg.numDimensions()));
+ return oldseedLabeling;
+ }
+
+
+ public int GetMaxlabelsseeded(RandomAccessibleInterval intimg) {
+
+ // To get maximum Labels on the image
+ Cursor intCursor = Views.iterable(intimg).cursor();
+ int currentLabel = 1;
+ boolean anythingFound = true;
+ while (anythingFound) {
+ anythingFound = false;
+ intCursor.reset();
+ while (intCursor.hasNext()) {
+ intCursor.fwd();
+ int i = intCursor.get().get();
+ if (i == currentLabel) {
+
+ anythingFound = true;
+
+ }
+ }
+ currentLabel++;
+ }
+
+ return currentLabel;
+
+ }
+
+ public NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg,
+ NativeImgLabeling seedLabeling) {
+
+ int n = inputimg.numDimensions();
+ long[] dimensions = new long[n];
+
+ for (int d = 0; d < n; ++d)
+ dimensions[d] = inputimg.dimension(d);
+ final NativeImgLabeling outputLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ final Watershed watershed = new Watershed();
+
+ watershed.setSeeds(seedLabeling);
+ watershed.setIntensityImage(inputimg);
+ watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2));
+ watershed.setOutputLabeling(outputLabeling);
+ watershed.process();
+ DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory();
+ LabelingROIStrategy> factory = deffactory
+ .createLabelingROIStrategy(watershed.getResult());
+ outputLabeling.setLabelingCursorStrategy(factory);
+
+ return outputLabeling;
+
+ }
+
+
+}
diff --git a/src/main/java/distanceTransform/DistWatershed.java b/src/main/java/distanceTransform/DistWatershed.java
new file mode 100644
index 0000000..ebe8dd9
--- /dev/null
+++ b/src/main/java/distanceTransform/DistWatershed.java
@@ -0,0 +1,237 @@
+package distanceTransform;
+
+import java.util.Iterator;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JProgressBar;
+import javax.swing.SwingWorker;
+
+import ij.IJ;
+import interactivePreprocessing.InteractiveMethods;
+import net.imglib2.Cursor;
+import net.imglib2.KDTree;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.RealPoint;
+import net.imglib2.RealPointSampleList;
+import net.imglib2.algorithm.labeling.AllConnectedComponents;
+import net.imglib2.algorithm.labeling.Watershed;
+import net.imglib2.img.ImgFactory;
+import net.imglib2.img.array.ArrayImgFactory;
+import net.imglib2.img.display.imagej.ImageJFunctions;
+import net.imglib2.labeling.DefaultROIStrategyFactory;
+import net.imglib2.labeling.Labeling;
+import net.imglib2.labeling.LabelingROIStrategy;
+import net.imglib2.labeling.NativeImgLabeling;
+import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree;
+import net.imglib2.roi.labeling.ImgLabeling;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.numeric.integer.IntType;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import net.imglib2.type.numeric.real.FloatType;
+import net.imglib2.util.Util;
+import net.imglib2.view.Views;
+
+public class DistWatershed > {
+
+
+ final InteractiveMethods parent;
+ final JProgressBar jpb;
+ private final RandomAccessibleInterval source;
+ private RandomAccessibleInterval watershedimage;
+
+ RandomAccessibleInterval distimg;
+ private final RandomAccessibleInterval bitimg;
+ public boolean apply3D = false;
+
+
+ public DistWatershed(final InteractiveMethods parent, final RandomAccessibleInterval source, final RandomAccessibleInterval bitimg, final JProgressBar jpb, boolean apply3D) {
+
+ this.parent = parent;
+ this.source = source;
+ this.bitimg = bitimg;
+ this.jpb = jpb;
+ this.apply3D = apply3D;
+
+ }
+
+
+ public void execute() {
+ // Perform the distance transform
+ final T type = source.randomAccess().get().createVariable();
+ final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new FloatType());
+ distimg = factory.create(source, new FloatType());
+
+ utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, "Doing Distance Transformed Watershedding, Please Wait...");
+ DistanceTransformImage(source, distimg);
+
+ // Prepare seed image for watershedding
+ NativeImgLabeling oldseedLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(source, new IntType()));
+ oldseedLabeling = PrepareSeedImage(source);
+ // Do watershedding on the distance transformed image
+
+ NativeImgLabeling outputLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(source, new IntType()));
+
+ outputLabeling = GetlabeledImage(distimg, oldseedLabeling);
+ watershedimage = outputLabeling.getStorageImg();
+
+ }
+ public RandomAccessibleInterval getResult() {
+
+ return watershedimage;
+ }
+ public RandomAccessibleInterval getDistanceTransformedimg() {
+
+ return distimg;
+ }
+ /***
+ *
+ * Do the distance transform of the input image using the bit image
+ * provided.
+ *
+ * @param inputimg
+ * The pre-processed input image as RandomAccessibleInterval
+ * @param outimg
+ * The distance transormed image having the same dimensions as
+ * the input image.
+ * @param invtype
+ * Straight: The intensity value is set to the distance, gives
+ * white on black background. Inverse: The intensity is set to
+ * the negative of the distance, gives black on white background.
+ */
+
+ private void DistanceTransformImage(RandomAccessibleInterval inputimg,
+ RandomAccessibleInterval outimg) {
+ int n = inputimg.numDimensions();
+
+ // make an empty list
+ final RealPointSampleList list = new RealPointSampleList(n);
+
+ // cursor on the binary image
+ final Cursor cursor = Views.iterable(bitimg).localizingCursor();
+
+ // for every pixel that is 1, make a new RealPoint at that location
+ while (cursor.hasNext())
+ if (cursor.next().getInteger() == 1)
+ list.add(new RealPoint(cursor), cursor.get());
+
+ // build the KD-Tree from the list of points that == 1
+ final KDTree tree = new KDTree(list);
+
+ // Instantiate a nearest neighbor search on the tree (does not modifiy
+ // the tree, just uses it)
+ final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree);
+
+ // randomaccess on the output
+ final RandomAccess ranac = outimg.randomAccess();
+
+ // reset cursor for the input (or make a new one)
+ cursor.reset();
+
+ // for every pixel of the binary image
+ while (cursor.hasNext()) {
+ cursor.fwd();
+
+ // set the randomaccess to the same location
+ ranac.setPosition(cursor);
+
+ // if value == 0, look for the nearest 1-valued pixel
+ if (cursor.get().getInteger() == 0) {
+ // search the nearest 1 to the location of the cursor (the
+ // current 0)
+ search.search(cursor);
+
+ // get the distance (the previous call could return that, this
+ // for generality that it is two calls)
+
+
+ ranac.get().setReal(search.getDistance());
+
+ } else {
+ // if value == 1, no need to search
+ ranac.get().setZero();
+ }
+ }
+
+ }
+
+ private NativeImgLabeling PrepareSeedImage(RandomAccessibleInterval inputimg) {
+
+ // New Labeling type
+ final ImgLabeling seedLabeling = new ImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ // Old Labeling type
+ final NativeImgLabeling oldseedLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ // The label generator for both new and old type
+ final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0);
+
+
+
+ // Getting unique labelled image (old version)
+ AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, bitimg, labelGenerator,
+ AllConnectedComponents.getStructuringElement(inputimg.numDimensions()));
+ return oldseedLabeling;
+ }
+
+
+ public int GetMaxlabelsseeded(RandomAccessibleInterval intimg) {
+
+ // To get maximum Labels on the image
+ Cursor intCursor = Views.iterable(intimg).cursor();
+ int currentLabel = 0;
+ boolean anythingFound = true;
+ while (anythingFound) {
+ anythingFound = false;
+ intCursor.reset();
+ while (intCursor.hasNext()) {
+ intCursor.fwd();
+ int i = intCursor.get().get();
+ if (i == currentLabel) {
+
+ anythingFound = true;
+
+ }
+ }
+ currentLabel++;
+ }
+
+ return currentLabel;
+
+ }
+
+ public NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg,
+ NativeImgLabeling seedLabeling) {
+
+ int n = inputimg.numDimensions();
+ long[] dimensions = new long[n];
+
+ for (int d = 0; d < n; ++d)
+ dimensions[d] = inputimg.dimension(d);
+ final NativeImgLabeling outputLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ final Watershed watershed = new Watershed();
+
+ watershed.setSeeds(seedLabeling);
+ watershed.setIntensityImage(inputimg);
+ watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2));
+ watershed.setOutputLabeling(outputLabeling);
+ watershed.process();
+ DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory();
+ LabelingROIStrategy> factory = deffactory
+ .createLabelingROIStrategy(watershed.getResult());
+ outputLabeling.setLabelingCursorStrategy(factory);
+
+ return outputLabeling;
+
+ }
+
+
+
+}
diff --git a/src/main/java/distanceTransform/DistWatershedBinary.java b/src/main/java/distanceTransform/DistWatershedBinary.java
new file mode 100644
index 0000000..dc0cf00
--- /dev/null
+++ b/src/main/java/distanceTransform/DistWatershedBinary.java
@@ -0,0 +1,267 @@
+package distanceTransform;
+
+import java.util.Iterator;
+
+import net.imglib2.Cursor;
+import net.imglib2.KDTree;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.RealPoint;
+import net.imglib2.RealPointSampleList;
+import net.imglib2.algorithm.BenchmarkAlgorithm;
+import net.imglib2.algorithm.OutputAlgorithm;
+import net.imglib2.algorithm.labeling.AllConnectedComponents;
+import net.imglib2.algorithm.labeling.Watershed;
+import net.imglib2.img.ImgFactory;
+import net.imglib2.img.array.ArrayImgFactory;
+import net.imglib2.labeling.DefaultROIStrategyFactory;
+import net.imglib2.labeling.Labeling;
+import net.imglib2.labeling.LabelingROIStrategy;
+import net.imglib2.labeling.NativeImgLabeling;
+import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree;
+import net.imglib2.roi.labeling.ImgLabeling;
+import net.imglib2.type.logic.BitType;
+import net.imglib2.type.numeric.integer.IntType;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import net.imglib2.util.Util;
+import net.imglib2.view.Views;
+
+
+
+@SuppressWarnings("deprecation")
+ public class DistWatershedBinary extends BenchmarkAlgorithm
+ implements OutputAlgorithm > {
+
+ private static final String BASE_ERROR_MSG = "[WatershedDistimg] ";
+ private final RandomAccessibleInterval source;
+
+ private RandomAccessibleInterval watershedimage;
+ /**
+ * Do watershedding after doing distance transformation on the biimg
+ * provided by the user using a user set threshold value.
+ *
+ * @param source
+ * The image to be watershedded.
+ * @param bitimg
+ * The image used to compute distance transform and seeds for watershedding.
+ */
+ public DistWatershedBinary(final RandomAccessibleInterval source){
+
+ this.source = source;
+ }
+
+
+
+ @Override
+ public boolean checkInput() {
+ if (source.numDimensions() > 2) {
+ errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D, make slices of your stack . Got "
+ + source.numDimensions() + "D.";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean process() {
+ // Perform the distance transform
+ final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new UnsignedByteType());
+ RandomAccessibleInterval distimg = factory.create(source, new UnsignedByteType());
+
+ DistanceTransformImage(source, distimg);
+
+ // Prepare seed image for watershedding
+ NativeImgLabeling oldseedLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(source, new IntType()));
+ oldseedLabeling = PrepareSeedImage(source);
+ // Do watershedding on the distance transformed image
+
+ NativeImgLabeling outputLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(source, new IntType()));
+
+ outputLabeling = GetlabeledImageByte(distimg, oldseedLabeling);
+
+ watershedimage = outputLabeling.getStorageImg();
+ return true;
+ }
+
+ @Override
+ public RandomAccessibleInterval getResult() {
+
+ return watershedimage;
+ }
+
+ public NativeImgLabeling GetlabeledImageByte(RandomAccessibleInterval inputimg,
+ NativeImgLabeling seedLabeling) {
+
+ int n = inputimg.numDimensions();
+ long[] dimensions = new long[n];
+
+ for (int d = 0; d < n; ++d)
+ dimensions[d] = inputimg.dimension(d);
+ final NativeImgLabeling outputLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ final Watershed watershed = new Watershed();
+
+ watershed.setSeeds(seedLabeling);
+ watershed.setIntensityImage(inputimg);
+ watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2));
+ watershed.setOutputLabeling(outputLabeling);
+ watershed.process();
+ DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory();
+ LabelingROIStrategy> factory = deffactory
+ .createLabelingROIStrategy(watershed.getResult());
+ outputLabeling.setLabelingCursorStrategy(factory);
+
+ return outputLabeling;
+
+ }
+
+ /***
+ *
+ * Do the distance transform of the input image using the bit image
+ * provided.
+ *
+ * @param inputimg
+ * The pre-processed input image as RandomAccessibleInterval
+ * @param outimg
+ * The distance transormed image having the same dimensions as
+ * the input image.
+ * @param invtype
+ * Straight: The intensity value is set to the distance, gives
+ * white on black background. Inverse: The intensity is set to
+ * the negative of the distance, gives black on white background.
+ */
+
+ private void DistanceTransformImage(RandomAccessibleInterval inputimg,
+ RandomAccessibleInterval outimg) {
+ int n = inputimg.numDimensions();
+
+ // make an empty list
+ final RealPointSampleList list = new RealPointSampleList(n);
+
+ // cursor on the binary image
+ final Cursor cursor = Views.iterable(source).localizingCursor();
+
+ // for every pixel that is 1, make a new RealPoint at that location
+ while (cursor.hasNext())
+ if (cursor.next().getInteger() == 1)
+ list.add(new RealPoint(cursor), cursor.get());
+
+ // build the KD-Tree from the list of points that == 1
+ final KDTree tree = new KDTree(list);
+
+ // Instantiate a nearest neighbor search on the tree (does not modifiy
+ // the tree, just uses it)
+ final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree);
+
+ // randomaccess on the output
+ final RandomAccess ranac = outimg.randomAccess();
+
+ // reset cursor for the input (or make a new one)
+ cursor.reset();
+
+ // for every pixel of the binary image
+ while (cursor.hasNext()) {
+ cursor.fwd();
+
+ // set the randomaccess to the same location
+ ranac.setPosition(cursor);
+
+ // if value == 0, look for the nearest 1-valued pixel
+ if (cursor.get().getInteger() == 0) {
+ // search the nearest 1 to the location of the cursor (the
+ // current 0)
+ search.search(cursor);
+
+ // get the distance (the previous call could return that, this
+ // for generality that it is two calls)
+
+
+ ranac.get().setReal(search.getDistance());
+
+ } else {
+ // if value == 1, no need to search
+ ranac.get().setZero();
+ }
+ }
+
+ }
+
+ private NativeImgLabeling PrepareSeedImage(RandomAccessibleInterval inputimg) {
+
+ // New Labeling type
+ final ImgLabeling seedLabeling = new ImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ // Old Labeling type
+ final NativeImgLabeling oldseedLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ // The label generator for both new and old type
+ final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0);
+
+
+
+ // Getting unique labelled image (old version)
+ AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, source, labelGenerator,
+ AllConnectedComponents.getStructuringElement(inputimg.numDimensions()));
+ return oldseedLabeling;
+ }
+
+
+ public int GetMaxlabelsseeded(RandomAccessibleInterval intimg) {
+
+ // To get maximum Labels on the image
+ Cursor intCursor = Views.iterable(intimg).cursor();
+ int currentLabel = 1;
+ boolean anythingFound = true;
+ while (anythingFound) {
+ anythingFound = false;
+ intCursor.reset();
+ while (intCursor.hasNext()) {
+ intCursor.fwd();
+ int i = intCursor.get().get();
+ if (i == currentLabel) {
+
+ anythingFound = true;
+
+ }
+ }
+ currentLabel++;
+ }
+
+ return currentLabel;
+
+ }
+
+ public NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg,
+ NativeImgLabeling seedLabeling) {
+
+ int n = inputimg.numDimensions();
+ long[] dimensions = new long[n];
+
+ for (int d = 0; d < n; ++d)
+ dimensions[d] = inputimg.dimension(d);
+ final NativeImgLabeling outputLabeling = new NativeImgLabeling(
+ new ArrayImgFactory().create(inputimg, new IntType()));
+
+ final Watershed watershed = new Watershed();
+
+ watershed.setSeeds(seedLabeling);
+ watershed.setIntensityImage(inputimg);
+ watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2));
+ watershed.setOutputLabeling(outputLabeling);
+ watershed.process();
+ DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory();
+ LabelingROIStrategy