Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 143 additions & 31 deletions src/imcflibs/imagej/processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,77 @@
from ..log import LOG as log


def filter_options(filter_method, filter_radius, do_3d=False):
"""Build the ImageJ filter command and options strings.

Parameters
----------
filter_method : str
Name of the filter method to use. Must be one of:
- Median
- Mean
- Gaussian Blur
- Minimum
- Maximum
filter_radius : int
Radius of the filter to use.
do_3d : bool, optional
If set to True, will do a 3D filtering, by default False.

Returns
-------
tuple[str, str]
The filter name and options strings.
"""

Comment thread
ehrenfeu marked this conversation as resolved.
if do_3d:
filter_name = filter_method + " 3D..."
else:
filter_name = filter_method + "..."

if filter_method == "Gaussian Blur":
options = "sigma=" + str(filter_radius) + " stack"
else:
options = "radius=" + str(filter_radius) + " stack"

return filter_name, options


def threshold_options(threshold_method, do_3d=True):
"""Build the ImageJ threshold option strings.

Parameters
----------
threshold_method : str
Name of the threshold method to use.
do_3d : bool, optional
If set to True, the automatic threshold will be done on a 3D stack,
by default True.

Returns
-------
tuple[str, str]
The auto threshold options and the convert to binary options strings.
"""

Comment thread
ehrenfeu marked this conversation as resolved.
auto_threshold_options = (
threshold_method + " " + "dark" + " " + "stack" if do_3d else ""
)

convert_to_binary_options = (
"method=" + threshold_method + " " + "background=Dark" + " " + "black"
)

return auto_threshold_options, convert_to_binary_options


def apply_filter(imp, filter_method, filter_radius, do_3d=False):
"""Make a specific filter followed by a threshold method of choice.

Parameters
----------
imp : ImagePlus
Input ImagePlus to filter and threshold
Input ImagePlus to filter and threshold.
filter_method : str
Name of the filter method to use. Must be one of:
- Median
Expand All @@ -24,16 +88,17 @@ def apply_filter(imp, filter_method, filter_radius, do_3d=False):
- Minimum
- Maximum
filter_radius : int
Radius of the filter filter to use
Radius of the filter filter to use.
do_3d : bool, optional
If set to True, will do a 3D filtering, by default False
If set to True, will do a 3D filtering, by default False.


Returns
-------
ij.ImagePlus
Filtered ImagePlus
Filtered ImagePlus.
"""

log.info("Applying filter %s with radius %d" % (filter_method, filter_radius))

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

if do_3d:
filter = filter_method + " 3D..."
else:
filter = filter_method + "..."

options = (
"sigma="
if filter_method == "Gaussian Blur"
else "radius=" + str(filter_radius) + " stack"
)
filter, options = filter_options(filter_method, filter_radius, do_3d=do_3d)

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

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


def apply_rollingball_bg_subtraction(imp, rolling_ball_radius, do_3d=False):
def apply_rollingball_bg_subtraction(
imp,
rolling_ball_radius,
light_background=False,
sliding=False,
disable_smoothing=False,
do_3d=False,
):
"""Perform background subtraction using a rolling ball method.

Parameters
----------
imp : ij.ImagePlus
Input ImagePlus to filter and threshold
Input ImagePlus to filter and threshold.
rolling_ball_radius : int
Radius of the rolling ball filter to use
Radius of the rolling ball filter to use.
light_background : bool, optional
If set to True, will treat the background as light, by default False.
sliding : bool, optional
If set to True, will do a sliding window approach, by default False.
disable_smoothing : bool, optional
If set to True, will disable the smoothing, by default False.
do_3d : bool, optional
If set to True, will do a 3D filtering, by default False
If set to True, will do a 3D filtering, by default False.

Returns
-------
ij.ImagePlus
Filtered ImagePlus
Filtered ImagePlus.
"""
log.info("Applying rolling ball with radius %d" % rolling_ball_radius)

options = "rolling=" + str(rolling_ball_radius) + " stack" if do_3d else ""
options = rolling_ball_options(
rolling_ball_radius,
light_background=light_background,
sliding=sliding,
disable_smoothing=disable_smoothing,
do_3d=do_3d,
)

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

