Commit 0e5a4626 authored by Stephane Dallongeville's avatar Stephane Dallongeville
Browse files

Tweak and minor fix on maximum iteration

- added detection of early complete operation when doing tracking and there is no more object to track.
- fixed an issue which was causing the double of maximum allowed number of iteration to be processed on contours (not really hurting but it was slower because of that).
parent e8206e4b
......@@ -29,6 +29,7 @@ public abstract class ActiveContour extends Detection implements Iterable<Point3
protected int id;
protected SlidingWindow convergence;
protected int lastConvergedFrame;
protected VarDouble sampling = new VarDouble("sampling", 1.0);
......@@ -47,6 +48,7 @@ public abstract class ActiveContour extends Detection implements Iterable<Point3
name = "";
id = -1;
lastConvergedFrame = -1;
}
@SuppressWarnings("unchecked")
......@@ -56,6 +58,7 @@ public abstract class ActiveContour extends Detection implements Iterable<Point3
name = "";
id = -1;
lastConvergedFrame = -1;
// follow and shortcut references to avoid memory leaks
while (sampling.getReference() != null)
......@@ -350,6 +353,16 @@ public abstract class ActiveContour extends Detection implements Iterable<Point3
*/
public abstract boolean hasConverged(SlidingWindow.Operation operation, double epsilon);
public int getLastConvergedFrame()
{
return lastConvergedFrame;
}
public void setLastConvergedFrame(int frame)
{
lastConvergedFrame = frame;
}
public void setDivisionSensitivity(Var<Double> divisionSensitivity)
{
this.divisionSensitivity = divisionSensitivity;
......
......@@ -80,26 +80,33 @@ import plugins.kernel.roi.roi3d.ROI3DStack;
import plugins.nchenouard.spot.Detection;
import vtk.vtkObjectBase;
public class ActiveContours extends EzPlug implements EzStoppable, Block {
public class ActiveContours extends EzPlug implements EzStoppable, Block
{
public static final String CONTOUR_BASE_NAME = "Contour #";
public static final String CONTOUR_ID = "contourId";
private class LocalRegionStatisticsComputer implements Callable<Object> {
private class LocalRegionStatisticsComputer implements Callable<Object>
{
final ActiveContour contour;
final boolean maskBased;
public LocalRegionStatisticsComputer(ActiveContour contour, boolean maskBased) {
public LocalRegionStatisticsComputer(ActiveContour contour, boolean maskBased)
{
this.contour = contour;
this.maskBased = maskBased;
}
@Override
public Object call() {
try {
public Object call()
{
try
{
double cin = contour.computeAverageIntensity(contour instanceof Mesh3D ? regionData : regionDataSummed,
maskBased ? contourMask_buffer : null);
region_cin.put(trackGroup.getValue().getTrackSegmentWithDetection(contour), cin);
} catch (TopologyException topo) {
}
catch (TopologyException topo)
{
System.err.println("Removing a contour. Reason: " + topo.getMessage());
allContoursAtTimeT.remove(contour);
evolvingContoursAtTimeT.remove(contour);
......@@ -108,15 +115,18 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
}
private class LocalBackgroundStatisticsComputer implements Callable<Object> {
private class LocalBackgroundStatisticsComputer implements Callable<Object>
{
final ActiveContour contour;
public LocalBackgroundStatisticsComputer(ActiveContour contour) {
public LocalBackgroundStatisticsComputer(ActiveContour contour)
{
this.contour = contour;
}
@Override
public Object call() {
public Object call()
{
TrackSegment segment = trackGroup.getValue().getTrackSegmentWithDetection(contour);
double cout = contour.computeBackgroundIntensity(regionData, contourMask_buffer);
......@@ -127,7 +137,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
}
private class ContourInitializer implements Callable<Object> {
private class ContourInitializer implements Callable<Object>
{
final ROI roi;
final int z;
final int t;
......@@ -137,7 +148,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
final List<TrackSegment> endedTracks;
public ContourInitializer(ROI roi, int z, int t, Tuple3d pixelSize, int convWinSize,
List<TrackSegment> activeTracks, List<TrackSegment> justEndedTracks) {
List<TrackSegment> activeTracks, List<TrackSegment> justEndedTracks)
{
super();
this.roi = roi;
......@@ -151,7 +163,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
// test if the object is colliding an existing contour or if we need to discard
// it for other reason
private boolean colliding() {
private boolean colliding()
{
// // image bounds
// final Rectangle imageBounds = inputData.getBounds2D();
// // minus one (to detect object on border)
......@@ -161,7 +174,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
// imageBounds.height -= 2;
// test if object is intersecting with current active contours
for (TrackSegment segment : activeTracks) {
for (TrackSegment segment : activeTracks)
{
// get contour for this active track
final ActiveContour contour = (ActiveContour) segment.getLastDetection();
// get ROI from contour
......@@ -176,7 +190,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
@Override
public Object call() {
public Object call()
{
// object colliding active contours ? --> discard it
if (colliding())
return null;
......@@ -188,13 +203,15 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
return null;
// does it overlap with a track that terminates in the previous frame?
for (TrackSegment track : endedTracks) {
for (TrackSegment track : endedTracks)
{
final ActiveContour previousContour = (ActiveContour) track.getLastDetection();
// get ROI from contour
final ROI previousContourROI = previousContour.toROI(ROIType.POLYGON, null);
// object is intersecting previous contour ?
if (roi.intersects(previousContourROI)) {
if (roi.intersects(previousContourROI))
{
System.out.println(
"Found link at time " + t + ", position (" + contour.getX() + ";" + contour.getY() + ")");
// add contour to the track
......@@ -214,12 +231,14 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
}
private class ContourDuplicator implements Callable<Object> {
private class ContourDuplicator implements Callable<Object>
{
final TrackSegment segment;
final int t;
final int convWinSize;
public ContourDuplicator(TrackSegment segment, int t, int convWinSize) {
public ContourDuplicator(TrackSegment segment, int t, int convWinSize)
{
super();
this.segment = segment;
......@@ -228,7 +247,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
@Override
public Object call() {
public Object call()
{
Detection detection;
detection = segment.getDetectionAtTime(t);
......@@ -289,16 +309,19 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
public final EzVarDouble convergence_criterion = new EzVarDouble("Convergence criterion", 0.001, 0, 1, 0.0001);
public final EzVarInteger convergence_nbIter = new EzVarInteger("Max. iterations", 100000, 100, 100000, 1000);
public enum ExportROI {
public enum ExportROI
{
NO, ON_INPUT, ON_NEW_IMAGE, AS_LABELS
}
public enum ROIType {
public enum ROIType
{
AREA(ROI2DArea.class), POLYGON(ROI2DPolygon.class);
final Class<? extends ROI> clazz;
private ROIType(Class<? extends ROI> clazz) {
private ROIType(Class<? extends ROI> clazz)
{
this.clazz = clazz;
}
}
......@@ -353,17 +376,22 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
private long lastVtkGCTime = 0L;
public ActiveContours() {
public ActiveContours()
{
super();
multiThreadService.setThreadName("Active Contours");
showTrackManager = new EzButton("Send to track manager", new ActionListener() {
showTrackManager = new EzButton("Send to track manager", new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e) {
ThreadUtil.invokeLater(new Runnable() {
public void actionPerformed(ActionEvent e)
{
ThreadUtil.invokeLater(new Runnable()
{
@Override
public void run() {
public void run()
{
if (trackGroup.getValue() == null)
return;
if (trackGroup.getValue().getTrackSegmentList().isEmpty())
......@@ -379,12 +407,14 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
});
}
public TrackGroup getTrackGroup() {
public TrackGroup getTrackGroup()
{
return trackGroup.getValue();
}
@Override
public void initialize() {
public void initialize()
{
addEzComponent(showAdvancedOptions);
addEzComponent(input);
......@@ -482,14 +512,16 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
@Override
public void loadParameters(File file) {
public void loadParameters(File file)
{
super.loadParameters(file);
volume_constraint.setValue(Boolean.FALSE);
}
@Override
public void execute() {
public void execute()
{
output_labels.setValue(null);
volumes.clear();
roiOutput.setValue(null);
......@@ -525,9 +557,11 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
contourMask_buffer = new BooleanMask3D(bounds3d, maskSlices);
if (!Icy.getMainInterface().isHeadLess()) {
if (!Icy.getMainInterface().isHeadLess())
{
// replace any ActiveContours Painter object on the sequence by ours
for (Overlay existingOverlay : inputData.getOverlays()) {
for (Overlay existingOverlay : inputData.getOverlays())
{
if (existingOverlay instanceof ActiveContoursOverlay)
existingOverlay.remove();
}
......@@ -545,13 +579,18 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
ROI field = null;
final Sequence boundSource = evolution_bounds.getValue();
if (boundSource != null) {
if (boundSource != null)
{
final List<ROI> rois = boundSource.getROIs();
if (rois.size() > 0) {
try {
if (rois.size() > 0)
{
try
{
field = ROIUtil.merge(rois, BooleanOperator.OR);
} catch (UnsupportedOperationException e) {
}
catch (UnsupportedOperationException e)
{
throw new VarException(evolution_bounds.getVariable(), "Cannot compute the evolution bounds: "
+ e.getMessage()
+ "\nIf you are not sure how to fix this, change this parameter to \"No Sequence\"");
......@@ -560,10 +599,12 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
// no specific field ? limit to sequence bounds
if (field == null) {
if (field == null)
{
if (inputData.getSizeZ() == 1)
field = new ROI2DRectangle(0, 0, inputData.getWidth(), inputData.getHeight());
else {
else
{
final ROI3DStack<ROI2DRectangle> field3D = new ROI3DStack<ROI2DRectangle>(ROI2DRectangle.class);
for (int z = 0; z < inputData.getSizeZ() - 1; z++)
......@@ -573,7 +614,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
}
for (int t = startT; t <= endT; t++) {
for (int t = startT; t <= endT; t++)
{
int iteration;
// if sizeT changed during AC processing (i already did it to shorthen
......@@ -607,6 +649,21 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
System.out.println("[Active Contours] converged current contours on frame " + t + " in " + iteration
+ " iterations");
// no more contours for this time point ?
if (iteration == -1)
{
// detection of new objects disable ?
if (!tracking_newObjects.getValue().booleanValue() || (inputData.getSizeZ() != 1))
{
// no ROIs to work with in future ? --> we can stop process here
if(!hasFutureRois(t))
{
storeResult(t);
break;
}
}
}
if (Thread.currentThread().isInterrupted())
break;
......@@ -637,21 +694,27 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
int maxId = 0;
// get maximum id
for (ROI roi : roiOutput.getValue()) {
try {
for (ROI roi : roiOutput.getValue())
{
try
{
// get object id
final int contourId = Integer.parseInt(roi.getProperty(CONTOUR_ID));
if (contourId > maxId)
maxId = contourId;
} catch (Exception e) {
}
catch (Exception e)
{
// ignore
}
}
// then fix ROI names
final int nbPaddingDigits = (int) Math.floor(Math.log10(maxId));
for (ROI roi : roiOutput.getValue()) {
try {
for (ROI roi : roiOutput.getValue())
{
try
{
// get object id
final int contourId = Integer.parseInt(roi.getProperty(CONTOUR_ID));
final String roiName = roi.getName();
......@@ -661,15 +724,19 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
else
roi.setName(
CONTOUR_BASE_NAME + StringUtil.toString(contourId, nbPaddingDigits) + " (" + roiName + ")");
} catch (Exception e) {
}
catch (Exception e)
{
// ignore
}
}
if (getUI() != null) {
if (getUI() != null)
{
Sequence out = inputData;
switch (output_rois.getValue()) {
switch (output_rois.getValue())
{
case ON_NEW_IMAGE:
out = SequenceUtil.getCopy(inputData);
out.setName(inputData.getName() + " + Active contours");
......@@ -711,17 +778,23 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
regionDataSummed = null;
}
boolean executeMultiThread(Collection<Callable<Object>> tasks, String messageOnError) throws Exception {
try {
boolean executeMultiThread(Collection<Callable<Object>> tasks, String messageOnError) throws Exception
{
try
{
// force wait completion for all tasks
for (Future<Object> res : multiThreadService.invokeAll(tasks))
res.get();
return true;
} catch (InterruptedException e) {
}
catch (InterruptedException e)
{
// restore the interrupted flag
Thread.currentThread().interrupt();
} catch (Exception e) {
}
catch (Exception e)
{
if (StringUtil.isEmpty(messageOnError))
throw e;
......@@ -732,7 +805,8 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
return false;
}
private void initData(int t) {
private void initData(int t)
{
final int edgeChannel = edge_c.getValue().intValue();
final int regionChannel = region_c.getValue().intValue();
......@@ -763,33 +837,39 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
regionData = new Sequence(OMEUtil.createOMEXMLMetadata(inputData.getOMEXMLMetadata()), "region data");
// convert float data rescaled to [0..1]
for (int z = 0; z < bounds.sizeZ; z++) {
for (int z = 0; z < bounds.sizeZ; z++)
{
IcyBufferedImage img;
img = currentFrame.getImage(0, z, edgeChannel);
img = IcyBufferedImageUtil.convertType(img, DataType.FLOAT, new Scaler[] { edgeScaler });
img = IcyBufferedImageUtil.convertType(img, DataType.FLOAT, new Scaler[] {edgeScaler});
edgeData.setImage(0, z, img);
img = currentFrame.getImage(0, z, regionChannel);
img = IcyBufferedImageUtil.convertType(img, DataType.FLOAT, new Scaler[] { regionScaler });
img = IcyBufferedImageUtil.convertType(img, DataType.FLOAT, new Scaler[] {regionScaler});
regionData.setImage(0, z, img);
}
try {
try
{
// smooth the signal
Sequence gaussian = Kernels1D.CUSTOM_GAUSSIAN.createGaussianKernel1D(1).toSequence();
Convolution1D.convolve(edgeData, gaussian, gaussian, null);
Convolution1D.convolve(regionData, gaussian, gaussian, null);
} catch (Exception e) {
}
catch (Exception e)
{
System.err.println("Warning: error while smoothing the signal: " + e.getMessage());
}
// summed region data (use to accelerate intensity calculations)
regionDataSummed = SequenceUtil.getCopy(regionData);
for (int z = 0; z < bounds.sizeZ; z++) {
for (int z = 0; z < bounds.sizeZ; z++)
{
final float[] regionDataSliceSummed = regionDataSummed.getDataXYAsFloat(0, z, 0);
for (int j = 0; j < bounds.sizeY; j++) {
for (int j = 0; j < bounds.sizeY; j++)
{
// start at the second pixel (index 1) of each line
int offset = (j * bounds.sizeX) + 1;
......@@ -803,27 +883,32 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
}
}
private void initCurrentContours(final int t) {
private void initCurrentContours(final int t)
{
// prepare parameters
final int convWinSize = convergence_winSize.getValue().intValue() * 2;
// use multi threading to initiate current contours (by duplicating ones from
// previous frame)
final List<Callable<Object>> tasks = new ArrayList<Callable<Object>>();
try {
try
{
// duplicate contours from the previous frame
for (TrackSegment segment : trackGroup.getValue().getTrackSegmentList())
tasks.add(new ContourDuplicator(segment, t, convWinSize));
// execute tasks
executeMultiThread(tasks, "couldn't duplicate a contour");
} catch (Exception e) {
}
catch (Exception e)
{
e.printStackTrace();
System.err.println("Warning: (reason: " + e.getMessage() + "). Moving on...");
}
}
private void addNewContours(final int t) {
private void addNewContours(final int t)
{
// prepare parameters
final TrackGroup tg = trackGroup.getValue();
final List<TrackSegment> tracks = trackGroup.getValue().getTrackSegmentList();
......@@ -844,38 +929,47 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
final List<Callable<Object>> tasks = new ArrayList<Callable<Object>>();
final List<TrackSegment> newTracks = new ArrayList<TrackSegment>();
try {
try
{
// initialize new contours
for (ROI roi : objects)
tasks.add(new ContourInitializer(roi, currentZ, t, pixelSize, convWinSize, activeTracks,
justEndedTracks));
// force wait completion for all tasks
for (Future<Object> res : multiThreadService.invokeAll(tasks)) {
for (Future<Object> res : multiThreadService.invokeAll(tasks))
{
final TrackSegment track = (TrackSegment) res.get();
// a new track was created ? --> add it
if (track != null)
newTracks.add(track);
}
} catch (InterruptedException e) {
}
catch (InterruptedException e)
{
// restore the interrupted flag
Thread.currentThread().interrupt();
} catch (Exception e) {
}
catch (Exception e)
{
e.printStackTrace();
System.err.println("Warning: couldn't initialize a contour (reason: " + e.getMessage() + ").");
}
// add new created tracks
synchronized (tg) {
synchronized (tg)
{
for (TrackSegment track : newTracks)
tg.addTrackSegment(track);
}
synchronized (region_cin) {
synchronized (region_cin)
{
for (TrackSegment track : newTracks)
region_cin.put(track, Double.valueOf(0d));
}
synchronized (region_cout) {
synchronized (region_cout)
{
for (TrackSegment track : newTracks)
region_cout.put(track, Double.valueOf(0d));
}
......@@ -884,11 +978,14 @@ public class ActiveContours extends EzPlug implements EzStoppable, Block {
/**
* Returns all {@link TrackSegment} ending at the specified time point
*/
private List<TrackSegment> getTracksEndingAt(List<TrackSegment> tracks, int t) {
private List<TrackSegment> getTracksEndingAt(List<TrackSegment> tracks, int t)
{
final List<TrackSegment> result = new ArrayList<TrackSegment>();
if (t >= 0) {
for (TrackSegment track : tracks) {
if (t >= 0)
{
for (TrackSegment track : tracks)
{