sigima 0.0.1.dev0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sigima/__init__.py +142 -2
- sigima/client/__init__.py +105 -0
- sigima/client/base.py +780 -0
- sigima/client/remote.py +469 -0
- sigima/client/stub.py +814 -0
- sigima/client/utils.py +90 -0
- sigima/config.py +444 -0
- sigima/data/logo/Sigima.svg +135 -0
- sigima/data/tests/annotations.json +798 -0
- sigima/data/tests/curve_fitting/exponential_fit.txt +511 -0
- sigima/data/tests/curve_fitting/gaussian_fit.txt +100 -0
- sigima/data/tests/curve_fitting/piecewiseexponential_fit.txt +1022 -0
- sigima/data/tests/curve_fitting/polynomial_fit.txt +100 -0
- sigima/data/tests/curve_fitting/twohalfgaussian_fit.txt +1000 -0
- sigima/data/tests/curve_formats/bandwidth.txt +201 -0
- sigima/data/tests/curve_formats/boxcar.npy +0 -0
- sigima/data/tests/curve_formats/datetime.txt +1001 -0
- sigima/data/tests/curve_formats/dynamic_parameters.txt +4000 -0
- sigima/data/tests/curve_formats/fw1e2.txt +301 -0
- sigima/data/tests/curve_formats/fwhm.txt +319 -0
- sigima/data/tests/curve_formats/multiple_curves.csv +29 -0
- sigima/data/tests/curve_formats/noised_saw.mat +0 -0
- sigima/data/tests/curve_formats/oscilloscope.csv +111 -0
- sigima/data/tests/curve_formats/other/other2/recursive2.txt +5 -0
- sigima/data/tests/curve_formats/other/recursive1.txt +5 -0
- sigima/data/tests/curve_formats/paracetamol.npy +0 -0
- sigima/data/tests/curve_formats/paracetamol.txt +1010 -0
- sigima/data/tests/curve_formats/paracetamol_dx_dy.csv +1000 -0
- sigima/data/tests/curve_formats/paracetamol_dy.csv +1001 -0
- sigima/data/tests/curve_formats/pulse1.npy +0 -0
- sigima/data/tests/curve_formats/pulse2.npy +0 -0
- sigima/data/tests/curve_formats/simple.txt +5 -0
- sigima/data/tests/curve_formats/spectrum.mca +2139 -0
- sigima/data/tests/curve_formats/square2.npy +0 -0
- sigima/data/tests/curve_formats/step.npy +0 -0
- sigima/data/tests/fabry-perot1.jpg +0 -0
- sigima/data/tests/fabry-perot2.jpg +0 -0
- sigima/data/tests/flower.npy +0 -0
- sigima/data/tests/image_formats/NF 180338201.scor-data +11003 -0
- sigima/data/tests/image_formats/binary_image.npy +0 -0
- sigima/data/tests/image_formats/binary_image.png +0 -0
- sigima/data/tests/image_formats/centroid_test.npy +0 -0
- sigima/data/tests/image_formats/coordinated_text/complex_image.txt +10011 -0
- sigima/data/tests/image_formats/coordinated_text/complex_ref_image.txt +10010 -0
- sigima/data/tests/image_formats/coordinated_text/image.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image2.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_no_unit_no_label.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_nan.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_unit.txt +14 -0
- sigima/data/tests/image_formats/fiber.csv +480 -0
- sigima/data/tests/image_formats/fiber.jpg +0 -0
- sigima/data/tests/image_formats/fiber.png +0 -0
- sigima/data/tests/image_formats/fiber.txt +480 -0
- sigima/data/tests/image_formats/gaussian_spot_with_noise.npy +0 -0
- sigima/data/tests/image_formats/mr-brain.dcm +0 -0
- sigima/data/tests/image_formats/noised_gaussian.mat +0 -0
- sigima/data/tests/image_formats/sif_reader/nd_lum_image_no_glue.sif +0 -0
- sigima/data/tests/image_formats/sif_reader/raman1.sif +0 -0
- sigima/data/tests/image_formats/tiling.txt +10 -0
- sigima/data/tests/image_formats/uint16.tiff +0 -0
- sigima/data/tests/image_formats/uint8.tiff +0 -0
- sigima/data/tests/laser_beam/TEM00_z_13.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_18.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_23.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_30.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_35.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_40.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_45.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_50.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_55.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_60.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_65.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_70.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_75.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_80.jpg +0 -0
- sigima/enums.py +195 -0
- sigima/io/__init__.py +123 -0
- sigima/io/base.py +311 -0
- sigima/io/common/__init__.py +5 -0
- sigima/io/common/basename.py +164 -0
- sigima/io/common/converters.py +189 -0
- sigima/io/common/objmeta.py +181 -0
- sigima/io/common/textreader.py +58 -0
- sigima/io/convenience.py +157 -0
- sigima/io/enums.py +17 -0
- sigima/io/ftlab.py +395 -0
- sigima/io/image/__init__.py +9 -0
- sigima/io/image/base.py +177 -0
- sigima/io/image/formats.py +1016 -0
- sigima/io/image/funcs.py +414 -0
- sigima/io/signal/__init__.py +9 -0
- sigima/io/signal/base.py +129 -0
- sigima/io/signal/formats.py +290 -0
- sigima/io/signal/funcs.py +723 -0
- sigima/objects/__init__.py +260 -0
- sigima/objects/base.py +937 -0
- sigima/objects/image/__init__.py +88 -0
- sigima/objects/image/creation.py +556 -0
- sigima/objects/image/object.py +524 -0
- sigima/objects/image/roi.py +904 -0
- sigima/objects/scalar/__init__.py +57 -0
- sigima/objects/scalar/common.py +215 -0
- sigima/objects/scalar/geometry.py +502 -0
- sigima/objects/scalar/table.py +784 -0
- sigima/objects/shape.py +290 -0
- sigima/objects/signal/__init__.py +133 -0
- sigima/objects/signal/constants.py +27 -0
- sigima/objects/signal/creation.py +1428 -0
- sigima/objects/signal/object.py +444 -0
- sigima/objects/signal/roi.py +274 -0
- sigima/params.py +405 -0
- sigima/proc/__init__.py +96 -0
- sigima/proc/base.py +381 -0
- sigima/proc/decorator.py +330 -0
- sigima/proc/image/__init__.py +513 -0
- sigima/proc/image/arithmetic.py +335 -0
- sigima/proc/image/base.py +260 -0
- sigima/proc/image/detection.py +519 -0
- sigima/proc/image/edges.py +329 -0
- sigima/proc/image/exposure.py +406 -0
- sigima/proc/image/extraction.py +458 -0
- sigima/proc/image/filtering.py +219 -0
- sigima/proc/image/fourier.py +147 -0
- sigima/proc/image/geometry.py +661 -0
- sigima/proc/image/mathops.py +340 -0
- sigima/proc/image/measurement.py +195 -0
- sigima/proc/image/morphology.py +155 -0
- sigima/proc/image/noise.py +107 -0
- sigima/proc/image/preprocessing.py +182 -0
- sigima/proc/image/restoration.py +235 -0
- sigima/proc/image/threshold.py +217 -0
- sigima/proc/image/transformations.py +393 -0
- sigima/proc/signal/__init__.py +376 -0
- sigima/proc/signal/analysis.py +206 -0
- sigima/proc/signal/arithmetic.py +551 -0
- sigima/proc/signal/base.py +262 -0
- sigima/proc/signal/extraction.py +60 -0
- sigima/proc/signal/features.py +310 -0
- sigima/proc/signal/filtering.py +484 -0
- sigima/proc/signal/fitting.py +276 -0
- sigima/proc/signal/fourier.py +259 -0
- sigima/proc/signal/mathops.py +420 -0
- sigima/proc/signal/processing.py +580 -0
- sigima/proc/signal/stability.py +175 -0
- sigima/proc/title_formatting.py +227 -0
- sigima/proc/validation.py +272 -0
- sigima/tests/__init__.py +7 -0
- sigima/tests/common/__init__.py +0 -0
- sigima/tests/common/arithmeticparam_unit_test.py +26 -0
- sigima/tests/common/basename_unit_test.py +126 -0
- sigima/tests/common/client_unit_test.py +412 -0
- sigima/tests/common/converters_unit_test.py +77 -0
- sigima/tests/common/decorator_unit_test.py +176 -0
- sigima/tests/common/examples_unit_test.py +104 -0
- sigima/tests/common/kernel_normalization_unit_test.py +242 -0
- sigima/tests/common/roi_basic_unit_test.py +73 -0
- sigima/tests/common/roi_geometry_unit_test.py +171 -0
- sigima/tests/common/scalar_builder_unit_test.py +142 -0
- sigima/tests/common/scalar_unit_test.py +991 -0
- sigima/tests/common/shape_unit_test.py +183 -0
- sigima/tests/common/stat_unit_test.py +138 -0
- sigima/tests/common/title_formatting_unit_test.py +338 -0
- sigima/tests/common/tools_coordinates_unit_test.py +60 -0
- sigima/tests/common/transformations_unit_test.py +178 -0
- sigima/tests/common/validation_unit_test.py +205 -0
- sigima/tests/conftest.py +129 -0
- sigima/tests/data.py +998 -0
- sigima/tests/env.py +280 -0
- sigima/tests/guiutils.py +163 -0
- sigima/tests/helpers.py +532 -0
- sigima/tests/image/__init__.py +28 -0
- sigima/tests/image/binning_unit_test.py +128 -0
- sigima/tests/image/blob_detection_unit_test.py +312 -0
- sigima/tests/image/centroid_unit_test.py +170 -0
- sigima/tests/image/check_2d_array_unit_test.py +63 -0
- sigima/tests/image/contour_unit_test.py +172 -0
- sigima/tests/image/convolution_unit_test.py +178 -0
- sigima/tests/image/datatype_unit_test.py +67 -0
- sigima/tests/image/edges_unit_test.py +155 -0
- sigima/tests/image/enclosingcircle_unit_test.py +88 -0
- sigima/tests/image/exposure_unit_test.py +223 -0
- sigima/tests/image/fft2d_unit_test.py +189 -0
- sigima/tests/image/filtering_unit_test.py +166 -0
- sigima/tests/image/geometry_unit_test.py +654 -0
- sigima/tests/image/hough_circle_unit_test.py +147 -0
- sigima/tests/image/imageobj_unit_test.py +737 -0
- sigima/tests/image/morphology_unit_test.py +71 -0
- sigima/tests/image/noise_unit_test.py +57 -0
- sigima/tests/image/offset_correction_unit_test.py +72 -0
- sigima/tests/image/operation_unit_test.py +518 -0
- sigima/tests/image/peak2d_limits_unit_test.py +41 -0
- sigima/tests/image/peak2d_unit_test.py +133 -0
- sigima/tests/image/profile_unit_test.py +159 -0
- sigima/tests/image/projections_unit_test.py +121 -0
- sigima/tests/image/restoration_unit_test.py +141 -0
- sigima/tests/image/roi2dparam_unit_test.py +53 -0
- sigima/tests/image/roi_advanced_unit_test.py +588 -0
- sigima/tests/image/roi_grid_unit_test.py +279 -0
- sigima/tests/image/spectrum2d_unit_test.py +40 -0
- sigima/tests/image/threshold_unit_test.py +91 -0
- sigima/tests/io/__init__.py +0 -0
- sigima/tests/io/addnewformat_unit_test.py +125 -0
- sigima/tests/io/convenience_funcs_unit_test.py +470 -0
- sigima/tests/io/coordinated_text_format_unit_test.py +495 -0
- sigima/tests/io/datetime_csv_unit_test.py +198 -0
- sigima/tests/io/imageio_formats_test.py +41 -0
- sigima/tests/io/ioregistry_unit_test.py +69 -0
- sigima/tests/io/objmeta_unit_test.py +87 -0
- sigima/tests/io/readobj_unit_test.py +130 -0
- sigima/tests/io/readwriteobj_unit_test.py +67 -0
- sigima/tests/signal/__init__.py +0 -0
- sigima/tests/signal/analysis_unit_test.py +135 -0
- sigima/tests/signal/check_1d_arrays_unit_test.py +169 -0
- sigima/tests/signal/convolution_unit_test.py +404 -0
- sigima/tests/signal/datetime_unit_test.py +176 -0
- sigima/tests/signal/fft1d_unit_test.py +303 -0
- sigima/tests/signal/filters_unit_test.py +403 -0
- sigima/tests/signal/fitting_unit_test.py +929 -0
- sigima/tests/signal/fwhm_unit_test.py +111 -0
- sigima/tests/signal/noise_unit_test.py +128 -0
- sigima/tests/signal/offset_correction_unit_test.py +34 -0
- sigima/tests/signal/operation_unit_test.py +489 -0
- sigima/tests/signal/peakdetection_unit_test.py +145 -0
- sigima/tests/signal/processing_unit_test.py +657 -0
- sigima/tests/signal/pulse/__init__.py +112 -0
- sigima/tests/signal/pulse/crossing_times_unit_test.py +123 -0
- sigima/tests/signal/pulse/plateau_detection_unit_test.py +102 -0
- sigima/tests/signal/pulse/pulse_unit_test.py +1824 -0
- sigima/tests/signal/roi_advanced_unit_test.py +392 -0
- sigima/tests/signal/signalobj_unit_test.py +603 -0
- sigima/tests/signal/stability_unit_test.py +431 -0
- sigima/tests/signal/uncertainty_unit_test.py +611 -0
- sigima/tests/vistools.py +1030 -0
- sigima/tools/__init__.py +59 -0
- sigima/tools/checks.py +290 -0
- sigima/tools/coordinates.py +308 -0
- sigima/tools/datatypes.py +26 -0
- sigima/tools/image/__init__.py +97 -0
- sigima/tools/image/detection.py +451 -0
- sigima/tools/image/exposure.py +77 -0
- sigima/tools/image/extraction.py +48 -0
- sigima/tools/image/fourier.py +260 -0
- sigima/tools/image/geometry.py +190 -0
- sigima/tools/image/preprocessing.py +165 -0
- sigima/tools/signal/__init__.py +86 -0
- sigima/tools/signal/dynamic.py +254 -0
- sigima/tools/signal/features.py +135 -0
- sigima/tools/signal/filtering.py +171 -0
- sigima/tools/signal/fitting.py +1171 -0
- sigima/tools/signal/fourier.py +466 -0
- sigima/tools/signal/interpolation.py +70 -0
- sigima/tools/signal/peakdetection.py +126 -0
- sigima/tools/signal/pulse.py +1626 -0
- sigima/tools/signal/scaling.py +50 -0
- sigima/tools/signal/stability.py +258 -0
- sigima/tools/signal/windowing.py +90 -0
- sigima/worker.py +79 -0
- sigima-1.0.0.dist-info/METADATA +233 -0
- sigima-1.0.0.dist-info/RECORD +262 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/licenses/LICENSE +29 -29
- sigima-0.0.1.dev0.dist-info/METADATA +0 -60
- sigima-0.0.1.dev0.dist-info/RECORD +0 -6
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/WHEEL +0 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Mathematical Operations Module
|
|
5
|
+
------------------------------
|
|
6
|
+
|
|
7
|
+
This module implements mathematical operations on images, such as inversion,
|
|
8
|
+
absolute value, real/imaginary part extraction, type casting, and exponentiation.
|
|
9
|
+
|
|
10
|
+
Main features include:
|
|
11
|
+
|
|
12
|
+
- Pixel-wise mathematical transformations (e.g., log, exp, abs, real, imag, log10)
|
|
13
|
+
- Type casting and other value-level operations
|
|
14
|
+
|
|
15
|
+
These functions enable flexible manipulation of image data at the value level.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
19
|
+
|
|
20
|
+
# Note:
|
|
21
|
+
# ----
|
|
22
|
+
# - All `guidata.dataset.DataSet` parameter classes must also be imported
|
|
23
|
+
# in the `sigima.params` module.
|
|
24
|
+
# - All functions decorated by `computation_function` must be imported in the upper
|
|
25
|
+
# level `sigima.proc.image` module.
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import warnings
|
|
30
|
+
|
|
31
|
+
import guidata.dataset as gds
|
|
32
|
+
import numpy as np
|
|
33
|
+
|
|
34
|
+
import sigima.tools.image
|
|
35
|
+
from sigima.config import _
|
|
36
|
+
from sigima.config import options as sigima_options
|
|
37
|
+
from sigima.enums import AngleUnit
|
|
38
|
+
from sigima.objects.image import ImageObj
|
|
39
|
+
from sigima.proc.base import AngleUnitParam, PhaseParam, dst_1_to_1, dst_2_to_1
|
|
40
|
+
from sigima.proc.decorator import computation_function
|
|
41
|
+
from sigima.proc.image.base import Wrap1to1Func, restore_data_outside_roi
|
|
42
|
+
from sigima.tools import coordinates
|
|
43
|
+
from sigima.tools.datatypes import clip_astype
|
|
44
|
+
|
|
45
|
+
# NOTE: Only parameter classes DEFINED in this module should be included in __all__.
|
|
46
|
+
# Parameter classes imported from other modules (like sigima.proc.base) should NOT
|
|
47
|
+
# be re-exported to avoid Sphinx cross-reference conflicts. The sigima.params module
|
|
48
|
+
# serves as the central API point that imports and re-exports all parameter classes.
|
|
49
|
+
__all__ = [
|
|
50
|
+
"DataTypeIParam",
|
|
51
|
+
"Log10ZPlusNParam",
|
|
52
|
+
"absolute",
|
|
53
|
+
"absolute",
|
|
54
|
+
"astype",
|
|
55
|
+
"complex_from_magnitude_phase",
|
|
56
|
+
"complex_from_real_imag",
|
|
57
|
+
"convolution",
|
|
58
|
+
"deconvolution",
|
|
59
|
+
"exp",
|
|
60
|
+
"exp",
|
|
61
|
+
"imag",
|
|
62
|
+
"imag",
|
|
63
|
+
"inverse",
|
|
64
|
+
"inverse",
|
|
65
|
+
"log10",
|
|
66
|
+
"log10",
|
|
67
|
+
"log10_z_plus_n",
|
|
68
|
+
"phase",
|
|
69
|
+
"real",
|
|
70
|
+
"real",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@computation_function()
|
|
75
|
+
def inverse(src: ImageObj) -> ImageObj:
|
|
76
|
+
"""Compute the inverse of an image and return the new result image object
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
src: input image object
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Result image object 1 / **src** (new object)
|
|
83
|
+
"""
|
|
84
|
+
dst = dst_1_to_1(src, "inverse")
|
|
85
|
+
with warnings.catch_warnings():
|
|
86
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
87
|
+
dst.data = np.reciprocal(src.data, dtype=float)
|
|
88
|
+
dst.data[np.isinf(dst.data)] = np.nan
|
|
89
|
+
restore_data_outside_roi(dst, src)
|
|
90
|
+
return dst
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@computation_function()
|
|
94
|
+
def absolute(src: ImageObj) -> ImageObj:
|
|
95
|
+
"""Compute absolute value with :py:data:`numpy.absolute`
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
src: input image object
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Output image object
|
|
102
|
+
"""
|
|
103
|
+
return Wrap1to1Func(np.absolute)(src)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@computation_function()
|
|
107
|
+
def real(src: ImageObj) -> ImageObj:
|
|
108
|
+
"""Compute real part with :py:func:`numpy.real`
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
src: input image object
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Output image object
|
|
115
|
+
"""
|
|
116
|
+
return Wrap1to1Func(np.real)(src)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@computation_function()
|
|
120
|
+
def imag(src: ImageObj) -> ImageObj:
|
|
121
|
+
"""Compute imaginary part with :py:func:`numpy.imag`
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
src: input image object
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Output image object
|
|
128
|
+
"""
|
|
129
|
+
return Wrap1to1Func(np.imag)(src)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@computation_function()
|
|
133
|
+
def phase(src: ImageObj, p: PhaseParam) -> ImageObj:
|
|
134
|
+
"""Compute the phase (argument) of a complex image.
|
|
135
|
+
|
|
136
|
+
The function uses :py:func:`numpy.angle` to compute the argument and
|
|
137
|
+
:py:func:`numpy.unwrap` to unwrap it.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
src: Input image object.
|
|
141
|
+
p: Phase parameters.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Image object containing the phase, optionally unwrapped.
|
|
145
|
+
"""
|
|
146
|
+
suffix = "unwrap" if p.unwrap else ""
|
|
147
|
+
dst = dst_1_to_1(src, "phase", suffix)
|
|
148
|
+
data = src.get_data()
|
|
149
|
+
argument = np.angle(data)
|
|
150
|
+
if p.unwrap:
|
|
151
|
+
argument = np.unwrap(argument)
|
|
152
|
+
if p.unit == AngleUnit.DEGREE:
|
|
153
|
+
argument = np.rad2deg(argument)
|
|
154
|
+
dst.data = argument
|
|
155
|
+
dst.zunit = p.unit
|
|
156
|
+
restore_data_outside_roi(dst, src)
|
|
157
|
+
return dst
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@computation_function()
|
|
161
|
+
def complex_from_magnitude_phase(
|
|
162
|
+
src1: ImageObj, src2: ImageObj, p: AngleUnitParam
|
|
163
|
+
) -> ImageObj:
|
|
164
|
+
"""Combine magnitude and phase images into a complex image.
|
|
165
|
+
|
|
166
|
+
.. warning::
|
|
167
|
+
|
|
168
|
+
This function assumes that the input images have the same dimensions.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
src1: Magnitude (module) image.
|
|
172
|
+
src2: Phase (argument) image.
|
|
173
|
+
p: Parameters (provides unit for the phase).
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Image object with complex-valued z.
|
|
177
|
+
"""
|
|
178
|
+
dst = dst_2_to_1(src1, src2, "mag_phase")
|
|
179
|
+
assert p.unit is not None
|
|
180
|
+
dst.data = coordinates.polar_to_complex(src1.data, src2.data, unit=p.unit)
|
|
181
|
+
return dst
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@computation_function()
|
|
185
|
+
def complex_from_real_imag(src1: ImageObj, src2: ImageObj) -> ImageObj:
|
|
186
|
+
"""Combine two real images into a complex image using real + i * imag.
|
|
187
|
+
|
|
188
|
+
.. warning::
|
|
189
|
+
|
|
190
|
+
This function assumes that the input images have the same dimensions and are
|
|
191
|
+
properly aligned.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
src1: Real part image.
|
|
195
|
+
src2: Imaginary part image.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Image object with complex-valued z.
|
|
199
|
+
|
|
200
|
+
Raises:
|
|
201
|
+
ValueError: If the x or y coordinates of the two images are not the same.
|
|
202
|
+
"""
|
|
203
|
+
dst = dst_2_to_1(src1, src2, "real_imag")
|
|
204
|
+
assert src1.data is not None
|
|
205
|
+
assert src2.data is not None
|
|
206
|
+
dst.data = src1.data + 1j * src2.data
|
|
207
|
+
return dst
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@computation_function()
|
|
211
|
+
def convolution(src: ImageObj, kernel: ImageObj) -> ImageObj:
|
|
212
|
+
"""Convolve an image with a kernel.
|
|
213
|
+
|
|
214
|
+
The kernel should ideally be smaller than the input image and centered.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
src: Input image object.
|
|
218
|
+
kernel: Kernel image object.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Output image object.
|
|
222
|
+
|
|
223
|
+
Notes:
|
|
224
|
+
The behavior of kernel normalization is controlled by the global configuration
|
|
225
|
+
option ``sigima.config.options.auto_normalize_kernel``.
|
|
226
|
+
"""
|
|
227
|
+
# Get configuration option for kernel normalization
|
|
228
|
+
normalize_kernel = sigima_options.auto_normalize_kernel.get()
|
|
229
|
+
|
|
230
|
+
dst = dst_2_to_1(src, kernel, "⊛")
|
|
231
|
+
dst.data = sigima.tools.image.convolve(
|
|
232
|
+
src.data,
|
|
233
|
+
kernel.data,
|
|
234
|
+
normalize_kernel_flag=normalize_kernel,
|
|
235
|
+
)
|
|
236
|
+
restore_data_outside_roi(dst, src)
|
|
237
|
+
return dst
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@computation_function()
|
|
241
|
+
def deconvolution(src: ImageObj, kernel: ImageObj) -> ImageObj:
|
|
242
|
+
"""Deconvolve a kernel from an image using Fast Fourier Transform (FFT).
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
src: Input image object.
|
|
246
|
+
kernel: Kernel image object.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Output image object.
|
|
250
|
+
|
|
251
|
+
Notes:
|
|
252
|
+
The behavior of kernel normalization is controlled by the global configuration
|
|
253
|
+
option ``sigima.config.options.auto_normalize_kernel``.
|
|
254
|
+
"""
|
|
255
|
+
# Get configuration option for kernel normalization
|
|
256
|
+
normalize_kernel = sigima_options.auto_normalize_kernel.get()
|
|
257
|
+
|
|
258
|
+
dst = dst_2_to_1(src, kernel, "⊛⁻¹")
|
|
259
|
+
dst.data = sigima.tools.image.deconvolve(
|
|
260
|
+
src.data,
|
|
261
|
+
kernel.data,
|
|
262
|
+
normalize_kernel_flag=normalize_kernel,
|
|
263
|
+
)
|
|
264
|
+
restore_data_outside_roi(dst, src)
|
|
265
|
+
return dst
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@computation_function()
|
|
269
|
+
def log10(src: ImageObj) -> ImageObj:
|
|
270
|
+
"""Compute log10 with :py:data:`numpy.log10`
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
src: input image object
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Output image object
|
|
277
|
+
"""
|
|
278
|
+
return Wrap1to1Func(np.log10)(src)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@computation_function()
|
|
282
|
+
def exp(src: ImageObj) -> ImageObj:
|
|
283
|
+
"""Compute exponential with :py:data:`numpy.exp`
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
src: input image object
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Output image object
|
|
290
|
+
"""
|
|
291
|
+
return Wrap1to1Func(np.exp)(src)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class Log10ZPlusNParam(gds.DataSet):
|
|
295
|
+
"""Log10(z+n) parameters"""
|
|
296
|
+
|
|
297
|
+
n = gds.FloatItem("n")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@computation_function()
|
|
301
|
+
def log10_z_plus_n(src: ImageObj, p: Log10ZPlusNParam) -> ImageObj:
|
|
302
|
+
"""Compute log10(z+n) with :py:data:`numpy.log10`
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
src: input image object
|
|
306
|
+
p: parameters
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Output image object
|
|
310
|
+
"""
|
|
311
|
+
dst = dst_1_to_1(src, "log10_z_plus_n", f"n={p.n}")
|
|
312
|
+
dst.data = np.log10(src.data + p.n)
|
|
313
|
+
restore_data_outside_roi(dst, src)
|
|
314
|
+
return dst
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class DataTypeIParam(gds.DataSet):
|
|
318
|
+
"""Convert image data type parameters"""
|
|
319
|
+
|
|
320
|
+
dtype_str = gds.ChoiceItem(
|
|
321
|
+
_("Destination data type"),
|
|
322
|
+
list(zip(ImageObj.get_valid_dtypenames(), ImageObj.get_valid_dtypenames())),
|
|
323
|
+
help=_("Output image data type."),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@computation_function()
|
|
328
|
+
def astype(src: ImageObj, p: DataTypeIParam) -> ImageObj:
|
|
329
|
+
"""Convert image data type with :py:func:`sigima.tools.datatypes.clip_astype`
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
src: input image object
|
|
333
|
+
p: parameters
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Output image object
|
|
337
|
+
"""
|
|
338
|
+
dst = dst_1_to_1(src, "clip_astype", p.dtype_str)
|
|
339
|
+
dst.data = clip_astype(src.data, p.dtype_str)
|
|
340
|
+
return dst
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Measurement computation module
|
|
5
|
+
------------------------------
|
|
6
|
+
|
|
7
|
+
This module provides tools for extracting quantitative information from images,
|
|
8
|
+
such as object centroids, enclosing circles, and region-based statistics.
|
|
9
|
+
|
|
10
|
+
Main features include:
|
|
11
|
+
|
|
12
|
+
- Centroid and enclosing circle computation
|
|
13
|
+
- Region/property measurements
|
|
14
|
+
- Statistical analysis of image regions
|
|
15
|
+
|
|
16
|
+
These functions are useful for image quantification and morphometric analysis.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
20
|
+
|
|
21
|
+
# Note:
|
|
22
|
+
# ----
|
|
23
|
+
# - All `guidata.dataset.DataSet` parameter classes must also be imported
|
|
24
|
+
# in the `sigima.params` module.
|
|
25
|
+
# - All functions decorated by `computation_function` must be imported in the upper
|
|
26
|
+
# level `sigima.proc.image` module.
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
from numpy import ma
|
|
32
|
+
|
|
33
|
+
import sigima.tools.image
|
|
34
|
+
from sigima.config import _
|
|
35
|
+
from sigima.objects import (
|
|
36
|
+
GeometryResult,
|
|
37
|
+
ImageObj,
|
|
38
|
+
SignalObj,
|
|
39
|
+
TableKind,
|
|
40
|
+
TableResult,
|
|
41
|
+
TableResultBuilder,
|
|
42
|
+
)
|
|
43
|
+
from sigima.proc.base import new_signal_result
|
|
44
|
+
from sigima.proc.decorator import computation_function
|
|
45
|
+
from sigima.proc.image.base import compute_geometry_from_obj
|
|
46
|
+
|
|
47
|
+
# NOTE: Only parameter classes DEFINED in this module should be included in __all__.
|
|
48
|
+
# Parameter classes imported from other modules (like sigima.proc.base) should NOT
|
|
49
|
+
# be re-exported to avoid Sphinx cross-reference conflicts. The sigima.params module
|
|
50
|
+
# serves as the central API point that imports and re-exports all parameter classes.
|
|
51
|
+
__all__ = [
|
|
52
|
+
"centroid",
|
|
53
|
+
"enclosing_circle",
|
|
54
|
+
"horizontal_projection",
|
|
55
|
+
"stats",
|
|
56
|
+
"vertical_projection",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_centroid_coords(data: np.ndarray) -> np.ndarray:
|
|
61
|
+
"""Return centroid coordinates
|
|
62
|
+
with :py:func:`sigima.tools.image.get_centroid_auto`
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
data: input data
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Centroid coordinates
|
|
69
|
+
"""
|
|
70
|
+
y, x = sigima.tools.image.get_centroid_auto(data)
|
|
71
|
+
return np.array([(x, y)])
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@computation_function()
|
|
75
|
+
def centroid(image: ImageObj) -> GeometryResult | None:
|
|
76
|
+
"""Compute centroid
|
|
77
|
+
with :py:func:`sigima.tools.image.get_centroid_fourier`
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
image: input image
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Centroid coordinates
|
|
84
|
+
"""
|
|
85
|
+
return compute_geometry_from_obj("centroid", "marker", image, get_centroid_coords)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_enclosing_circle_coords(data: np.ndarray) -> np.ndarray:
|
|
89
|
+
"""Return diameter coords for the circle contour enclosing image
|
|
90
|
+
values above threshold (FWHM)
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
data: input data
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Diameter coords
|
|
97
|
+
"""
|
|
98
|
+
x, y, r = sigima.tools.image.get_enclosing_circle(data)
|
|
99
|
+
return np.array([[x, y, r]])
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@computation_function()
|
|
103
|
+
def enclosing_circle(image: ImageObj) -> GeometryResult | None:
|
|
104
|
+
"""Compute minimum enclosing circle
|
|
105
|
+
with :py:func:`sigima.tools.image.get_enclosing_circle`
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
image: input image
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Diameter coords
|
|
112
|
+
"""
|
|
113
|
+
return compute_geometry_from_obj(
|
|
114
|
+
"enclosing_circle", "circle", image, get_enclosing_circle_coords
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def __calc_snr_without_warning(data: np.ndarray) -> float:
|
|
119
|
+
"""Calculate SNR based on <z>/σ(z), ignoring warnings
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
data: input data
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Signal-to-noise ratio
|
|
126
|
+
"""
|
|
127
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
128
|
+
snr = ma.mean(data) / ma.std(data)
|
|
129
|
+
return snr
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@computation_function()
|
|
133
|
+
def stats(obj: ImageObj) -> TableResult:
|
|
134
|
+
"""Compute statistics on an image
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
obj: input image object
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Result properties
|
|
141
|
+
"""
|
|
142
|
+
builder = TableResultBuilder(_("Image statistics"), kind=TableKind.STATISTICS)
|
|
143
|
+
builder.add(ma.min, "min")
|
|
144
|
+
builder.add(ma.max, "max")
|
|
145
|
+
builder.add(ma.mean, "mean")
|
|
146
|
+
builder.add(ma.median, "median")
|
|
147
|
+
builder.add(ma.std, "std")
|
|
148
|
+
builder.add(__calc_snr_without_warning, "snr")
|
|
149
|
+
builder.add(ma.ptp, "ptp")
|
|
150
|
+
builder.add(ma.sum, "sum")
|
|
151
|
+
return builder.compute(obj)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@computation_function()
|
|
155
|
+
def horizontal_projection(image: ImageObj) -> SignalObj:
|
|
156
|
+
"""Compute the sum of pixel intensities along each col. (projection on the x-axis).
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
image: Input image object.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Signal object containing the profile.
|
|
163
|
+
"""
|
|
164
|
+
dst_signal = new_signal_result(
|
|
165
|
+
image,
|
|
166
|
+
"horizontal_projection",
|
|
167
|
+
units=(image.xunit, image.zunit),
|
|
168
|
+
labels=(image.xlabel, image.zlabel),
|
|
169
|
+
)
|
|
170
|
+
x = np.linspace(image.x0, image.x0 + image.width - image.dx, image.data.shape[1])
|
|
171
|
+
y = image.data.sum(axis=0, dtype=float)
|
|
172
|
+
dst_signal.set_xydata(x, y)
|
|
173
|
+
return dst_signal
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@computation_function()
|
|
177
|
+
def vertical_projection(image: ImageObj) -> SignalObj:
|
|
178
|
+
"""Compute the sum of pixel intensities along each row (projection on the y-axis).
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
image: Input image object.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Signal object containing the profile.
|
|
185
|
+
"""
|
|
186
|
+
dst_signal = new_signal_result(
|
|
187
|
+
image,
|
|
188
|
+
"vertical_projection",
|
|
189
|
+
units=(image.yunit, image.zunit),
|
|
190
|
+
labels=(image.ylabel, image.zlabel),
|
|
191
|
+
)
|
|
192
|
+
x = np.linspace(image.y0, image.y0 + image.height - image.dy, image.data.shape[0])
|
|
193
|
+
y = image.data.sum(axis=1, dtype=float)
|
|
194
|
+
dst_signal.set_xydata(x, y)
|
|
195
|
+
return dst_signal
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Morphology computation module
|
|
5
|
+
-----------------------------
|
|
6
|
+
|
|
7
|
+
This module provides morphological operations for image processing, such as
|
|
8
|
+
dilation, erosion, opening, and closing. These operations are useful for
|
|
9
|
+
removing noise, filling gaps, and extracting structures in binary and grayscale
|
|
10
|
+
images.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
14
|
+
|
|
15
|
+
# Note:
|
|
16
|
+
# ----
|
|
17
|
+
# - All `guidata.dataset.DataSet` parameter classes must also be imported
|
|
18
|
+
# in the `sigima.params` module.
|
|
19
|
+
# - All functions decorated by `computation_function` must be imported in the upper
|
|
20
|
+
# level `sigima.proc.image` module.
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import guidata.dataset as gds
|
|
25
|
+
from skimage import morphology
|
|
26
|
+
|
|
27
|
+
from sigima.config import _
|
|
28
|
+
from sigima.objects.image import ImageObj
|
|
29
|
+
from sigima.proc.base import dst_1_to_1
|
|
30
|
+
from sigima.proc.decorator import computation_function
|
|
31
|
+
from sigima.proc.image.base import restore_data_outside_roi
|
|
32
|
+
|
|
33
|
+
# NOTE: Only parameter classes DEFINED in this module should be included in __all__.
|
|
34
|
+
# Parameter classes imported from other modules (like sigima.proc.base) should NOT
|
|
35
|
+
# be re-exported to avoid Sphinx cross-reference conflicts. The sigima.params module
|
|
36
|
+
# serves as the central API point that imports and re-exports all parameter classes.
|
|
37
|
+
__all__ = [
|
|
38
|
+
"MorphologyParam",
|
|
39
|
+
"black_tophat",
|
|
40
|
+
"closing",
|
|
41
|
+
"dilation",
|
|
42
|
+
"erosion",
|
|
43
|
+
"opening",
|
|
44
|
+
"white_tophat",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MorphologyParam(gds.DataSet):
|
|
49
|
+
"""White Top-Hat parameters"""
|
|
50
|
+
|
|
51
|
+
radius = gds.IntItem(
|
|
52
|
+
_("Radius"), default=1, min=1, help=_("Footprint (disk) radius.")
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@computation_function()
|
|
57
|
+
def white_tophat(src: ImageObj, p: MorphologyParam) -> ImageObj:
|
|
58
|
+
"""Compute White Top-Hat with :py:func:`skimage.morphology.white_tophat`
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
src: input image object
|
|
62
|
+
p: parameters
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Output image object
|
|
66
|
+
"""
|
|
67
|
+
dst = dst_1_to_1(src, "white_tophat", f"radius={p.radius}")
|
|
68
|
+
dst.data = morphology.white_tophat(src.data, morphology.disk(p.radius))
|
|
69
|
+
restore_data_outside_roi(dst, src)
|
|
70
|
+
return dst
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@computation_function()
|
|
74
|
+
def black_tophat(src: ImageObj, p: MorphologyParam) -> ImageObj:
|
|
75
|
+
"""Compute Black Top-Hat with :py:func:`skimage.morphology.black_tophat`
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
src: input image object
|
|
79
|
+
p: parameters
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Output image object
|
|
83
|
+
"""
|
|
84
|
+
dst = dst_1_to_1(src, "black_tophat", f"radius={p.radius}")
|
|
85
|
+
dst.data = morphology.black_tophat(src.data, morphology.disk(p.radius))
|
|
86
|
+
restore_data_outside_roi(dst, src)
|
|
87
|
+
return dst
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@computation_function()
|
|
91
|
+
def erosion(src: ImageObj, p: MorphologyParam) -> ImageObj:
|
|
92
|
+
"""Compute Erosion with :py:func:`skimage.morphology.erosion`
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
src: input image object
|
|
96
|
+
p: parameters
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Output image object
|
|
100
|
+
"""
|
|
101
|
+
dst = dst_1_to_1(src, "erosion", f"radius={p.radius}")
|
|
102
|
+
dst.data = morphology.erosion(src.data, morphology.disk(p.radius))
|
|
103
|
+
restore_data_outside_roi(dst, src)
|
|
104
|
+
return dst
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@computation_function()
|
|
108
|
+
def dilation(src: ImageObj, p: MorphologyParam) -> ImageObj:
|
|
109
|
+
"""Compute Dilation with :py:func:`skimage.morphology.dilation`
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
src: input image object
|
|
113
|
+
p: parameters
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Output image object
|
|
117
|
+
"""
|
|
118
|
+
dst = dst_1_to_1(src, "dilation", f"radius={p.radius}")
|
|
119
|
+
dst.data = morphology.dilation(src.data, morphology.disk(p.radius))
|
|
120
|
+
restore_data_outside_roi(dst, src)
|
|
121
|
+
return dst
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@computation_function()
|
|
125
|
+
def opening(src: ImageObj, p: MorphologyParam) -> ImageObj:
|
|
126
|
+
"""Compute morphological opening with :py:func:`skimage.morphology.opening`
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
src: input image object
|
|
130
|
+
p: parameters
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Output image object
|
|
134
|
+
"""
|
|
135
|
+
dst = dst_1_to_1(src, "opening", f"radius={p.radius}")
|
|
136
|
+
dst.data = morphology.opening(src.data, morphology.disk(p.radius))
|
|
137
|
+
restore_data_outside_roi(dst, src)
|
|
138
|
+
return dst
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@computation_function()
|
|
142
|
+
def closing(src: ImageObj, p: MorphologyParam) -> ImageObj:
|
|
143
|
+
"""Compute morphological closing with :py:func:`skimage.morphology.closing`
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
src: input image object
|
|
147
|
+
p: parameters
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Output image object
|
|
151
|
+
"""
|
|
152
|
+
dst = dst_1_to_1(src, "closing", f"radius={p.radius}")
|
|
153
|
+
dst.data = morphology.closing(src.data, morphology.disk(p.radius))
|
|
154
|
+
restore_data_outside_roi(dst, src)
|
|
155
|
+
return dst
|