imageplus = imp.duplicate()
IJ.run(imageplus, "Substract Background...", options)
IJ.run(imageplus, "Subtract Background...", options)

return imageplus


def rolling_ball_options(
rolling_ball_radius,
light_background=False,
sliding=False,
disable_smoothing=False,
do_3d=False,
):
"""Generate the options for the "Subtract Background..." macro command.

Parameters
----------
rolling_ball_radius : int
Radius of the rolling ball filter to use.
light_background : bool, optional
If set to True, will treat the background as light, by default False.
sliding : bool, optional
If set to True, will do a sliding window approach, by default False.
disable_smoothing : bool, optional
If set to True, will disable the smoothing, by default False.
do_3d : bool, optional
If set to True, will do a 3D filtering, by default False.

Returns
-------
str
The options string for the "Subtract Background..." macro command.
"""

parts = ["rolling=" + str(rolling_ball_radius)]
if light_background:
parts.append("light")
if sliding:
parts.append("sliding")
if disable_smoothing:
parts.append("disable")
if do_3d:
parts.append("stack")
return " ".join(parts)


def apply_threshold(imp, threshold_method, do_3d=True):
"""Apply a threshold method to the input ImagePlus.

Parameters
----------
imp : ij.ImagePlus
Input ImagePlus to filter and threshold
Input ImagePlus to filter and threshold.
threshold_method : str
Name of the threshold method to use
Name of the threshold method to use.
do_3d : bool, optional
If set to True, the automatic threshold will be done on a 3D stack, by default True
If set to True, the automatic threshold will be done on a 3D stack,
by default True.

Returns
-------
ij.ImagePlus
Thresholded ImagePlus
Thresholded ImagePlus.
"""

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

imageplus = imp.duplicate()

auto_threshold_options = (
threshold_method + " " + "dark" + " " + "stack" if do_3d else ""
auto_threshold_options, convert_to_binary_options = threshold_options(
threshold_method, do_3d=do_3d
)

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

IJ.setAutoThreshold(imageplus, auto_threshold_options)

convert_to_binary_options = (
"method=" + threshold_method + " " + "background=Dark" + " " + "black"
)

log.debug("Convert to binary options: %s" % convert_to_binary_options)

IJ.run(imageplus, "Convert to Mask", convert_to_binary_options)
Expand Down
59 changes: 59 additions & 0 deletions tests/test_processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Tests for the imcflibs.imagej.processing module."""

from imcflibs.imagej.processing import (
filter_options,
rolling_ball_options,
threshold_options,
)


def test_rolling_ball_options():
"""Test the rolling_ball_options function."""

options = rolling_ball_options(42.23)
assert options == "rolling=42.23"


def test_rolling_ball_options_with_flags():
"""Test `rolling_ball_options()` string concatenation with all flags."""

options = rolling_ball_options(
12,
light_background=True,
sliding=True,
disable_smoothing=True,
do_3d=True,
)
assert options == "rolling=12 light sliding disable stack"


def test_filter_options():
"""Test `filter_options()` string concatenation."""

command, options = filter_options("Mean", 5, do_3d=True)
assert command == "Mean 3D..."
assert options == "radius=5 stack"


def test_filter_options_gaussian_blur():
"""Test `filter_options()` with the Gaussian Blur branch."""

command, options = filter_options("Gaussian Blur", 5)
assert command == "Gaussian Blur..."
assert options == "sigma=5 stack"


def test_threshold_options():
"""Test `threshold_options()` string concatenation."""

auto_threshold, convert_to_binary = threshold_options("Otsu", do_3d=True)
assert auto_threshold == "Otsu dark stack"
assert convert_to_binary == "method=Otsu background=Dark black"


def test_threshold_options_without_stack():
"""Test `threshold_options()` when 3D stacking is disabled."""

auto_threshold, convert_to_binary = threshold_options("Otsu", do_3d=False)
assert auto_threshold == ""
assert convert_to_binary == "method=Otsu background=Dark black"
Loading