Skip to content

Commit a9b618c

Browse files
authored
Merge pull request #143 from lguerard/rollingball_options
Additional options for rolling ball background subtraction
2 parents dc6dbb8 + 94bea93 commit a9b618c

2 files changed

Lines changed: 202 additions & 31 deletions

File tree

src/imcflibs/imagej/processing.py

Lines changed: 143 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,77 @@
99
from ..log import LOG as log
1010

1111

12+
def filter_options(filter_method, filter_radius, do_3d=False):
13+
"""Build the ImageJ filter command and options strings.
14+
15+
Parameters
16+
----------
17+
filter_method : str
18+
Name of the filter method to use. Must be one of:
19+
- Median
20+
- Mean
21+
- Gaussian Blur
22+
- Minimum
23+
- Maximum
24+
filter_radius : int
25+
Radius of the filter to use.
26+
do_3d : bool, optional
27+
If set to True, will do a 3D filtering, by default False.
28+
29+
Returns
30+
-------
31+
tuple[str, str]
32+
The filter name and options strings.
33+
"""
34+
35+
if do_3d:
36+
filter_name = filter_method + " 3D..."
37+
else:
38+
filter_name = filter_method + "..."
39+
40+
if filter_method == "Gaussian Blur":
41+
options = "sigma=" + str(filter_radius) + " stack"
42+
else:
43+
options = "radius=" + str(filter_radius) + " stack"
44+
45+
return filter_name, options
46+
47+
48+
def threshold_options(threshold_method, do_3d=True):
49+
"""Build the ImageJ threshold option strings.
50+
51+
Parameters
52+
----------
53+
threshold_method : str
54+
Name of the threshold method to use.
55+
do_3d : bool, optional
56+
If set to True, the automatic threshold will be done on a 3D stack,
57+
by default True.
58+
59+
Returns
60+
-------
61+
tuple[str, str]
62+
The auto threshold options and the convert to binary options strings.
63+
"""
64+
65+
auto_threshold_options = (
66+
threshold_method + " " + "dark" + " " + "stack" if do_3d else ""
67+
)
68+
69+
convert_to_binary_options = (
70+
"method=" + threshold_method + " " + "background=Dark" + " " + "black"
71+
)
72+
73+
return auto_threshold_options, convert_to_binary_options
74+
75+
1276
def apply_filter(imp, filter_method, filter_radius, do_3d=False):
1377
"""Make a specific filter followed by a threshold method of choice.
1478
1579
Parameters
1680
----------
1781
imp : ImagePlus
18-
Input ImagePlus to filter and threshold
82+
Input ImagePlus to filter and threshold.
1983
filter_method : str
2084
Name of the filter method to use. Must be one of:
2185
- Median
@@ -24,16 +88,17 @@ def apply_filter(imp, filter_method, filter_radius, do_3d=False):
2488
- Minimum
2589
- Maximum
2690
filter_radius : int
27-
Radius of the filter filter to use
91+
Radius of the filter filter to use.
2892
do_3d : bool, optional
29-
If set to True, will do a 3D filtering, by default False
93+
If set to True, will do a 3D filtering, by default False.
3094
3195
3296
Returns
3397
-------
3498
ij.ImagePlus
35-
Filtered ImagePlus
99+
Filtered ImagePlus.
36100
"""
101+
37102
log.info("Applying filter %s with radius %d" % (filter_method, filter_radius))
38103

39104
if filter_method not in [
@@ -47,16 +112,7 @@ def apply_filter(imp, filter_method, filter_radius, do_3d=False):
47112
"filter_method must be one of: Median, Mean, Gaussian Blur, Minimum, Maximum"
48113
)
49114

50-
if do_3d:
51-
filter = filter_method + " 3D..."
52-
else:
53-
filter = filter_method + "..."
54-
55-
options = (
56-
"sigma="
57-
if filter_method == "Gaussian Blur"
58-
else "radius=" + str(filter_radius) + " stack"
59-
)
115+
filter, options = filter_options(filter_method, filter_radius, do_3d=do_3d)
60116

61117
log.debug("Filter: <%s> with options <%s>" % (filter, options))
62118

@@ -66,69 +122,125 @@ def apply_filter(imp, filter_method, filter_radius, do_3d=False):
66122
return imageplus
67123

68124

69-
def apply_rollingball_bg_subtraction(imp, rolling_ball_radius, do_3d=False):
125+
def apply_rollingball_bg_subtraction(
126+
imp,
127+
rolling_ball_radius,
128+
light_background=False,
129+
sliding=False,
130+
disable_smoothing=False,
131+
do_3d=False,
132+
):
70133
"""Perform background subtraction using a rolling ball method.
71134
72135
Parameters
73136
----------
74137
imp : ij.ImagePlus
75-
Input ImagePlus to filter and threshold
138+
Input ImagePlus to filter and threshold.
76139
rolling_ball_radius : int
77-
Radius of the rolling ball filter to use
140+
Radius of the rolling ball filter to use.
141+
light_background : bool, optional
142+
If set to True, will treat the background as light, by default False.
143+
sliding : bool, optional
144+
If set to True, will do a sliding window approach, by default False.
145+
disable_smoothing : bool, optional
146+
If set to True, will disable the smoothing, by default False.
78147
do_3d : bool, optional
79-
If set to True, will do a 3D filtering, by default False
148+
If set to True, will do a 3D filtering, by default False.
80149
81150
Returns
82151
-------
83152
ij.ImagePlus
84-
Filtered ImagePlus
153+
Filtered ImagePlus.
85154
"""
86155
log.info("Applying rolling ball with radius %d" % rolling_ball_radius)
87156

