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,551 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Arithmetic operations on signals
|
|
5
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import warnings
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from sigima.enums import MathOperator, SignalsToImageOrientation
|
|
16
|
+
from sigima.objects import SignalObj, create_image
|
|
17
|
+
from sigima.proc.base import (
|
|
18
|
+
ArithmeticParam,
|
|
19
|
+
ConstantParam,
|
|
20
|
+
SignalsToImageParam,
|
|
21
|
+
dst_1_to_1,
|
|
22
|
+
dst_2_to_1,
|
|
23
|
+
dst_n_to_1,
|
|
24
|
+
)
|
|
25
|
+
from sigima.proc.decorator import computation_function
|
|
26
|
+
from sigima.proc.signal.base import (
|
|
27
|
+
is_uncertainty_data_available,
|
|
28
|
+
restore_data_outside_roi,
|
|
29
|
+
signals_dy_to_array,
|
|
30
|
+
signals_y_to_array,
|
|
31
|
+
)
|
|
32
|
+
from sigima.proc.signal.mathops import inverse
|
|
33
|
+
from sigima.tools.signal import scaling
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from sigima.objects import ImageObj
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@computation_function()
|
|
40
|
+
def addition(src_list: list[SignalObj]) -> SignalObj:
|
|
41
|
+
"""Compute the element-wise sum of multiple signals.
|
|
42
|
+
|
|
43
|
+
The first signal in the list defines the "base" signal. All other signals are added
|
|
44
|
+
element-wise to the base signal.
|
|
45
|
+
|
|
46
|
+
.. note::
|
|
47
|
+
|
|
48
|
+
If all signals share the same region of interest (ROI), the sum is performed
|
|
49
|
+
only within the ROI.
|
|
50
|
+
|
|
51
|
+
.. note::
|
|
52
|
+
|
|
53
|
+
Uncertainties are propagated.
|
|
54
|
+
|
|
55
|
+
.. warning::
|
|
56
|
+
|
|
57
|
+
It is assumed that all signals have the same size and x-coordinates.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
src_list: List of source signals.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Signal object representing the sum of the source signals.
|
|
64
|
+
"""
|
|
65
|
+
dst = dst_n_to_1(src_list, "Σ") # `dst` data is initialized to `src_list[0]` data.
|
|
66
|
+
dst.y = np.sum(signals_y_to_array(src_list), axis=0)
|
|
67
|
+
if is_uncertainty_data_available(src_list):
|
|
68
|
+
dst.dy = np.sqrt(np.sum(signals_dy_to_array(src_list) ** 2, axis=0))
|
|
69
|
+
restore_data_outside_roi(dst, src_list[0])
|
|
70
|
+
return dst
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@computation_function()
|
|
74
|
+
def average(src_list: list[SignalObj]) -> SignalObj:
|
|
75
|
+
"""Compute the element-wise average of multiple signals.
|
|
76
|
+
|
|
77
|
+
The first signal in the list defines the "base" signal. All other signals are
|
|
78
|
+
averaged element-wise with the base signal.
|
|
79
|
+
|
|
80
|
+
.. note::
|
|
81
|
+
|
|
82
|
+
If all signals share the same region of interest (ROI), the average is performed
|
|
83
|
+
only within the ROI.
|
|
84
|
+
|
|
85
|
+
.. note::
|
|
86
|
+
|
|
87
|
+
Uncertainties are propagated.
|
|
88
|
+
|
|
89
|
+
.. warning::
|
|
90
|
+
|
|
91
|
+
It is assumed that all signals have the same size and x-coordinates.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
src_list: List of source signals.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Signal object representing the average of the source signals.
|
|
98
|
+
"""
|
|
99
|
+
dst = dst_n_to_1(src_list, "µ") # `dst` data is initialized to `src_list[0]` data.
|
|
100
|
+
dst.y = np.mean(signals_y_to_array(src_list), axis=0)
|
|
101
|
+
if is_uncertainty_data_available(src_list):
|
|
102
|
+
dy_array = signals_dy_to_array(src_list)
|
|
103
|
+
dst.dy = np.sqrt(np.sum(dy_array**2, axis=0)) / len(src_list)
|
|
104
|
+
restore_data_outside_roi(dst, src_list[0])
|
|
105
|
+
return dst
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@computation_function()
|
|
109
|
+
def standard_deviation(src_list: list[SignalObj]) -> SignalObj:
|
|
110
|
+
"""Compute the element-wise standard deviation of multiple signals.
|
|
111
|
+
|
|
112
|
+
The first signal in the list defines the "base" signal. All other signals are
|
|
113
|
+
used to compute the element-wise standard deviation with the base signal.
|
|
114
|
+
|
|
115
|
+
.. note::
|
|
116
|
+
|
|
117
|
+
If all signals share the same region of interest (ROI), the standard deviation
|
|
118
|
+
is computed only within the ROI.
|
|
119
|
+
|
|
120
|
+
.. warning::
|
|
121
|
+
|
|
122
|
+
It is assumed that all signals have the same size and x-coordinates.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
src_list: List of source signals.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Signal object representing the standard deviation of the source signals.
|
|
129
|
+
"""
|
|
130
|
+
dst = dst_n_to_1(src_list, "𝜎") # `dst` data is initialized to `src_list[0]` data
|
|
131
|
+
dst.y = np.std(signals_y_to_array(src_list), axis=0, ddof=0)
|
|
132
|
+
if is_uncertainty_data_available(src_list):
|
|
133
|
+
dst.dy = dst.y / np.sqrt(2 * (len(src_list) - 1))
|
|
134
|
+
restore_data_outside_roi(dst, src_list[0])
|
|
135
|
+
return dst
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@computation_function()
|
|
139
|
+
def product(src_list: list[SignalObj]) -> SignalObj:
|
|
140
|
+
"""Compute the element-wise product of multiple signals.
|
|
141
|
+
|
|
142
|
+
The first signal in the list defines the "base" signal. All other signals are
|
|
143
|
+
multiplied element-wise with the base signal.
|
|
144
|
+
|
|
145
|
+
.. note::
|
|
146
|
+
|
|
147
|
+
If all signals share the same region of interest (ROI), the product is performed
|
|
148
|
+
only within the ROI.
|
|
149
|
+
|
|
150
|
+
.. note::
|
|
151
|
+
|
|
152
|
+
Uncertainties are propagated.
|
|
153
|
+
|
|
154
|
+
.. warning::
|
|
155
|
+
|
|
156
|
+
It is assumed that all signals have the same size and x-coordinates.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
src_list: List of source signals.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Signal object representing the product of the source signals.
|
|
163
|
+
"""
|
|
164
|
+
dst = dst_n_to_1(src_list, "Π") # `dst` data is initialized to `src_list[0]` data.
|
|
165
|
+
y_array = signals_y_to_array(src_list)
|
|
166
|
+
dst.y = np.prod(y_array, axis=0)
|
|
167
|
+
if is_uncertainty_data_available(src_list):
|
|
168
|
+
dy_array = signals_dy_to_array(src_list)
|
|
169
|
+
with warnings.catch_warnings():
|
|
170
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
171
|
+
uncertainty = np.abs(dst.y) * np.sqrt(
|
|
172
|
+
np.sum((dy_array / y_array) ** 2, axis=0)
|
|
173
|
+
)
|
|
174
|
+
uncertainty[np.isinf(uncertainty)] = np.nan
|
|
175
|
+
dst.dy = uncertainty
|
|
176
|
+
restore_data_outside_roi(dst, src_list[0])
|
|
177
|
+
return dst
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@computation_function()
|
|
181
|
+
def addition_constant(src: SignalObj, p: ConstantParam) -> SignalObj:
|
|
182
|
+
"""Compute the sum of a signal and a constant value.
|
|
183
|
+
|
|
184
|
+
The function adds a constant value to each element of the input signal.
|
|
185
|
+
|
|
186
|
+
.. note::
|
|
187
|
+
|
|
188
|
+
If the signal has a region of interest (ROI), the addition is performed
|
|
189
|
+
only within the ROI.
|
|
190
|
+
|
|
191
|
+
.. note::
|
|
192
|
+
|
|
193
|
+
Uncertainties are propagated.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
src: Input signal object.
|
|
197
|
+
p: Constant value.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Result signal object representing the sum of the input signal and the constant.
|
|
201
|
+
"""
|
|
202
|
+
# Uncertainty propagation: dst_1_to_1() copies all data including uncertainties.
|
|
203
|
+
# For addition with constant: σ(y + c) = σ(y), so no modification needed.
|
|
204
|
+
dst = dst_1_to_1(src, "+", str(p.value))
|
|
205
|
+
dst.y += p.value
|
|
206
|
+
restore_data_outside_roi(dst, src)
|
|
207
|
+
return dst
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@computation_function()
|
|
211
|
+
def difference_constant(src: SignalObj, p: ConstantParam) -> SignalObj:
|
|
212
|
+
"""Compute the difference between a signal and a constant value.
|
|
213
|
+
|
|
214
|
+
The function subtracts a constant value from each element of the input signal.
|
|
215
|
+
|
|
216
|
+
.. note::
|
|
217
|
+
|
|
218
|
+
If the signal has a region of interest (ROI), the subtraction is performed
|
|
219
|
+
only within the ROI.
|
|
220
|
+
|
|
221
|
+
.. note::
|
|
222
|
+
|
|
223
|
+
Uncertainties are propagated.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
src: Input signal object.
|
|
227
|
+
p: Constant value.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Result signal object representing the difference between the input signal and
|
|
231
|
+
the constant.
|
|
232
|
+
"""
|
|
233
|
+
# Uncertainty propagation: dst_1_to_1() copies all data including uncertainties.
|
|
234
|
+
# For subtraction with constant: σ(y - c) = σ(y), so no modification needed.
|
|
235
|
+
dst = dst_1_to_1(src, "-", str(p.value))
|
|
236
|
+
dst.y -= p.value
|
|
237
|
+
restore_data_outside_roi(dst, src)
|
|
238
|
+
return dst
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@computation_function()
|
|
242
|
+
def product_constant(src: SignalObj, p: ConstantParam) -> SignalObj:
|
|
243
|
+
"""Compute the product of a signal and a constant value.
|
|
244
|
+
|
|
245
|
+
The function multiplies each element of the input signal by a constant value.
|
|
246
|
+
|
|
247
|
+
.. note::
|
|
248
|
+
|
|
249
|
+
If the signal has a region of interest (ROI), the multiplication is performed
|
|
250
|
+
only within the ROI.
|
|
251
|
+
|
|
252
|
+
.. note::
|
|
253
|
+
|
|
254
|
+
Uncertainties are propagated.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
src: Input signal object.
|
|
258
|
+
p: Constant value.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Result signal object representing the product of the input signal and the
|
|
262
|
+
constant.
|
|
263
|
+
"""
|
|
264
|
+
assert p.value is not None
|
|
265
|
+
# Uncertainty propagation: dst_1_to_1() copies all data including uncertainties.
|
|
266
|
+
# For multiplication with constant: σ(c*y) = |c| * σ(y), so modification needed.
|
|
267
|
+
dst = dst_1_to_1(src, "×", str(p.value))
|
|
268
|
+
dst.y *= p.value
|
|
269
|
+
if is_uncertainty_data_available(src):
|
|
270
|
+
dst.dy *= np.abs(p.value) # Modify in-place since dy already copied from src
|
|
271
|
+
restore_data_outside_roi(dst, src)
|
|
272
|
+
return dst
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@computation_function()
|
|
276
|
+
def division_constant(src: SignalObj, p: ConstantParam) -> SignalObj:
|
|
277
|
+
"""Compute the division of a signal by a constant value.
|
|
278
|
+
|
|
279
|
+
The function divides each element of the input signal by a constant value.
|
|
280
|
+
|
|
281
|
+
.. note::
|
|
282
|
+
|
|
283
|
+
If the signal has a region of interest (ROI), the division is performed
|
|
284
|
+
only within the ROI.
|
|
285
|
+
|
|
286
|
+
.. note::
|
|
287
|
+
|
|
288
|
+
Uncertainties are propagated.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
src: Input signal object.
|
|
292
|
+
p: Constant value.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Result signal object representing the division of the input signal by the
|
|
296
|
+
constant.
|
|
297
|
+
"""
|
|
298
|
+
assert p.value is not None
|
|
299
|
+
# Uncertainty propagation: dst_1_to_1() copies all data including uncertainties.
|
|
300
|
+
# For division with constant: σ(y/c) = σ(y) / |c|, so modification needed.
|
|
301
|
+
dst = dst_1_to_1(src, "/", str(p.value))
|
|
302
|
+
with warnings.catch_warnings():
|
|
303
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
304
|
+
dst.y /= p.value
|
|
305
|
+
dst.y[np.isinf(dst.y)] = np.nan
|
|
306
|
+
if is_uncertainty_data_available(src):
|
|
307
|
+
dst.dy /= np.abs(p.value) # Modify in-place since dy already copied
|
|
308
|
+
dst.dy[np.isinf(dst.dy)] = np.nan
|
|
309
|
+
restore_data_outside_roi(dst, src)
|
|
310
|
+
return dst
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@computation_function()
|
|
314
|
+
def arithmetic(src1: SignalObj, src2: SignalObj, p: ArithmeticParam) -> SignalObj:
|
|
315
|
+
"""Perform an arithmetic operation on two signals.
|
|
316
|
+
|
|
317
|
+
The function applies the specified arithmetic operation to each element of the input
|
|
318
|
+
signals.
|
|
319
|
+
|
|
320
|
+
.. note::
|
|
321
|
+
|
|
322
|
+
The operation is performed only within the region of interest of `src1`.
|
|
323
|
+
|
|
324
|
+
.. note::
|
|
325
|
+
|
|
326
|
+
Uncertainties are propagated.
|
|
327
|
+
|
|
328
|
+
.. warning::
|
|
329
|
+
|
|
330
|
+
It is assumed that both signals have the same size and x-coordinates.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
src1: First input signal.
|
|
334
|
+
src2: Second input signal.
|
|
335
|
+
p: Arithmetic operation parameters.
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
Result signal object representing the arithmetic operation on the input signals.
|
|
339
|
+
"""
|
|
340
|
+
initial_dtype = src1.xydata.dtype
|
|
341
|
+
title = p.operation.replace("obj1", "{0}").replace("obj2", "{1}")
|
|
342
|
+
dst = src1.copy(title=title)
|
|
343
|
+
a = ConstantParam.create(value=p.factor)
|
|
344
|
+
b = ConstantParam.create(value=p.constant)
|
|
345
|
+
if p.operator == MathOperator.ADD:
|
|
346
|
+
dst = addition_constant(product_constant(addition([src1, src2]), a), b)
|
|
347
|
+
elif p.operator == MathOperator.SUBTRACT:
|
|
348
|
+
dst = addition_constant(product_constant(difference(src1, src2), a), b)
|
|
349
|
+
elif p.operator == MathOperator.MULTIPLY:
|
|
350
|
+
dst = addition_constant(product_constant(product([src1, src2]), a), b)
|
|
351
|
+
elif p.operator == MathOperator.DIVIDE:
|
|
352
|
+
dst = addition_constant(product_constant(product([src1, inverse(src2)]), a), b)
|
|
353
|
+
# Eventually convert to initial data type
|
|
354
|
+
if p.restore_dtype:
|
|
355
|
+
dst.xydata = dst.xydata.astype(initial_dtype)
|
|
356
|
+
restore_data_outside_roi(dst, src1)
|
|
357
|
+
return dst
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
@computation_function()
|
|
361
|
+
def difference(src1: SignalObj, src2: SignalObj) -> SignalObj:
|
|
362
|
+
"""Compute the element-wise difference between two signals.
|
|
363
|
+
|
|
364
|
+
The function subtracts each element of the second signal from the corresponding
|
|
365
|
+
element of the first signal.
|
|
366
|
+
|
|
367
|
+
.. note::
|
|
368
|
+
|
|
369
|
+
If both signals share the same region of interest (ROI), the difference is
|
|
370
|
+
performed only within the ROI.
|
|
371
|
+
|
|
372
|
+
.. note::
|
|
373
|
+
|
|
374
|
+
Uncertainties are propagated.
|
|
375
|
+
|
|
376
|
+
.. warning::
|
|
377
|
+
|
|
378
|
+
It is assumed that both signals have the same size and x-coordinates.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
src1: First input signal.
|
|
382
|
+
src2: Second input signal.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
Result signal object representing the difference between the input signals.
|
|
386
|
+
"""
|
|
387
|
+
dst = dst_2_to_1(src1, src2, "-")
|
|
388
|
+
dst.y = src1.y - src2.y
|
|
389
|
+
if is_uncertainty_data_available([src1, src2]):
|
|
390
|
+
dy_array = signals_dy_to_array([src1, src2])
|
|
391
|
+
dst.dy = np.sqrt(np.sum(dy_array**2, axis=0))
|
|
392
|
+
restore_data_outside_roi(dst, src1)
|
|
393
|
+
return dst
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
@computation_function()
|
|
397
|
+
def quadratic_difference(src1: SignalObj, src2: SignalObj) -> SignalObj:
|
|
398
|
+
"""Compute the normalized difference between two signals.
|
|
399
|
+
|
|
400
|
+
The function computes the element-wise difference between the two signals and
|
|
401
|
+
divides the result by sqrt(2.0).
|
|
402
|
+
|
|
403
|
+
.. note::
|
|
404
|
+
|
|
405
|
+
If both signals share the same region of interest (ROI), the operation is
|
|
406
|
+
performed only within the ROI.
|
|
407
|
+
|
|
408
|
+
.. note::
|
|
409
|
+
|
|
410
|
+
Uncertainties are propagated. For two input signals with identical standard
|
|
411
|
+
deviations, the standard deviation of the output signal equals the standard
|
|
412
|
+
deviation of each of the input signals.
|
|
413
|
+
|
|
414
|
+
.. warning::
|
|
415
|
+
|
|
416
|
+
It is assumed that both signals have the same size and x-coordinates.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
src1: First input signal.
|
|
420
|
+
src2: Second input signal.
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
Result signal object representing the quadratic difference between the input
|
|
424
|
+
signals.
|
|
425
|
+
"""
|
|
426
|
+
norm = ConstantParam.create(value=1.0 / np.sqrt(2.0))
|
|
427
|
+
return product_constant(difference(src1, src2), norm)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
@computation_function()
|
|
431
|
+
def division(src1: SignalObj, src2: SignalObj) -> SignalObj:
|
|
432
|
+
"""Compute the element-wise division between two signals.
|
|
433
|
+
|
|
434
|
+
The function divides each element of the first signal by the corresponding element
|
|
435
|
+
of the second signal.
|
|
436
|
+
|
|
437
|
+
.. note::
|
|
438
|
+
|
|
439
|
+
If both signals share the same region of interest (ROI), the division is
|
|
440
|
+
performed only within the ROI.
|
|
441
|
+
|
|
442
|
+
.. note::
|
|
443
|
+
|
|
444
|
+
Uncertainties are propagated.
|
|
445
|
+
|
|
446
|
+
.. warning::
|
|
447
|
+
|
|
448
|
+
It is assumed that both signals have the same size and x-coordinates.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
src1: First input signal.
|
|
452
|
+
src2: Second input signal.
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Result signal object representing the division of the input signals.
|
|
456
|
+
"""
|
|
457
|
+
dst = product([src1, inverse(src2)])
|
|
458
|
+
return dst
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def signals_to_array(
|
|
462
|
+
signals: list[SignalObj], attr: str = "y", dtype: np.dtype | None = None
|
|
463
|
+
) -> np.ndarray:
|
|
464
|
+
"""Create an array from a list of signals.
|
|
465
|
+
|
|
466
|
+
Args:
|
|
467
|
+
signals: List of signal objects.
|
|
468
|
+
attr: Name of the attribute to extract ("y", "dy", etc.). Defaults to "y".
|
|
469
|
+
dtype: Desired type for the output array. If None, use the first signal's dtype.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
A NumPy array stacking the specified attribute from all signals.
|
|
473
|
+
|
|
474
|
+
Raises:
|
|
475
|
+
ValueError: If the signals list is empty.
|
|
476
|
+
"""
|
|
477
|
+
if not signals:
|
|
478
|
+
raise ValueError("The signal list is empty.")
|
|
479
|
+
if dtype is None:
|
|
480
|
+
dtype = getattr(signals[0], attr).dtype
|
|
481
|
+
arr = np.array([getattr(sig, attr) for sig in signals], dtype=dtype)
|
|
482
|
+
return arr
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@computation_function()
|
|
486
|
+
def signals_to_image(src_list: list[SignalObj], p: SignalsToImageParam) -> ImageObj:
|
|
487
|
+
"""Combine multiple signals into an image.
|
|
488
|
+
|
|
489
|
+
The function takes a list of signals and combines them into a 2D image,
|
|
490
|
+
arranging them either as rows or columns based on the specified orientation.
|
|
491
|
+
Optionally, each signal can be normalized before combining.
|
|
492
|
+
|
|
493
|
+
.. note::
|
|
494
|
+
|
|
495
|
+
All signals must have the same size (number of data points).
|
|
496
|
+
|
|
497
|
+
.. note::
|
|
498
|
+
|
|
499
|
+
If normalization is enabled, each signal is normalized independently
|
|
500
|
+
using the specified normalization method before being added to the image.
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
src_list: List of source signals to combine.
|
|
504
|
+
p: Parameters specifying orientation and normalization options.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
Image object representing the combined signals.
|
|
508
|
+
|
|
509
|
+
Raises:
|
|
510
|
+
ValueError: If the signal list is empty or signals have different sizes.
|
|
511
|
+
"""
|
|
512
|
+
if not src_list:
|
|
513
|
+
raise ValueError("The signal list is empty.")
|
|
514
|
+
|
|
515
|
+
# Check that all signals have the same size
|
|
516
|
+
sizes = [len(sig.y) for sig in src_list]
|
|
517
|
+
if len(set(sizes)) > 1:
|
|
518
|
+
raise ValueError(
|
|
519
|
+
f"All signals must have the same size. Found sizes: {set(sizes)}"
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
# Prepare data array
|
|
523
|
+
y_array = signals_y_to_array(src_list)
|
|
524
|
+
|
|
525
|
+
# Normalize if requested
|
|
526
|
+
if p.normalize:
|
|
527
|
+
for i in range(len(src_list)):
|
|
528
|
+
y_array[i] = scaling.normalize(y_array[i], p.normalize_method)
|
|
529
|
+
|
|
530
|
+
# Arrange as rows or columns
|
|
531
|
+
if p.orientation == SignalsToImageOrientation.COLUMNS:
|
|
532
|
+
data = y_array.T
|
|
533
|
+
else: # ROWS
|
|
534
|
+
data = y_array
|
|
535
|
+
|
|
536
|
+
# Create the result image
|
|
537
|
+
suffix_parts = [f"n={len(src_list)}", f"orientation={p.orientation}"]
|
|
538
|
+
if p.normalize:
|
|
539
|
+
suffix_parts.append(f"norm={p.normalize_method}")
|
|
540
|
+
suffix = ", ".join(suffix_parts)
|
|
541
|
+
title = f"combined_signals|{suffix}"
|
|
542
|
+
|
|
543
|
+
dst = create_image(title, data)
|
|
544
|
+
if p.orientation == SignalsToImageOrientation.ROWS:
|
|
545
|
+
dst.xlabel = src_list[0].ylabel
|
|
546
|
+
else:
|
|
547
|
+
dst.ylabel = src_list[0].ylabel
|
|
548
|
+
dst.zlabel = src_list[0].ylabel
|
|
549
|
+
dst.zunit = src_list[0].yunit
|
|
550
|
+
|
|
551
|
+
return dst
|