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,107 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Noise addition computation module.
|
|
5
|
+
----------------------------------
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y...
|
|
9
|
+
|
|
10
|
+
# Note:
|
|
11
|
+
# ----
|
|
12
|
+
# - All `guidata.dataset.DataSet` parameter classes must also be imported in the
|
|
13
|
+
# `sigima.params` module.
|
|
14
|
+
# - All functions decorated by `computation_function` must be imported in the upper
|
|
15
|
+
# level `sigima.proc.image` module.
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import guidata.dataset as gds
|
|
20
|
+
|
|
21
|
+
from sigima.objects.base import (
|
|
22
|
+
NormalDistributionParam,
|
|
23
|
+
PoissonDistributionParam,
|
|
24
|
+
UniformDistributionParam,
|
|
25
|
+
)
|
|
26
|
+
from sigima.objects.image import (
|
|
27
|
+
ImageObj,
|
|
28
|
+
NormalDistribution2DParam,
|
|
29
|
+
PoissonDistribution2DParam,
|
|
30
|
+
UniformDistribution2DParam,
|
|
31
|
+
create_image_from_param,
|
|
32
|
+
)
|
|
33
|
+
from sigima.proc.base import dst_1_to_1
|
|
34
|
+
from sigima.proc.decorator import computation_function
|
|
35
|
+
from sigima.proc.image.arithmetic import addition
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@computation_function()
|
|
39
|
+
def add_gaussian_noise(src: ImageObj, p: NormalDistributionParam) -> ImageObj:
|
|
40
|
+
"""Add Gaussian (normal) noise to the input image.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
src: Source image.
|
|
44
|
+
p: Parameters.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Result image object.
|
|
48
|
+
"""
|
|
49
|
+
param = NormalDistribution2DParam() # Do not confuse with NormalDistributionParam
|
|
50
|
+
gds.update_dataset(param, p)
|
|
51
|
+
assert src.data is not None
|
|
52
|
+
shape = src.data.shape
|
|
53
|
+
param.height = shape[0]
|
|
54
|
+
param.width = shape[1]
|
|
55
|
+
param.dtype = src.data.dtype
|
|
56
|
+
noise = create_image_from_param(param)
|
|
57
|
+
dst = dst_1_to_1(src, "add_gaussian_noise", f"mu={p.mu},sigma={p.sigma}")
|
|
58
|
+
dst.data = addition([dst, noise]).data
|
|
59
|
+
return dst
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@computation_function()
|
|
63
|
+
def add_poisson_noise(src: ImageObj, p: PoissonDistributionParam) -> ImageObj:
|
|
64
|
+
"""Add Poisson noise to the input image.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
src: Source image.
|
|
68
|
+
p: Parameters.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Result image object.
|
|
72
|
+
"""
|
|
73
|
+
param = PoissonDistribution2DParam() # Do not confuse with PoissonDistributionParam
|
|
74
|
+
gds.update_dataset(param, p)
|
|
75
|
+
assert src.data is not None
|
|
76
|
+
shape = src.data.shape
|
|
77
|
+
param.height = shape[0]
|
|
78
|
+
param.width = shape[1]
|
|
79
|
+
param.dtype = src.data.dtype
|
|
80
|
+
noise = create_image_from_param(param)
|
|
81
|
+
dst = dst_1_to_1(src, "add_poisson_noise", f"lam={p.lam}")
|
|
82
|
+
dst.data = addition([dst, noise]).data
|
|
83
|
+
return dst
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@computation_function()
|
|
87
|
+
def add_uniform_noise(src: ImageObj, p: UniformDistributionParam) -> ImageObj:
|
|
88
|
+
"""Add uniform noise to the input image.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
src: Source image.
|
|
92
|
+
p: Parameters.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Result image object.
|
|
96
|
+
"""
|
|
97
|
+
param = UniformDistribution2DParam() # Do not confuse with UniformDistributionParam
|
|
98
|
+
gds.update_dataset(param, p)
|
|
99
|
+
assert src.data is not None
|
|
100
|
+
shape = src.data.shape
|
|
101
|
+
param.height = shape[0]
|
|
102
|
+
param.width = shape[1]
|
|
103
|
+
param.dtype = src.data.dtype
|
|
104
|
+
noise = create_image_from_param(param)
|
|
105
|
+
dst = dst_1_to_1(src, "add_uniform_noise", f"low={p.vmin}, high={p.vmax}")
|
|
106
|
+
dst.data = addition([dst, noise]).data
|
|
107
|
+
return dst
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Image preprocessing functions.
|
|
4
|
+
|
|
5
|
+
This module consolidates preprocessing functions that were previously scattered
|
|
6
|
+
across different modules (exposure, geometry, fourier). All functions in this
|
|
7
|
+
module operate on high-level ImageObj objects and use parameter classes from
|
|
8
|
+
the sigima.proc framework.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import guidata.dataset as gds
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
import sigima.enums
|
|
17
|
+
import sigima.tools.image
|
|
18
|
+
from sigima.config import _
|
|
19
|
+
from sigima.enums import PadLocation2D
|
|
20
|
+
from sigima.objects.image import ImageObj
|
|
21
|
+
from sigima.proc.base import dst_1_to_1
|
|
22
|
+
from sigima.proc.decorator import computation_function
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"BinningParam",
|
|
26
|
+
"ZeroPadding2DParam",
|
|
27
|
+
"binning",
|
|
28
|
+
"zero_padding",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BinningParam(gds.DataSet):
|
|
33
|
+
"""Binning parameters."""
|
|
34
|
+
|
|
35
|
+
sx = gds.IntItem(
|
|
36
|
+
_("Cluster size (X)"),
|
|
37
|
+
default=2,
|
|
38
|
+
min=2,
|
|
39
|
+
help=_("Number of adjacent pixels to be combined together along X-axis."),
|
|
40
|
+
)
|
|
41
|
+
sy = gds.IntItem(
|
|
42
|
+
_("Cluster size (Y)"),
|
|
43
|
+
default=2,
|
|
44
|
+
min=2,
|
|
45
|
+
help=_("Number of adjacent pixels to be combined together along Y-axis."),
|
|
46
|
+
)
|
|
47
|
+
operation = gds.ChoiceItem(
|
|
48
|
+
_("Operation"),
|
|
49
|
+
sigima.enums.BinningOperation,
|
|
50
|
+
default=sigima.enums.BinningOperation.SUM,
|
|
51
|
+
)
|
|
52
|
+
dtypes = ["dtype"] + ImageObj.get_valid_dtypenames()
|
|
53
|
+
dtype_str = gds.ChoiceItem(
|
|
54
|
+
_("Data type"),
|
|
55
|
+
list(zip(dtypes, dtypes)),
|
|
56
|
+
help=_("Output image data type."),
|
|
57
|
+
)
|
|
58
|
+
change_pixel_size = gds.BoolItem(
|
|
59
|
+
_("Change pixel size"),
|
|
60
|
+
default=True,
|
|
61
|
+
help=_(
|
|
62
|
+
"If checked, pixel size is updated according to binning factors. "
|
|
63
|
+
"Users who prefer to work with pixel coordinates may want to uncheck this."
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@computation_function()
|
|
69
|
+
def binning(src: ImageObj, p: BinningParam) -> ImageObj:
|
|
70
|
+
"""Binning: image pixel binning (or aggregation).
|
|
71
|
+
|
|
72
|
+
Depending on the algorithm, the input image may be cropped to fit an integer
|
|
73
|
+
number of blocks.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
src: source image
|
|
77
|
+
p: parameters
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Output image
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ValueError: if source image has non-uniform coordinates
|
|
84
|
+
"""
|
|
85
|
+
if not src.is_uniform_coords:
|
|
86
|
+
raise ValueError("Binning only works with images having uniform coordinates")
|
|
87
|
+
# Create destination image
|
|
88
|
+
dst = dst_1_to_1(
|
|
89
|
+
src,
|
|
90
|
+
"binning",
|
|
91
|
+
f"{p.sx}x{p.sy},{p.operation},change_pixel_size={p.change_pixel_size}",
|
|
92
|
+
)
|
|
93
|
+
dst.data = sigima.tools.image.binning(
|
|
94
|
+
src.data,
|
|
95
|
+
sx=p.sx,
|
|
96
|
+
sy=p.sy,
|
|
97
|
+
operation=p.operation,
|
|
98
|
+
dtype=None if p.dtype_str == "dtype" else p.dtype_str,
|
|
99
|
+
)
|
|
100
|
+
if p.change_pixel_size:
|
|
101
|
+
if not np.isnan(src.dx) and not np.isnan(src.dy):
|
|
102
|
+
# Update coordinates with new pixel spacing
|
|
103
|
+
new_dx = src.dx * p.sx
|
|
104
|
+
new_dy = src.dy * p.sy
|
|
105
|
+
dst.set_uniform_coords(new_dx, new_dy, src.x0, src.y0)
|
|
106
|
+
return dst
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ZeroPadding2DParam(gds.DataSet):
|
|
110
|
+
"""Zero padding parameters for 2D images"""
|
|
111
|
+
|
|
112
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
113
|
+
super().__init__(*args, **kwargs)
|
|
114
|
+
self.__obj: ImageObj | None = None
|
|
115
|
+
|
|
116
|
+
def update_from_obj(self, obj: ImageObj) -> None:
|
|
117
|
+
"""Update parameters from image"""
|
|
118
|
+
self.__obj = obj
|
|
119
|
+
self.choice_callback(None, self.strategy)
|
|
120
|
+
|
|
121
|
+
def choice_callback(self, item, value): # pylint: disable=unused-argument
|
|
122
|
+
"""Callback to update padding values"""
|
|
123
|
+
if self.__obj is None:
|
|
124
|
+
return
|
|
125
|
+
rows, cols = self.__obj.data.shape
|
|
126
|
+
if value == "next_pow2":
|
|
127
|
+
self.rows = 2 ** int(np.ceil(np.log2(rows))) - rows
|
|
128
|
+
self.cols = 2 ** int(np.ceil(np.log2(cols))) - cols
|
|
129
|
+
elif value == "multiple_of_64":
|
|
130
|
+
self.rows = (64 - rows % 64) if rows % 64 != 0 else 0
|
|
131
|
+
self.cols = (64 - cols % 64) if cols % 64 != 0 else 0
|
|
132
|
+
|
|
133
|
+
strategies = ("next_pow2", "multiple_of_64", "custom")
|
|
134
|
+
_prop = gds.GetAttrProp("strategy")
|
|
135
|
+
strategy = gds.ChoiceItem(
|
|
136
|
+
_("Padding strategy"), zip(strategies, strategies), default=strategies[-1]
|
|
137
|
+
).set_prop("display", store=_prop, callback=choice_callback)
|
|
138
|
+
|
|
139
|
+
_func_prop = gds.FuncProp(_prop, lambda x: x == "custom")
|
|
140
|
+
rows = gds.IntItem(_("Rows to add"), min=0, default=0).set_prop(
|
|
141
|
+
"display", active=_func_prop
|
|
142
|
+
)
|
|
143
|
+
cols = gds.IntItem(_("Columns to add"), min=0, default=0).set_prop(
|
|
144
|
+
"display", active=_func_prop
|
|
145
|
+
)
|
|
146
|
+
position = gds.ChoiceItem(
|
|
147
|
+
_("Padding position"), PadLocation2D, default=PadLocation2D.BOTTOM_RIGHT
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@computation_function()
|
|
152
|
+
def zero_padding(
|
|
153
|
+
src: ImageObj,
|
|
154
|
+
p: ZeroPadding2DParam | None = None,
|
|
155
|
+
) -> ImageObj:
|
|
156
|
+
"""Zero-padding: add zeros to image borders.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
src: input image object
|
|
160
|
+
p: parameters
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Output image object
|
|
164
|
+
"""
|
|
165
|
+
if p is None:
|
|
166
|
+
p = ZeroPadding2DParam.create()
|
|
167
|
+
|
|
168
|
+
if p.strategy == "custom":
|
|
169
|
+
suffix = f"rows={p.rows}, cols={p.cols}"
|
|
170
|
+
else:
|
|
171
|
+
suffix = f"strategy={p.strategy}"
|
|
172
|
+
suffix += f", position={p.position}"
|
|
173
|
+
|
|
174
|
+
dst = dst_1_to_1(src, "zero_padding", suffix)
|
|
175
|
+
result = sigima.tools.image.zero_padding(
|
|
176
|
+
src.data,
|
|
177
|
+
rows=p.rows,
|
|
178
|
+
cols=p.cols,
|
|
179
|
+
position=p.position,
|
|
180
|
+
)
|
|
181
|
+
dst.data = result
|
|
182
|
+
return dst
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Restoration computation module
|
|
5
|
+
------------------------------
|
|
6
|
+
|
|
7
|
+
This module provides image restoration techniques, such as
|
|
8
|
+
denoising, inpainting, and deblurring. These methods aim to recover
|
|
9
|
+
the original quality of images by removing artifacts, noise, or
|
|
10
|
+
distortions.
|
|
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
|
+
from typing import TYPE_CHECKING
|
|
25
|
+
|
|
26
|
+
import guidata.dataset as gds
|
|
27
|
+
import numpy as np
|
|
28
|
+
import pywt
|
|
29
|
+
from skimage import morphology, restoration
|
|
30
|
+
|
|
31
|
+
from sigima.config import _
|
|
32
|
+
from sigima.enums import ShrinkageMethod, ThresholdMethod, WaveletMode
|
|
33
|
+
from sigima.objects.image import ImageObj, ROI2DParam
|
|
34
|
+
from sigima.proc.base import dst_1_to_1
|
|
35
|
+
from sigima.proc.decorator import computation_function
|
|
36
|
+
from sigima.proc.image.base import Wrap1to1Func, restore_data_outside_roi
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
import sigima.params
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# NOTE: Only parameter classes DEFINED in this module should be included in __all__.
|
|
43
|
+
# Parameter classes imported from other modules (like sigima.proc.base) should NOT
|
|
44
|
+
# be re-exported to avoid Sphinx cross-reference conflicts. The sigima.params module
|
|
45
|
+
# serves as the central API point that imports and re-exports all parameter classes.
|
|
46
|
+
__all__ = [
|
|
47
|
+
"DenoiseBilateralParam",
|
|
48
|
+
"DenoiseTVParam",
|
|
49
|
+
"DenoiseWaveletParam",
|
|
50
|
+
"denoise_bilateral",
|
|
51
|
+
"denoise_tophat",
|
|
52
|
+
"denoise_tv",
|
|
53
|
+
"denoise_wavelet",
|
|
54
|
+
"erase",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DenoiseTVParam(gds.DataSet):
|
|
59
|
+
"""Total Variation denoising parameters"""
|
|
60
|
+
|
|
61
|
+
weight = gds.FloatItem(
|
|
62
|
+
_("Denoising weight"),
|
|
63
|
+
default=0.1,
|
|
64
|
+
min=0,
|
|
65
|
+
nonzero=True,
|
|
66
|
+
help=_(
|
|
67
|
+
"The greater weight, the more denoising "
|
|
68
|
+
"(at the expense of fidelity to input)."
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
eps = gds.FloatItem(
|
|
72
|
+
"Epsilon",
|
|
73
|
+
default=0.0002,
|
|
74
|
+
min=0,
|
|
75
|
+
nonzero=True,
|
|
76
|
+
help=_(
|
|
77
|
+
"Relative difference of the value of the cost function that "
|
|
78
|
+
"determines the stop criterion. The algorithm stops when: "
|
|
79
|
+
"(E_(n-1) - E_n) < eps * E_0"
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
max_num_iter = gds.IntItem(
|
|
83
|
+
_("Max. iterations"),
|
|
84
|
+
default=200,
|
|
85
|
+
min=0,
|
|
86
|
+
nonzero=True,
|
|
87
|
+
help=_("Maximal number of iterations used for the optimization"),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@computation_function()
|
|
92
|
+
def denoise_tv(src: ImageObj, p: DenoiseTVParam) -> ImageObj:
|
|
93
|
+
"""Compute Total Variation denoising
|
|
94
|
+
with :py:func:`skimage.restoration.denoise_tv_chambolle`
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
src: input image object
|
|
98
|
+
p: parameters
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Output image object
|
|
102
|
+
"""
|
|
103
|
+
return Wrap1to1Func(
|
|
104
|
+
restoration.denoise_tv_chambolle,
|
|
105
|
+
weight=p.weight,
|
|
106
|
+
eps=p.eps,
|
|
107
|
+
max_num_iter=p.max_num_iter,
|
|
108
|
+
func_name="denoise_tv",
|
|
109
|
+
)(src)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class DenoiseBilateralParam(gds.DataSet):
|
|
113
|
+
"""Bilateral filter denoising parameters"""
|
|
114
|
+
|
|
115
|
+
sigma_spatial = gds.FloatItem(
|
|
116
|
+
"σ<sub>spatial</sub>",
|
|
117
|
+
default=1.0,
|
|
118
|
+
min=0,
|
|
119
|
+
nonzero=True,
|
|
120
|
+
unit="pixels",
|
|
121
|
+
help=_(
|
|
122
|
+
"Standard deviation for range distance. "
|
|
123
|
+
"A larger value results in averaging of pixels "
|
|
124
|
+
"with larger spatial differences."
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
mode = gds.ChoiceItem(_("Mode"), WaveletMode, default=WaveletMode.CONSTANT)
|
|
128
|
+
cval = gds.FloatItem(
|
|
129
|
+
"cval",
|
|
130
|
+
default=0.0,
|
|
131
|
+
help=_(
|
|
132
|
+
"Used in conjunction with mode 'constant', "
|
|
133
|
+
"the value outside the image boundaries."
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@computation_function()
|
|
139
|
+
def denoise_bilateral(src: ImageObj, p: DenoiseBilateralParam) -> ImageObj:
|
|
140
|
+
"""Compute bilateral filter denoising
|
|
141
|
+
with :py:func:`skimage.restoration.denoise_bilateral`
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
src: input image object
|
|
145
|
+
p: parameters
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Output image object
|
|
149
|
+
"""
|
|
150
|
+
return Wrap1to1Func(
|
|
151
|
+
restoration.denoise_bilateral,
|
|
152
|
+
sigma_spatial=p.sigma_spatial,
|
|
153
|
+
mode=p.mode,
|
|
154
|
+
cval=p.cval,
|
|
155
|
+
)(src)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class DenoiseWaveletParam(gds.DataSet):
|
|
159
|
+
"""Wavelet denoising parameters"""
|
|
160
|
+
|
|
161
|
+
wavelets = pywt.wavelist()
|
|
162
|
+
wavelet = gds.ChoiceItem(
|
|
163
|
+
_("Wavelet"), list(zip(wavelets, wavelets)), default="sym9"
|
|
164
|
+
)
|
|
165
|
+
mode = gds.ChoiceItem(_("Mode"), ThresholdMethod, default=ThresholdMethod.SOFT)
|
|
166
|
+
method = gds.ChoiceItem(
|
|
167
|
+
_("Method"), ShrinkageMethod, default=ShrinkageMethod.VISU_SHRINK
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@computation_function()
|
|
172
|
+
def denoise_wavelet(src: ImageObj, p: DenoiseWaveletParam) -> ImageObj:
|
|
173
|
+
"""Compute Wavelet denoising
|
|
174
|
+
with :py:func:`skimage.restoration.denoise_wavelet`
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
src: input image object
|
|
178
|
+
p: parameters
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Output image object
|
|
182
|
+
"""
|
|
183
|
+
return Wrap1to1Func(
|
|
184
|
+
restoration.denoise_wavelet, wavelet=p.wavelet, mode=p.mode, method=p.method
|
|
185
|
+
)(src)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@computation_function()
|
|
189
|
+
def denoise_tophat(src: ImageObj, p: sigima.params.MorphologyParam) -> ImageObj:
|
|
190
|
+
"""Denoise using White Top-Hat
|
|
191
|
+
with :py:func:`skimage.morphology.white_tophat`
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
src: input image object
|
|
195
|
+
p: parameters
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Output image object
|
|
199
|
+
"""
|
|
200
|
+
dst = dst_1_to_1(src, "denoise_tophat", f"radius={p.radius}")
|
|
201
|
+
dst.data = src.data - morphology.white_tophat(src.data, morphology.disk(p.radius))
|
|
202
|
+
restore_data_outside_roi(dst, src)
|
|
203
|
+
return dst
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@computation_function()
|
|
207
|
+
def erase(src: ImageObj, p: ROI2DParam | list[ROI2DParam]) -> ImageObj:
|
|
208
|
+
"""Erase an area of the image using the mean value of the image.
|
|
209
|
+
|
|
210
|
+
.. note::
|
|
211
|
+
|
|
212
|
+
The erased area is defined by a region of interest (ROI) parameter set.
|
|
213
|
+
This ROI must not be mistaken with the ROI of the image object. If the
|
|
214
|
+
image object has a ROI, it is not used in this processing, except to
|
|
215
|
+
restore the data outside the ROI (as in all other processing).
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
src: input image object
|
|
219
|
+
p: parameters defining the area to erase (region of interest)
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Output image object
|
|
223
|
+
"""
|
|
224
|
+
params = [p] if isinstance(p, ROI2DParam) else p
|
|
225
|
+
suffix = None
|
|
226
|
+
if len(params) == 1:
|
|
227
|
+
suffix = params[0].get_suffix()
|
|
228
|
+
dst = dst_1_to_1(src, "erase", suffix)
|
|
229
|
+
for param in params:
|
|
230
|
+
value = np.nanmean(param.get_data(src))
|
|
231
|
+
erase_roi = param.to_single_roi(src)
|
|
232
|
+
mask = erase_roi.to_mask(src)
|
|
233
|
+
dst.data[~mask] = value
|
|
234
|
+
restore_data_outside_roi(dst, src)
|
|
235
|
+
return dst
|