88-
options = "rolling=" + str(rolling_ball_radius) + " stack" if do_3d else ""
157+
options = rolling_ball_options(
158+
rolling_ball_radius,
159+
light_background=light_background,
160+
sliding=sliding,
161+
disable_smoothing=disable_smoothing,
162+
do_3d=do_3d,
163+
)
89164

90165
log.debug("Background subtraction options: %s" % options)
91166

92167
imageplus = imp.duplicate()
93-
IJ.run(imageplus, "Substract Background...", options)
168+
IJ.run(imageplus, "Subtract Background...", options)
94169

95170
return imageplus
96171

97172

173+
def rolling_ball_options(
174+
rolling_ball_radius,
175+
light_background=False,
176+
sliding=False,
177+
disable_smoothing=False,
178+
do_3d=False,
179+
):
180+
"""Generate the options for the "Subtract Background..." macro command.
181+
182+
Parameters
183+
----------
184+
rolling_ball_radius : int
185+
Radius of the rolling ball filter to use.
186+
light_background : bool, optional
187+
If set to True, will treat the background as light, by default False.
188+
sliding : bool, optional
189+
If set to True, will do a sliding window approach, by default False.
190+
disable_smoothing : bool, optional
191+
If set to True, will disable the smoothing, by default False.
192+
do_3d : bool, optional
193+
If set to True, will do a 3D filtering, by default False.
194+
195+
Returns
196+
-------
197+
str
198+
The options string for the "Subtract Background..." macro command.
199+
"""
200+
201+
parts = ["rolling=" + str(rolling_ball_radius)]
202+
if light_background:
203+
parts.append("light")
204+
if sliding:
205+
parts.append("sliding")
206+
if disable_smoothing:
207+
parts.append("disable")
208+
if do_3d:
209+
parts.append("stack")
210+
return " ".join(parts)
211+
212+
98213
def apply_threshold(imp, threshold_method, do_3d=True):
99214
"""Apply a threshold method to the input ImagePlus.
100215
101216
Parameters
102217
----------
103218
imp : ij.ImagePlus
104-
Input ImagePlus to filter and threshold
219+
Input ImagePlus to filter and threshold.
105220
threshold_method : str
106-
Name of the threshold method to use
221+
Name of the threshold method to use.
107222
do_3d : bool, optional
108-
If set to True, the automatic threshold will be done on a 3D stack, by default True
223+
If set to True, the automatic threshold will be done on a 3D stack,
224+
by default True.
109225
110226
Returns
111227
-------
112228
ij.ImagePlus
113-
Thresholded ImagePlus
229+
Thresholded ImagePlus.
114230
"""
115231

116232
log.info("Applying threshold method %s" % threshold_method)
117233

118234
imageplus = imp.duplicate()
119235

120-
auto_threshold_options = (
121-
threshold_method + " " + "dark" + " " + "stack" if do_3d else ""
236+
auto_threshold_options, convert_to_binary_options = threshold_options(
237+
threshold_method, do_3d=do_3d
122238
)
123239

124240
log.debug("Auto threshold options: %s" % auto_threshold_options)
125241

126242
IJ.setAutoThreshold(imageplus, auto_threshold_options)
127243

128-
convert_to_binary_options = (
129-
"method=" + threshold_method + " " + "background=Dark" + " " + "black"
130-
)
131-
132244
log.debug("Convert to binary options: %s" % convert_to_binary_options)
133245

134246
IJ.run(imageplus, "Convert to Mask", convert_to_binary_options)

tests/test_processing.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Tests for the imcflibs.imagej.processing module."""
2+
3+
from imcflibs.imagej.processing import (
4+
filter_options,
5+
rolling_ball_options,
6+
threshold_options,
7+
)
8+
9+
10+
def test_rolling_ball_options():
11+
"""Test the rolling_ball_options function."""
12+
13+
options = rolling_ball_options(42.23)
14+
assert options == "rolling=42.23"
15+
16+
17+
def test_rolling_ball_options_with_flags():
18+
"""Test `rolling_ball_options()` string concatenation with all flags."""
19+
20+
options = rolling_ball_options(
21+
12,
22+
light_background=True,
23+
sliding=True,
24+
disable_smoothing=True,
25+
do_3d=True,
26+
)
27+
assert options == "rolling=12 light sliding disable stack"
28+
29+
30+
def test_filter_options():
31+
"""Test `filter_options()` string concatenation."""
32+
33+
command, options = filter_options("Mean", 5, do_3d=True)
34+
assert command == "Mean 3D..."
35+
assert options == "radius=5 stack"
36+
37+
38+
def test_filter_options_gaussian_blur():
39+
"""Test `filter_options()` with the Gaussian Blur branch."""
40+
41+
command, options = filter_options("Gaussian Blur", 5)
42+
assert command == "Gaussian Blur..."
43+
assert options == "sigma=5 stack"
44+
45+
46+
def test_threshold_options():
47+
"""Test `threshold_options()` string concatenation."""
48+
49+
auto_threshold, convert_to_binary = threshold_options("Otsu", do_3d=True)
50+
assert auto_threshold == "Otsu dark stack"
51+
assert convert_to_binary == "method=Otsu background=Dark black"
52+
53+
54+
def test_threshold_options_without_stack():
55+
"""Test `threshold_options()` when 3D stacking is disabled."""
56+
57+
auto_threshold, convert_to_binary = threshold_options("Otsu", do_3d=False)
58+
assert auto_threshold == ""
59+
assert convert_to_binary == "method=Otsu background=Dark black"

0 commit comments

Comments
 (0)