Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Bioimage Analysis
Active Contour
Commits
f5df793f
Commit
f5df793f
authored
Jan 29, 2020
by
Daniel Felipe GONZALEZ OBANDO
Browse files
enabled volume constraint in Plugin GUI
parent
4be794ba
Changes
2
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
f5df793f
bin
pluginfile-active-contours*.jar
/ActiveContours.jar
src/plugins/adufour/activecontours/ActiveContours.java
View file @
f5df793f
...
...
@@ -80,1797 +80,1610 @@ import plugins.kernel.roi.roi3d.ROI3DStack;
import
plugins.nchenouard.spot.Detection
;
import
vtk.vtkObjectBase
;
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
>
{
final
ActiveContour
contour
;
final
boolean
maskBased
;
public
LocalRegionStatisticsComputer
(
ActiveContour
contour
,
boolean
maskBased
)
{
this
.
contour
=
contour
;
this
.
maskBased
=
maskBased
;
}
@Override
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
)
{
System
.
err
.
println
(
"Removing a contour. Reason: "
+
topo
.
getMessage
());
allContoursAtTimeT
.
remove
(
contour
);
evolvingContoursAtTimeT
.
remove
(
contour
);
}
return
null
;
}
}
private
class
LocalBackgroundStatisticsComputer
implements
Callable
<
Object
>
{
final
ActiveContour
contour
;
public
LocalBackgroundStatisticsComputer
(
ActiveContour
contour
)
{
this
.
contour
=
contour
;
}
@Override
public
Object
call
()
{
TrackSegment
segment
=
trackGroup
.
getValue
().
getTrackSegmentWithDetection
(
contour
);
double
cout
=
contour
.
computeBackgroundIntensity
(
regionData
,
contourMask_buffer
);
region_cout
.
put
(
segment
,
cout
);
return
null
;
}
}
private
class
ContourInitializer
implements
Callable
<
Object
>
{
final
ROI
roi
;
final
int
z
;
final
int
t
;
final
Tuple3d
pixelSize
;
final
int
convWinSize
;
final
List
<
TrackSegment
>
activeTracks
;
final
List
<
TrackSegment
>
endedTracks
;
public
ContourInitializer
(
ROI
roi
,
int
z
,
int
t
,
Tuple3d
pixelSize
,
int
convWinSize
,
List
<
TrackSegment
>
activeTracks
,
List
<
TrackSegment
>
justEndedTracks
)
{
super
();
this
.
roi
=
roi
;
this
.
z
=
z
;
this
.
t
=
t
;
this
.
pixelSize
=
pixelSize
;
this
.
convWinSize
=
convWinSize
;
this
.
activeTracks
=
activeTracks
;
this
.
endedTracks
=
justEndedTracks
;
}
// test if the object is colliding an existing contour or if we need to discard it for other reason
private
boolean
colliding
()
{
// // image bounds
// final Rectangle imageBounds = inputData.getBounds2D();
// // minus one (to detect object on border)
// imageBounds.x++;
// imageBounds.y++;
// imageBounds.width -= 2;
// imageBounds.height -= 2;
// test if object is intersecting with current active contours
for
(
TrackSegment
segment
:
activeTracks
)
{
// get contour for this active track
final
ActiveContour
contour
=
(
ActiveContour
)
segment
.
getLastDetection
();
// get ROI from contour
final
ROI
contourROI
=
contour
.
toROI
(
ROIType
.
POLYGON
,
null
);
// object is intersecting contour ? --> discard
if
(
roi
.
intersects
(
contourROI
))
return
true
;
}
return
false
;
}
@Override
public
Object
call
()
{
// object colliding active contours ? --> discard it
if
(
colliding
())
return
null
;
// get contour for the new object
final
ActiveContour
contour
=
getContourOf
(
roi
,
z
,
t
,
pixelSize
,
convWinSize
);
// error creating contour
if
(
contour
==
null
)
return
null
;
// does it overlap with a track that terminates in the previous frame?
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
))
{
System
.
out
.
println
(
"Found link at time "
+
t
+
", position ("
+
contour
.
getX
()
+
";"
+
contour
.
getY
()
+
")"
);
// add contour to the track
track
.
addDetection
(
contour
);
// done (no new track)
return
null
;
}
}
// need to create a new track
final
TrackSegment
result
=
new
TrackSegment
();
// add contour to it
result
.
addDetection
(
contour
);
// and return the new created track
return
result
;
}
}
private
class
ContourDuplicator
implements
Callable
<
Object
>
{
final
TrackSegment
segment
;
final
int
t
;
final
int
convWinSize
;
public
ContourDuplicator
(
TrackSegment
segment
,
int
t
,
int
convWinSize
)
{
super
();
this
.
segment
=
segment
;
this
.
t
=
t
;
this
.
convWinSize
=
convWinSize
;
}
@Override
public
Object
call
()
{
Detection
detection
;
detection
=
segment
.
getDetectionAtTime
(
t
);
// already have detection for that time point (shouldn't be the case) ? --> exit
if
(
detection
!=
null
)
return
detection
;
detection
=
segment
.
getDetectionAtTime
(
t
-
1
);
// no detection for previous time point ? nothing to do
if
(
detection
==
null
)
return
null
;
final
ActiveContour
previousContour
=
(
ActiveContour
)
detection
;
final
ActiveContour
currentContour
=
previousContour
.
clone
();
currentContour
.
convergence
.
setSize
(
convWinSize
);
currentContour
.
setT
(
t
);
segment
.
addDetection
(
currentContour
);
return
currentContour
;
}
}
private
final
double
EPSILON
=
0.0000001
;
private
final
EzVarBoolean
showAdvancedOptions
=
new
EzVarBoolean
(
"Show advanced options"
,
false
);
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
>
{
final
ActiveContour
contour
;
final
boolean
maskBased
;
public
LocalRegionStatisticsComputer
(
ActiveContour
contour
,
boolean
maskBased
)
{
this
.
contour
=
contour
;
this
.
maskBased
=
maskBased
;
}
@Override
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
)
{
System
.
err
.
println
(
"Removing a contour. Reason: "
+
topo
.
getMessage
());
allContoursAtTimeT
.
remove
(
contour
);
evolvingContoursAtTimeT
.
remove
(
contour
);
}
return
null
;
}
}
private
class
LocalBackgroundStatisticsComputer
implements
Callable
<
Object
>
{
final
ActiveContour
contour
;
public
LocalBackgroundStatisticsComputer
(
ActiveContour
contour
)
{
this
.
contour
=
contour
;
}
@Override
public
Object
call
()
{
TrackSegment
segment
=
trackGroup
.
getValue
().
getTrackSegmentWithDetection
(
contour
);
double
cout
=
contour
.
computeBackgroundIntensity
(
regionData
,
contourMask_buffer
);
region_cout
.
put
(
segment
,
cout
);
return
null
;
}
}
private
class
ContourInitializer
implements
Callable
<
Object
>
{
final
ROI
roi
;
final
int
z
;
final
int
t
;
final
Tuple3d
pixelSize
;
final
int
convWinSize
;
final
List
<
TrackSegment
>
activeTracks
;
final
List
<
TrackSegment
>
endedTracks
;
public
ContourInitializer
(
ROI
roi
,
int
z
,
int
t
,
Tuple3d
pixelSize
,
int
convWinSize
,
List
<
TrackSegment
>
activeTracks
,
List
<
TrackSegment
>
justEndedTracks
)
{
super
();
this
.
roi
=
roi
;
this
.
z
=
z
;
this
.
t
=
t
;
this
.
pixelSize
=
pixelSize
;
this
.
convWinSize
=
convWinSize
;
this
.
activeTracks
=
activeTracks
;
this
.
endedTracks
=
justEndedTracks
;
}
// test if the object is colliding an existing contour or if we need to discard
// it for other reason
private
boolean
colliding
()
{
// // image bounds
// final Rectangle imageBounds = inputData.getBounds2D();
// // minus one (to detect object on border)
// imageBounds.x++;
// imageBounds.y++;
// imageBounds.width -= 2;
// imageBounds.height -= 2;
// test if object is intersecting with current active contours
for
(
TrackSegment
segment
:
activeTracks
)
{
// get contour for this active track
final
ActiveContour
contour
=
(
ActiveContour
)
segment
.
getLastDetection
();
// get ROI from contour
final
ROI
contourROI
=
contour
.
toROI
(
ROIType
.
POLYGON
,
null
);
// object is intersecting contour ? --> discard
if
(
roi
.
intersects
(
contourROI
))
return
true
;
}
return
false
;
}
@Override
public
Object
call
()
{
// object colliding active contours ? --> discard it
if
(
colliding
())
return
null
;
// get contour for the new object
final
ActiveContour
contour
=
getContourOf
(
roi
,
z
,
t
,
pixelSize
,
convWinSize
);
// error creating contour
if
(
contour
==
null
)
return
null
;
// does it overlap with a track that terminates in the previous frame?
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
))
{
System
.
out
.
println
(
"Found link at time "
+
t
+
", position ("
+
contour
.
getX
()
+
";"
+
contour
.
getY
()
+
")"
);
// add contour to the track
track
.
addDetection
(
contour
);
// done (no new track)
return
null
;
}
}
// need to create a new track
final
TrackSegment
result
=
new
TrackSegment
();
// add contour to it
result
.
addDetection
(
contour
);
// and return the new created track
return
result
;
}
}
private
class
ContourDuplicator
implements
Callable
<
Object
>
{
final
TrackSegment
segment
;
final
int
t
;
final
int
convWinSize
;
public
ContourDuplicator
(
TrackSegment
segment
,
int
t
,
int
convWinSize
)
{
super
();
this
.
segment
=
segment
;
this
.
t
=
t
;
this
.
convWinSize
=
convWinSize
;
}
@Override
public
Object
call
()
{
Detection
detection
;
detection
=
segment
.
getDetectionAtTime
(
t
);
// already have detection for that time point (shouldn't be the case) ? --> exit
if
(
detection
!=
null
)
return
detection
;
detection
=
segment
.
getDetectionAtTime
(
t
-
1
);
// no detection for previous time point ? nothing to do
if
(
detection
==
null
)
return
null
;
final
ActiveContour
previousContour
=
(
ActiveContour
)
detection
;
final
ActiveContour
currentContour
=
previousContour
.
clone
();
currentContour
.
convergence
.
setSize
(
convWinSize
);
currentContour
.
setT
(
t
);
segment
.
addDetection
(
currentContour
);
public
final
EzVarSequence
input
=
new
EzVarSequence
(
"Input"
);
private
Sequence
inputData
;
return
currentContour
;
}
}
public
final
EzVarDouble
regul_weight
=
new
EzVarDouble
(
"Contour smoothness"
,
0.05
,
0
,
1.0
,
0.
01
)
;
private
final
double
EPSILON
=
0.00000
01
;
public
final
EzGroup
edge
=
new
EzGroup
(
"Find bright/dark edges"
);
public
final
EzVarDimensionPicker
edge_c
=
new
EzVarDimensionPicker
(
"Find edges in channel"
,
DimensionId
.
C
,
input
);
public
final
EzVarDouble
edge_weight
=
new
EzVarDouble
(
"Edge weight"
,
0
,
-
1
,
1
,
0.1
);
private
final
EzVarBoolean
showAdvancedOptions
=
new
EzVarBoolean
(
"Show advanced options"
,
false
);
public
final
EzGroup
region
=
new
EzGroup
(
"Find homogeneous intensity areas"
);
public
final
EzVarDimensionPicker
region_c
=
new
EzVarDimensionPicker
(
"Find regions in channel"
,
DimensionId
.
C
,
input
);
public
final
EzVarDouble
region_weight
=
new
EzVarDouble
(
"Region weight"
,
1.0
,
0.0
,
1.0
,
0.1
);
public
final
EzVarDouble
region_sensitivity
=
new
EzVarDouble
(
"Region sensitivity"
,
1.0
,
0.2
,
5.0
,
0.1
);
public
final
EzVarBoolean
region_localise
=
new
EzVarBoolean
(
"Variable background"
,
false
);
public
final
EzVarDouble
balloon_weight
=
new
EzVarDouble
(
"Contour inflation"
,
0
,
-
0.5
,
0.5
,
0.001
);
public
final
EzVarDouble
axis_weight
=
new
EzVarDouble
(
"Axis constraint"
,
0
,
0.0
,
1
,
0.1
);
public
final
EzVarBoolean
coupling_flag
=
new
EzVarBoolean
(
"Multi-contour coupling"
,
true
);
public
final
EzGroup
evolution
=
new
EzGroup
(
"Evolution parameters"
);
public
final
EzVarSequence
evolution_bounds
=
new
EzVarSequence
(
"Bound field to ROI of"
);
public
final
EzVarDouble
contour_resolution
=
new
EzVarDouble
(
"Contour sampling"
,
2
,
0.1
,
10000.0
,
0.1
);
public
final
EzVarDouble
contour_timeStep
=
new
EzVarDouble
(
"Evolution time step"
,
0.1
,
0.1
,
10
,
0.01
);
public
final
EzVarInteger
convergence_winSize
=
new
EzVarInteger
(
"Convergence window size"
,
50
,
10
,
10000
,
10
);
public
final
EzVarEnum
<
Operation
>
convergence_operation
=
new
EzVarEnum
<
SlidingWindow
.
Operation
>(
"Convergence operation"
,
Operation
.
values
(),
Operation
.
VAR_COEFF
);
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
{
NO
,
ON_INPUT
,
ON_NEW_IMAGE
,
AS_LABELS
}
public
enum
ROIType
{
AREA
(
ROI2DArea
.
class
),
POLYGON
(
ROI2DPolygon
.
class
);
public
final
EzVarSequence
input
=
new
EzVarSequence
(
"Input"
);
private
Sequence
inputData
;
final
Class
<?
extends
ROI
>
clazz
;
public
final
EzVarDouble
regul_weight
=
new
EzVarDouble
(
"Contour smoothness"
,
0.05
,
0
,
1.0
,
0.01
)
;
private
ROIType
(
Class
<?
extends
ROI
>
clazz
)
{
this
.
clazz
=
clazz
;
}
}
public
final
EzGroup
edge
=
new
EzGroup
(
"Find bright/dark edges"
);
public
final
EzVarDimensionPicker
edge_c
=
new
EzVarDimensionPicker
(
"Find edges in channel"
,
DimensionId
.
C
,
input
);
public
final
EzVarDouble
edge_weight
=
new
EzVarDouble
(
"Edge weight"
,
0
,
-
1
,
1
,
0.1
);
public
final
EzVarEnum
<
ExportROI
>
output_rois
=
new
EzVarEnum
<
ExportROI
>(
"Export ROI"
,
ExportROI
.
values
(),
ExportROI
.
NO
);
public
final
EzVarEnum
<
ROIType
>
output_roiType
=
new
EzVarEnum
<
ROIType
>(
"Type of ROI"
,
ROIType
.
values
(),
ROIType
.
AREA
);
private
VarSequence
output_labels
=
new
VarSequence
(
"Labels"
,
null
);
public
final
EzVarBoolean
tracking
=
new
EzVarBoolean
(
"Track objects over time"
,
false
);
public
final
EzVarDouble
division_sensitivity
=
new
EzVarDouble
(
"Division sensitivity"
,
0
,
0
,
2
,
0.1
);
public
final
EzVarBoolean
tracking_newObjects
=
new
EzVarBoolean
(
"Watch entering objects"
,
false
);
private
final
HashMap
<
TrackSegment
,
Double
>
volumes
=
new
HashMap
<
TrackSegment
,
Double
>();
public
final
EzVarBoolean
volume_constraint
=
new
EzVarBoolean
(
"Volume constraint"
,
false
);
public
final
EzVarDouble
volume_weight
=
new
EzVarDouble
(
"Volume weight"
,
0.01
,
0
,
1
,
0.01
);
public
final
EzButton
showTrackManager
;
private
Sequence
edgeData
;
private
Sequence
regionData
;
private
Sequence
regionDataSummed
;
private
BooleanMask3D
contourMask_buffer
;
HashMap
<
TrackSegment
,
Double
>
region_cin
=
new
HashMap
<
TrackSegment
,
Double
>(
0
);
HashMap
<
TrackSegment
,
Double
>
region_cout
=
new
HashMap
<
TrackSegment
,
Double
>(
0
);
public
final
VarROIArray
roiInput
=
new
VarROIArray
(
"input ROI"
);
public
final
VarROIArray
roiOutput
=
new
VarROIArray
(
"Regions of interest"
);
private
boolean
globalStop
;
Var
<
TrackGroup
>
trackGroup
=
new
Var
<
TrackGroup
>(
"Tracks"
,
TrackGroup
.
class
);
/**
* All contours present on the current time point
*/
private
final
HashSet
<
ActiveContour
>
allContoursAtTimeT
=
new
HashSet
<
ActiveContour
>();
/**
* Set of contours that have not yet converged on the current time point
*/
private
final
HashSet
<
ActiveContour
>
evolvingContoursAtTimeT
=
new
HashSet
<
ActiveContour
>();
private
ActiveContoursOverlay
overlay
;
private
Processor
multiThreadService
=
new
Processor
(
SystemUtil
.
getNumberOfCPUs
());
private
long
lastVtkGCTime
=
0L
;
public
ActiveContours
()
{
super
();
multiThreadService
.
setThreadName
(
"Active Contours"
);
showTrackManager
=
new
EzButton
(
"Send to track manager"
,
new
ActionListener
()
{
@Override
public
void
actionPerformed
(
ActionEvent
e
)
{
ThreadUtil
.
invokeLater
(
new
Runnable
()
{
@Override
public
void
run
()
{
if
(
trackGroup
.
getValue
()
==
null
)
return
;
if
(
trackGroup
.
getValue
().
getTrackSegmentList
().
isEmpty
())
return
;
Icy
.
getMainInterface
().
getSwimmingPool
().
add
(
new
SwimmingObject
(
trackGroup
.
getValue
()));
TrackManager
tm
=
new
TrackManager
();
tm
.
reOrganize
();
tm
.
setDisplaySequence
(
inputData
);
}
});
}
});
}
public
TrackGroup
getTrackGroup
()
{
return
trackGroup
.
getValue
();
}
@Override
public
void
initialize
()
{
addEzComponent
(
showAdvancedOptions
);
addEzComponent
(
input
);
// edge
edge
.
setToolTipText
(
"Sets the contour(s) to follow image intensity gradients"
);
edge_weight
.
setToolTipText
(
"Negative (resp. positive) weight pushes contours toward decreasing (resp. increasing) intensities"
);
edge
.
add
(
edge_c
,
edge_weight
);
addEzComponent
(
edge
);
// region
region
.
setToolTipText
(
"Sets the contour(s) to isolate homogeneous intensity regions"
);
region_weight
.
setToolTipText
(
"Set to 0 to deactivate this parameter"
);
region_sensitivity
.
setToolTipText
(
"Increase this value to be more sensitive to dim objects (default: 1)"
);
region_localise
.
setToolTipText
(
"Check this box if the image background is noisy or non-homogeneous"
);
showAdvancedOptions
.
addVisibilityTriggerTo
(
region_sensitivity
,
true
);
// don't show local means (for now)
showAdvancedOptions
.
addVisibilityTriggerTo
(
region_localise
,
true
);
region
.
add
(
region_c
,
region_weight
,
region_sensitivity
);
// , region_localise);
addEzComponent
(
region
);
// coupling
coupling_flag
.
setToolTipText
(
"Prevents multiple contours from overlapping"
);
showAdvancedOptions
.
addVisibilityTriggerTo
(
coupling_flag
,
true
);
addEzComponent
(
coupling_flag
);
// regul
regul_weight
.
setToolTipText
(
"Higher values result in a smoother contour, but may also slow its growth"
);
addEzComponent
(
regul_weight
);
// balloon force
balloon_weight
.
setToolTipText
(
"Positive (resp. negative) values will inflate (resp. deflate) the contour"
);
addEzComponent
(
balloon_weight
);
// axis contraint
axis_weight
.
setToolTipText
(
"Higher values restrict the evolution along the principal axis"
);
addEzComponent
(
axis_weight
);
// sensitivity to detect division ahead of time
division_sensitivity
.
setToolTipText
(
"Increase the sensitivity to cell division"
);
showAdvancedOptions
.
addVisibilityTriggerTo
(
division_sensitivity
,
true
);
addEzComponent
(
division_sensitivity
);
// contour
contour_resolution
.
setToolTipText
(
"Sets the contour(s) precision as the distance (in pixels) between control points"
);
contour_timeStep
.
setToolTipText
(
"Defines the evolution speed (warning: keep a low value to avoid vibration effects)"
);
convergence_winSize
.
setToolTipText
(
"Defines over how many iterations the algorithm should check for convergence"
);
showAdvancedOptions
.
addVisibilityTriggerTo
(
convergence_winSize
,
true
);
convergence_operation
.
setToolTipText
(
"Defines the operation used to detect convergence"
);
showAdvancedOptions
.
addVisibilityTriggerTo
(
convergence_operation
,
true
);
convergence_criterion
.
setToolTipText
(
"Defines the value of the criterion used to detect convergence"
);
convergence_nbIter
.
setToolTipText
(
"Defines the absolute number of iterations to use in case the contour does not converge automatically"
);
showAdvancedOptions
.
addVisibilityTriggerTo
(
convergence_nbIter
,
true
);
evolution_bounds
.
setNoSequenceSelection
();
evolution_bounds
.
setToolTipText
(
"Bounds the evolution of the contour to all ROI of the given sequence (select \"No sequence\" to deactivate)"
);