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,183 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Unit tests for shape module
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from sigima.objects import shape
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_point_inplace_transform_and_copy() -> None:
|
|
15
|
+
"""
|
|
16
|
+
Test inplace transformations and copy for PointCoordinates.
|
|
17
|
+
|
|
18
|
+
Verifies that translation and rotation work inplace, and that copy creates
|
|
19
|
+
an independent object.
|
|
20
|
+
"""
|
|
21
|
+
pt = shape.PointCoordinates([1, 2])
|
|
22
|
+
pt2 = pt.copy()
|
|
23
|
+
assert np.allclose(pt.data, pt2.data)
|
|
24
|
+
pt.translate(3, 4)
|
|
25
|
+
assert np.allclose(pt.data, [4, 6])
|
|
26
|
+
pt2.rotate(np.pi, center=(1, 2))
|
|
27
|
+
assert np.allclose(pt2.data, [1, 2]) # Rotating at own center: no change
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_segment_inplace_transform() -> None:
|
|
31
|
+
"""Test inplace transformations for SegmentCoordinates."""
|
|
32
|
+
seg = shape.SegmentCoordinates([0, 0, 1, 1])
|
|
33
|
+
seg2 = seg.copy()
|
|
34
|
+
seg.translate(1, 1)
|
|
35
|
+
assert np.allclose(seg.data, [1, 1, 2, 2])
|
|
36
|
+
seg2.rotate(np.pi / 2, center=(0, 0))
|
|
37
|
+
assert np.allclose(seg2.data, [0, 0, -1, 1])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_rectangle_inplace_transform() -> None:
|
|
41
|
+
"""
|
|
42
|
+
Test inplace horizontal and vertical flipping for RectangleCoordinates.
|
|
43
|
+
|
|
44
|
+
Verifies that flipping is performed inplace and independently on copies.
|
|
45
|
+
"""
|
|
46
|
+
rect = shape.RectangleCoordinates([0.0, 0.0, 2.0, 3.0])
|
|
47
|
+
rect2 = rect.copy()
|
|
48
|
+
rect.fliph(cx=1.5)
|
|
49
|
+
assert np.allclose(rect.data, [1.0, 0.0, 2.0, 3.0])
|
|
50
|
+
rect2.flipv(cy=2.5)
|
|
51
|
+
assert np.allclose(rect2.data, [0.0, 2.0, 2.0, 3.0])
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_circle_inplace_transform() -> None:
|
|
55
|
+
"""
|
|
56
|
+
Test inplace translation and scaling for CircleCoordinates.
|
|
57
|
+
|
|
58
|
+
Verifies that only the center is transformed and the radius remains
|
|
59
|
+
unchanged.
|
|
60
|
+
"""
|
|
61
|
+
circ = shape.CircleCoordinates([1, 2, 5])
|
|
62
|
+
circ2 = circ.copy()
|
|
63
|
+
circ.translate(2, -1)
|
|
64
|
+
assert np.allclose(circ.data, [3, 1, 5])
|
|
65
|
+
circ2.scale(2, 2)
|
|
66
|
+
assert np.allclose(circ2.data, [2, 4, 5]) # Only center is scaled
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_polygon_inplace_transform() -> None:
|
|
70
|
+
"""
|
|
71
|
+
Test inplace transpose and rotation for PolygonCoordinates.
|
|
72
|
+
|
|
73
|
+
Verifies that transpose and rotation are performed inplace and
|
|
74
|
+
independently on copies.
|
|
75
|
+
"""
|
|
76
|
+
poly = shape.PolygonCoordinates([0, 0, 1, 0, 1, 1, 0, 1])
|
|
77
|
+
poly2 = poly.copy()
|
|
78
|
+
poly.transpose()
|
|
79
|
+
assert np.allclose(poly.data, [0, 0, 0, 1, 1, 1, 1, 0])
|
|
80
|
+
poly2.rotate(np.pi / 2, center=(0, 0))
|
|
81
|
+
assert np.allclose(poly2.data, [0, 0, 0, 1, -1, 1, -1, 0])
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_inplace_vs_copy() -> None:
|
|
85
|
+
"""
|
|
86
|
+
Test that inplace transformation does not affect a copy.
|
|
87
|
+
|
|
88
|
+
Verifies that after translating the original, the copy remains unchanged.
|
|
89
|
+
"""
|
|
90
|
+
pt = shape.PointCoordinates([1, 2])
|
|
91
|
+
pt2 = pt.copy()
|
|
92
|
+
pt.translate(1, 1)
|
|
93
|
+
assert not np.allclose(pt.data, pt2.data)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_rotate_arbitrary_center() -> None:
|
|
97
|
+
"""
|
|
98
|
+
Test rotation around an arbitrary center for all coordinate types.
|
|
99
|
+
"""
|
|
100
|
+
pt = shape.PointCoordinates([2, 3])
|
|
101
|
+
pt.rotate(np.pi / 2, center=(1, 1))
|
|
102
|
+
# (2,3) rotated 90° CCW around (1,1) -> ( -1,2 )
|
|
103
|
+
assert np.allclose(pt.data, [-1, 2])
|
|
104
|
+
|
|
105
|
+
rect = shape.RectangleCoordinates([2, 3, 4, 5])
|
|
106
|
+
rect.rotate(np.pi / 2, center=(1, 1))
|
|
107
|
+
expected = [-6, 2, 5, 4]
|
|
108
|
+
assert np.allclose(rect.data, expected)
|
|
109
|
+
|
|
110
|
+
circ = shape.CircleCoordinates([2, 3, 5])
|
|
111
|
+
circ.rotate(np.pi / 2, center=(1, 1))
|
|
112
|
+
assert np.allclose(circ.data, [-1, 2, 5])
|
|
113
|
+
|
|
114
|
+
poly = shape.PolygonCoordinates([2, 3, 4, 5, 6, 7])
|
|
115
|
+
poly.rotate(np.pi / 2, center=(1, 1))
|
|
116
|
+
expected_poly = [-1, 2, -3, 4, -5, 6]
|
|
117
|
+
assert np.allclose(poly.data, expected_poly)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_fliph_flipv_arbitrary_axis() -> None:
|
|
121
|
+
"""
|
|
122
|
+
Test horizontal and vertical flip with respect to arbitrary axis
|
|
123
|
+
for all coordinate types.
|
|
124
|
+
"""
|
|
125
|
+
pt = shape.PointCoordinates([2, 3])
|
|
126
|
+
pt.fliph(cx=1.5)
|
|
127
|
+
assert np.allclose(pt.data, [1.0, 3])
|
|
128
|
+
pt2 = shape.PointCoordinates([2, 3])
|
|
129
|
+
pt2.flipv(cy=2.5)
|
|
130
|
+
assert np.allclose(pt2.data, [2, 2.0])
|
|
131
|
+
|
|
132
|
+
rect = shape.RectangleCoordinates([2, 3, 4, 5])
|
|
133
|
+
rect.fliph(cx=3)
|
|
134
|
+
assert np.allclose(rect.data, [0, 3, 4, 5])
|
|
135
|
+
rect2 = shape.RectangleCoordinates([2, 3, 4, 5])
|
|
136
|
+
rect2.flipv(cy=4)
|
|
137
|
+
assert np.allclose(rect2.data, [2, 0, 4, 5])
|
|
138
|
+
|
|
139
|
+
circ = shape.CircleCoordinates([2, 3, 5])
|
|
140
|
+
circ.fliph(cx=1)
|
|
141
|
+
assert np.allclose(circ.data, [0, 3, 5])
|
|
142
|
+
circ2 = shape.CircleCoordinates([2, 3, 5])
|
|
143
|
+
circ2.flipv(cy=2)
|
|
144
|
+
assert np.allclose(circ2.data, [2, 1, 5])
|
|
145
|
+
|
|
146
|
+
poly = shape.PolygonCoordinates([2, 3, 4, 5])
|
|
147
|
+
poly.fliph(cx=3)
|
|
148
|
+
assert np.allclose(poly.data, [4, 3, 2, 5])
|
|
149
|
+
poly2 = shape.PolygonCoordinates([2, 3, 4, 5])
|
|
150
|
+
poly2.flipv(cy=4)
|
|
151
|
+
assert np.allclose(poly2.data, [2, 5, 4, 3])
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_transpose_all_types() -> None:
|
|
155
|
+
"""
|
|
156
|
+
Test transposition (swap x and y) for all coordinate types.
|
|
157
|
+
"""
|
|
158
|
+
pt = shape.PointCoordinates([2, 3])
|
|
159
|
+
pt.transpose()
|
|
160
|
+
assert np.allclose(pt.data, [3, 2])
|
|
161
|
+
|
|
162
|
+
rect = shape.RectangleCoordinates([2, 3, 4, 5])
|
|
163
|
+
rect.transpose()
|
|
164
|
+
assert np.allclose(rect.data, [3, 2, 5, 4])
|
|
165
|
+
|
|
166
|
+
circ = shape.CircleCoordinates([2, 3, 5])
|
|
167
|
+
circ.transpose()
|
|
168
|
+
assert np.allclose(circ.data, [3, 2, 5])
|
|
169
|
+
|
|
170
|
+
poly = shape.PolygonCoordinates([2, 3, 4, 5, 6, 7])
|
|
171
|
+
poly.transpose()
|
|
172
|
+
assert np.allclose(poly.data, [3, 2, 5, 4, 7, 6])
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
test_point_inplace_transform_and_copy()
|
|
177
|
+
test_rectangle_inplace_transform()
|
|
178
|
+
test_circle_inplace_transform()
|
|
179
|
+
test_polygon_inplace_transform()
|
|
180
|
+
test_inplace_vs_copy()
|
|
181
|
+
test_rotate_arbitrary_center()
|
|
182
|
+
test_fliph_flipv_arbitrary_axis()
|
|
183
|
+
test_transpose_all_types()
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Statistics unit test
|
|
5
|
+
|
|
6
|
+
Testing the following:
|
|
7
|
+
- Create a signal
|
|
8
|
+
- Compute statistics on signal and compare with expected results
|
|
9
|
+
- Create an image
|
|
10
|
+
- Compute statistics on image and compare with expected results
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
import pytest
|
|
19
|
+
import scipy.integrate as spt
|
|
20
|
+
|
|
21
|
+
import sigima.objects
|
|
22
|
+
import sigima.proc.image
|
|
23
|
+
import sigima.proc.signal
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_analytical_stats(data: np.ndarray) -> dict[str, float]:
|
|
27
|
+
"""Compute analytical statistics for data
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
data: Array of data
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Dictionary with analytical statistics
|
|
34
|
+
"""
|
|
35
|
+
results = {}
|
|
36
|
+
if data.shape[0] == 2:
|
|
37
|
+
# This is a signal data (row 0: x, row 1: y)
|
|
38
|
+
results["trapz"] = spt.trapezoid(data[1], data[0])
|
|
39
|
+
data = data[1]
|
|
40
|
+
results.update(
|
|
41
|
+
{
|
|
42
|
+
"min": np.min(data),
|
|
43
|
+
"max": np.max(data),
|
|
44
|
+
"mean": np.mean(data),
|
|
45
|
+
"median": np.median(data),
|
|
46
|
+
"std": np.std(data),
|
|
47
|
+
"snr": np.mean(data) / np.std(data),
|
|
48
|
+
"ptp": np.ptp(data),
|
|
49
|
+
"sum": np.sum(data),
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
return results
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_reference_signal() -> sigima.objects.SignalObj:
|
|
56
|
+
"""Create reference signal"""
|
|
57
|
+
param = sigima.objects.GaussParam()
|
|
58
|
+
sig = sigima.objects.create_signal_from_param(param)
|
|
59
|
+
sig.roi = sigima.objects.create_signal_roi(
|
|
60
|
+
[len(sig.x) // 2, len(sig.x) - 1], indices=True
|
|
61
|
+
)
|
|
62
|
+
return sig
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def create_reference_image() -> sigima.objects.ImageObj:
|
|
66
|
+
"""Create reference image"""
|
|
67
|
+
param = sigima.objects.Gauss2DParam.create(title="2D-Gaussian")
|
|
68
|
+
ima = sigima.objects.create_image_from_param(param)
|
|
69
|
+
dy, dx = ima.data.shape
|
|
70
|
+
ima.roi = sigima.objects.create_image_roi(
|
|
71
|
+
"rectangle",
|
|
72
|
+
[
|
|
73
|
+
[dx // 2, 0, dx, dy],
|
|
74
|
+
[0, 0, dx // 3, dy // 3],
|
|
75
|
+
[dx // 2, dy // 2, dx, dy],
|
|
76
|
+
],
|
|
77
|
+
)
|
|
78
|
+
return ima
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@pytest.mark.validation
|
|
82
|
+
def test_signal_stats_unit() -> None:
|
|
83
|
+
"""Validate computed statistics for signals"""
|
|
84
|
+
obj = create_reference_signal()
|
|
85
|
+
table = sigima.proc.signal.stats(obj)
|
|
86
|
+
ref = get_analytical_stats(obj.xydata)
|
|
87
|
+
for key, val in ref.items():
|
|
88
|
+
assert key in table
|
|
89
|
+
assert np.isclose(table[key][0], val), f"Incorrect value for {key}"
|
|
90
|
+
|
|
91
|
+
# Given the fact that signal ROI is set to [len(sig.x) // 2, len(sig.x) - 1],
|
|
92
|
+
# we may check the relationship between the results on the whole signal and the ROI:
|
|
93
|
+
for key, val in ref.items():
|
|
94
|
+
if key in ("trapz", "sum"):
|
|
95
|
+
assert np.isclose(table[key][1], val / 2, rtol=0.02)
|
|
96
|
+
elif key == "median":
|
|
97
|
+
continue
|
|
98
|
+
else:
|
|
99
|
+
assert np.isclose(table[key][1], val, rtol=0.01)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@pytest.mark.validation
|
|
103
|
+
def test_image_stats_unit() -> None:
|
|
104
|
+
"""Validate computed statistics for images"""
|
|
105
|
+
obj = create_reference_image()
|
|
106
|
+
|
|
107
|
+
# Ignore "RuntimeWarning: invalid value encountered in scalar divide" in the test
|
|
108
|
+
# (this warning is due to the fact that the 2nd ROI has zero sum of pixel values,
|
|
109
|
+
# hence the mean/std is NaN)
|
|
110
|
+
with np.errstate(invalid="ignore"):
|
|
111
|
+
res = sigima.proc.image.stats(obj)
|
|
112
|
+
|
|
113
|
+
ref = get_analytical_stats(obj.data)
|
|
114
|
+
for key, val in ref.items():
|
|
115
|
+
assert key in res
|
|
116
|
+
assert np.isclose(res[key][0], val, rtol=1e-4, atol=1e-5), (
|
|
117
|
+
f"Incorrect value for {key}"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Given the fact that image ROI is set to
|
|
121
|
+
# [[dx // 2, 0, dx, dy], [0, 0, dx // 3, dy // 3], [dx // 2, dy // 2, dx, dy]],
|
|
122
|
+
# we may check the relationship between the results on the whole image and the ROIs:
|
|
123
|
+
for key, val in ref.items():
|
|
124
|
+
if key == "sum":
|
|
125
|
+
assert np.isclose(res[key][1], val / 2, rtol=0.02)
|
|
126
|
+
assert np.isclose(res[key][3], val / 4, rtol=0.02)
|
|
127
|
+
elif key == "median":
|
|
128
|
+
continue
|
|
129
|
+
else:
|
|
130
|
+
assert np.isclose(res[key][1], val, rtol=0.01)
|
|
131
|
+
assert np.isclose(res[key][3], val, rtol=0.01)
|
|
132
|
+
if key != "snr":
|
|
133
|
+
assert np.isclose(res[key][2], 0.0, atol=0.001)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
test_signal_stats_unit()
|
|
138
|
+
test_image_stats_unit()
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Title formatting system unit tests
|
|
5
|
+
|
|
6
|
+
Testing the configurable title formatting system that allows different applications
|
|
7
|
+
(Sigima standalone vs DataLab integration) to use different title formatting strategies.
|
|
8
|
+
|
|
9
|
+
This test verifies:
|
|
10
|
+
- SimpleTitleFormatter: Human-readable titles for standalone Sigima usage
|
|
11
|
+
- PlaceholderTitleFormatter: DataLab-compatible placeholder titles
|
|
12
|
+
- Configuration system: Setting and getting default formatters
|
|
13
|
+
- Integration: Testing with computation functions from sigima.proc.base
|
|
14
|
+
- Compatibility: Ensuring DataLab-style placeholder resolution works
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import pytest
|
|
20
|
+
|
|
21
|
+
from sigima import create_signal
|
|
22
|
+
from sigima.proc.base import dst_1_to_1, dst_2_to_1, dst_n_to_1
|
|
23
|
+
from sigima.proc.title_formatting import (
|
|
24
|
+
PlaceholderTitleFormatter,
|
|
25
|
+
SimpleTitleFormatter,
|
|
26
|
+
get_default_title_formatter,
|
|
27
|
+
set_default_title_formatter,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestSimpleTitleFormatter:
|
|
32
|
+
"""Test suite for SimpleTitleFormatter class."""
|
|
33
|
+
|
|
34
|
+
def test_1_to_1_operations(self):
|
|
35
|
+
"""Test SimpleTitleFormatter for 1-to-1 operations."""
|
|
36
|
+
formatter = SimpleTitleFormatter()
|
|
37
|
+
|
|
38
|
+
# Test basic function name formatting
|
|
39
|
+
assert (
|
|
40
|
+
formatter.format_1_to_1_title("gaussian_filter") == "Gaussian Filter Result"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Test with suffix
|
|
44
|
+
result = formatter.format_1_to_1_title("gaussian_filter", "sigma=1.5")
|
|
45
|
+
assert result == "Gaussian Filter Result (sigma=1.5)"
|
|
46
|
+
|
|
47
|
+
# Test with operator
|
|
48
|
+
assert formatter.format_1_to_1_title("+") == "operator_+"
|
|
49
|
+
|
|
50
|
+
def test_n_to_1_operations(self):
|
|
51
|
+
"""Test SimpleTitleFormatter for n-to-1 operations."""
|
|
52
|
+
formatter = SimpleTitleFormatter()
|
|
53
|
+
|
|
54
|
+
# Test basic n-to-1 formatting
|
|
55
|
+
result = formatter.format_n_to_1_title("add_signals", 3)
|
|
56
|
+
assert result == "Add Signals of 3 Objects"
|
|
57
|
+
|
|
58
|
+
# Test with suffix
|
|
59
|
+
result = formatter.format_n_to_1_title("mean", 5, "weighted")
|
|
60
|
+
assert result == "Mean of 5 Objects (weighted)"
|
|
61
|
+
|
|
62
|
+
def test_2_to_1_operations(self):
|
|
63
|
+
"""Test SimpleTitleFormatter for 2-to-1 operations."""
|
|
64
|
+
formatter = SimpleTitleFormatter()
|
|
65
|
+
|
|
66
|
+
# Test basic 2-to-1 formatting
|
|
67
|
+
assert formatter.format_2_to_1_title("subtract") == "Subtract Result"
|
|
68
|
+
|
|
69
|
+
# Test with operator
|
|
70
|
+
assert formatter.format_2_to_1_title("-") == "Binary Operation -"
|
|
71
|
+
|
|
72
|
+
# Test with suffix
|
|
73
|
+
result = formatter.format_2_to_1_title("divide", "method=euclidean")
|
|
74
|
+
assert result == "Divide Result (method=euclidean)"
|
|
75
|
+
|
|
76
|
+
def test_placeholder_resolution(self):
|
|
77
|
+
"""Test SimpleTitleFormatter placeholder resolution (should be unchanged)."""
|
|
78
|
+
formatter = SimpleTitleFormatter()
|
|
79
|
+
|
|
80
|
+
# Should return unchanged for simple formatter
|
|
81
|
+
result = formatter.resolve_placeholder_title("wiener({0})", [])
|
|
82
|
+
assert result == "wiener({0})"
|
|
83
|
+
|
|
84
|
+
def test_humanize_function_name(self):
|
|
85
|
+
"""Test the function name humanization logic."""
|
|
86
|
+
formatter = SimpleTitleFormatter()
|
|
87
|
+
|
|
88
|
+
# Test the internal logic through the public interface
|
|
89
|
+
# The formatter converts snake_case to Title Case internally
|
|
90
|
+
result = formatter.format_1_to_1_title("gaussian_filter")
|
|
91
|
+
assert "Gaussian Filter" in result
|
|
92
|
+
|
|
93
|
+
result = formatter.format_1_to_1_title("moving_average")
|
|
94
|
+
assert "Moving Average" in result
|
|
95
|
+
|
|
96
|
+
# Test single words
|
|
97
|
+
result = formatter.format_1_to_1_title("normalize")
|
|
98
|
+
assert "Normalize" in result
|
|
99
|
+
|
|
100
|
+
# Test operators (should be handled specially)
|
|
101
|
+
result = formatter.format_1_to_1_title("+")
|
|
102
|
+
assert "operator_+" in result
|
|
103
|
+
|
|
104
|
+
result = formatter.format_1_to_1_title("-")
|
|
105
|
+
assert "operator_-" in result
|
|
106
|
+
|
|
107
|
+
def test_edge_cases(self):
|
|
108
|
+
"""Test edge cases for SimpleTitleFormatter."""
|
|
109
|
+
formatter = SimpleTitleFormatter()
|
|
110
|
+
|
|
111
|
+
# Test empty function name
|
|
112
|
+
assert formatter.format_1_to_1_title("") == " Result"
|
|
113
|
+
|
|
114
|
+
# Test None suffix (should be handled gracefully)
|
|
115
|
+
result = formatter.format_1_to_1_title("test", None)
|
|
116
|
+
assert result == "Test Result"
|
|
117
|
+
|
|
118
|
+
# Test empty suffix
|
|
119
|
+
result = formatter.format_1_to_1_title("test", "")
|
|
120
|
+
assert result == "Test Result"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class TestPlaceholderTitleFormatter:
|
|
124
|
+
"""Test suite for PlaceholderTitleFormatter class."""
|
|
125
|
+
|
|
126
|
+
def test_1_to_1_operations(self):
|
|
127
|
+
"""Test PlaceholderTitleFormatter for 1-to-1 operations."""
|
|
128
|
+
formatter = PlaceholderTitleFormatter()
|
|
129
|
+
|
|
130
|
+
# Test basic function name formatting
|
|
131
|
+
assert formatter.format_1_to_1_title("wiener") == "wiener({0})"
|
|
132
|
+
|
|
133
|
+
# Test with suffix
|
|
134
|
+
result = formatter.format_1_to_1_title("gaussian_filter", "sigma=1.5")
|
|
135
|
+
assert result == "gaussian_filter({0})|sigma=1.5"
|
|
136
|
+
|
|
137
|
+
def test_n_to_1_operations(self):
|
|
138
|
+
"""Test PlaceholderTitleFormatter for n-to-1 operations."""
|
|
139
|
+
formatter = PlaceholderTitleFormatter()
|
|
140
|
+
|
|
141
|
+
# Test basic n-to-1 formatting
|
|
142
|
+
result = formatter.format_n_to_1_title("sum", 3)
|
|
143
|
+
assert result == "sum({0}, {1}, {2})"
|
|
144
|
+
|
|
145
|
+
# Test with suffix
|
|
146
|
+
result = formatter.format_n_to_1_title("mean", 5, "weighted=True")
|
|
147
|
+
assert result == "mean({0}, {1}, {2}, {3}, {4})|weighted=True"
|
|
148
|
+
|
|
149
|
+
def test_2_to_1_operations(self):
|
|
150
|
+
"""Test PlaceholderTitleFormatter for 2-to-1 operations."""
|
|
151
|
+
formatter = PlaceholderTitleFormatter()
|
|
152
|
+
|
|
153
|
+
# Test basic 2-to-1 formatting
|
|
154
|
+
assert formatter.format_2_to_1_title("subtract") == "subtract({0}, {1})"
|
|
155
|
+
|
|
156
|
+
# Test with suffix
|
|
157
|
+
result = formatter.format_2_to_1_title("divide", "method=euclidean")
|
|
158
|
+
assert result == "divide({0}, {1})|method=euclidean"
|
|
159
|
+
|
|
160
|
+
def test_placeholder_resolution(self):
|
|
161
|
+
"""Test PlaceholderTitleFormatter placeholder resolution."""
|
|
162
|
+
formatter = PlaceholderTitleFormatter()
|
|
163
|
+
|
|
164
|
+
# Create mock objects with simple string values (strings are used as fallback)
|
|
165
|
+
src1 = create_signal("Signal001", x=[1, 2], y=[3, 4])
|
|
166
|
+
|
|
167
|
+
# Test basic placeholder resolution (uses obj1 fallback since no short_id)
|
|
168
|
+
result = formatter.resolve_placeholder_title("wiener({0})", [src1])
|
|
169
|
+
assert result == "wiener(obj1)"
|
|
170
|
+
|
|
171
|
+
# Test with suffix (suffix is preserved in resolution)
|
|
172
|
+
result = formatter.resolve_placeholder_title("gaussian({0})|sigma=2.0", [src1])
|
|
173
|
+
assert result == "gaussian(obj1)|sigma=2.0"
|
|
174
|
+
|
|
175
|
+
# Test empty placeholders list (should raise IndexError)
|
|
176
|
+
with pytest.raises(IndexError):
|
|
177
|
+
formatter.resolve_placeholder_title("wiener({0})", [])
|
|
178
|
+
|
|
179
|
+
def test_edge_cases(self):
|
|
180
|
+
"""Test edge cases for PlaceholderTitleFormatter."""
|
|
181
|
+
formatter = PlaceholderTitleFormatter()
|
|
182
|
+
|
|
183
|
+
# Test empty function name
|
|
184
|
+
assert formatter.format_1_to_1_title("") == "({0})"
|
|
185
|
+
|
|
186
|
+
# Test None suffix (should be handled gracefully)
|
|
187
|
+
result = formatter.format_1_to_1_title("test", None)
|
|
188
|
+
assert result == "test({0})"
|
|
189
|
+
|
|
190
|
+
# Test empty suffix
|
|
191
|
+
result = formatter.format_1_to_1_title("test", "")
|
|
192
|
+
assert result == "test({0})"
|
|
193
|
+
|
|
194
|
+
def test_datalab_compatibility(self):
|
|
195
|
+
"""Test DataLab-style placeholder resolution compatibility."""
|
|
196
|
+
formatter = PlaceholderTitleFormatter()
|
|
197
|
+
|
|
198
|
+
# Create signal objects for testing
|
|
199
|
+
sig1 = create_signal("Signal001", x=[1, 2], y=[3, 4])
|
|
200
|
+
|
|
201
|
+
# Test typical DataLab usage patterns (uses obj1 fallback without short_id)
|
|
202
|
+
test_cases = [
|
|
203
|
+
("wiener({0})", [sig1], "wiener(obj1)"),
|
|
204
|
+
("gaussian({0})|sigma=2.0", [sig1], "gaussian(obj1)|sigma=2.0"),
|
|
205
|
+
("add({0})|3 objects", [sig1], "add(obj1)|3 objects"),
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
for placeholder_title, source_titles, expected in test_cases:
|
|
209
|
+
result = formatter.resolve_placeholder_title(
|
|
210
|
+
placeholder_title, source_titles
|
|
211
|
+
)
|
|
212
|
+
assert result == expected
|
|
213
|
+
|
|
214
|
+
# Test empty list case (should raise IndexError)
|
|
215
|
+
with pytest.raises(IndexError):
|
|
216
|
+
formatter.resolve_placeholder_title("fft({0})", [])
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class TestFormatterConfiguration:
|
|
220
|
+
"""Test suite for the global formatter configuration system."""
|
|
221
|
+
|
|
222
|
+
def test_configuration_system(self):
|
|
223
|
+
"""Test the global formatter configuration system."""
|
|
224
|
+
# Store original formatter to restore later
|
|
225
|
+
original_formatter = get_default_title_formatter()
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
# Test setting SimpleTitleFormatter
|
|
229
|
+
simple_formatter = SimpleTitleFormatter()
|
|
230
|
+
set_default_title_formatter(simple_formatter)
|
|
231
|
+
current_formatter = get_default_title_formatter()
|
|
232
|
+
assert isinstance(current_formatter, SimpleTitleFormatter)
|
|
233
|
+
|
|
234
|
+
# Test setting PlaceholderTitleFormatter
|
|
235
|
+
placeholder_formatter = PlaceholderTitleFormatter()
|
|
236
|
+
set_default_title_formatter(placeholder_formatter)
|
|
237
|
+
current_formatter = get_default_title_formatter()
|
|
238
|
+
assert isinstance(current_formatter, PlaceholderTitleFormatter)
|
|
239
|
+
|
|
240
|
+
finally:
|
|
241
|
+
# Restore original formatter
|
|
242
|
+
set_default_title_formatter(original_formatter)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class TestIntegrationWithComputationFunctions:
|
|
246
|
+
"""Test suite for integration with computation functions from sigima.proc.base."""
|
|
247
|
+
|
|
248
|
+
def test_integration_with_dst_1_to_1(self):
|
|
249
|
+
"""Test integration with dst_1_to_1 function."""
|
|
250
|
+
original_formatter = get_default_title_formatter()
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
# Test with SimpleTitleFormatter
|
|
254
|
+
set_default_title_formatter(SimpleTitleFormatter())
|
|
255
|
+
src = create_signal("Test Signal", x=[1, 2, 3], y=[4, 5, 6])
|
|
256
|
+
result = dst_1_to_1(src, "gaussian_filter", "sigma=1.0")
|
|
257
|
+
assert "Gaussian Filter Result" in result.title
|
|
258
|
+
assert "sigma=1.0" in result.title
|
|
259
|
+
|
|
260
|
+
# Test with PlaceholderTitleFormatter
|
|
261
|
+
set_default_title_formatter(PlaceholderTitleFormatter())
|
|
262
|
+
result2 = dst_1_to_1(src, "gaussian_filter", "sigma=1.0")
|
|
263
|
+
assert result2.title == "gaussian_filter({0})|sigma=1.0"
|
|
264
|
+
|
|
265
|
+
finally:
|
|
266
|
+
set_default_title_formatter(original_formatter)
|
|
267
|
+
|
|
268
|
+
def test_integration_with_dst_2_to_1(self):
|
|
269
|
+
"""Test integration with dst_2_to_1 function."""
|
|
270
|
+
original_formatter = get_default_title_formatter()
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
# Test with SimpleTitleFormatter
|
|
274
|
+
set_default_title_formatter(SimpleTitleFormatter())
|
|
275
|
+
src1 = create_signal("Signal1", x=[1, 2, 3], y=[1, 2, 3])
|
|
276
|
+
src2 = create_signal("Signal2", x=[1, 2, 3], y=[4, 5, 6])
|
|
277
|
+
result = dst_2_to_1(src1, src2, "subtract")
|
|
278
|
+
assert result.title == "Subtract Result"
|
|
279
|
+
|
|
280
|
+
# Test with PlaceholderTitleFormatter
|
|
281
|
+
set_default_title_formatter(PlaceholderTitleFormatter())
|
|
282
|
+
result2 = dst_2_to_1(src1, src2, "subtract")
|
|
283
|
+
assert result2.title == "subtract({0}, {1})"
|
|
284
|
+
|
|
285
|
+
finally:
|
|
286
|
+
set_default_title_formatter(original_formatter)
|
|
287
|
+
|
|
288
|
+
def test_integration_with_dst_n_to_1(self):
|
|
289
|
+
"""Test integration with dst_n_to_1 function."""
|
|
290
|
+
original_formatter = get_default_title_formatter()
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
# Test with SimpleTitleFormatter
|
|
294
|
+
set_default_title_formatter(SimpleTitleFormatter())
|
|
295
|
+
signals = [
|
|
296
|
+
create_signal(f"Signal{i}", x=[1, 2, 3], y=[i, i + 1, i + 2])
|
|
297
|
+
for i in range(1, 4)
|
|
298
|
+
]
|
|
299
|
+
result = dst_n_to_1(signals, "sum")
|
|
300
|
+
assert result.title == "Sum of 3 Objects"
|
|
301
|
+
|
|
302
|
+
# Test with PlaceholderTitleFormatter
|
|
303
|
+
set_default_title_formatter(PlaceholderTitleFormatter())
|
|
304
|
+
result2 = dst_n_to_1(signals, "sum")
|
|
305
|
+
assert result2.title == "sum({0}, {1}, {2})"
|
|
306
|
+
|
|
307
|
+
finally:
|
|
308
|
+
set_default_title_formatter(original_formatter)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class TestFormatterConsistency:
|
|
312
|
+
"""Test suite for formatter consistency and compatibility."""
|
|
313
|
+
|
|
314
|
+
def test_formatter_consistency(self):
|
|
315
|
+
"""Test that formatters produce consistent results."""
|
|
316
|
+
simple = SimpleTitleFormatter()
|
|
317
|
+
placeholder = PlaceholderTitleFormatter()
|
|
318
|
+
|
|
319
|
+
# Test same function name produces consistent patterns
|
|
320
|
+
func_name = "gaussian_filter"
|
|
321
|
+
suffix = "sigma=1.0"
|
|
322
|
+
|
|
323
|
+
simple_result = simple.format_1_to_1_title(func_name, suffix)
|
|
324
|
+
placeholder_result = placeholder.format_1_to_1_title(func_name, suffix)
|
|
325
|
+
|
|
326
|
+
# Both should include the suffix information
|
|
327
|
+
assert suffix in simple_result
|
|
328
|
+
assert suffix in placeholder_result
|
|
329
|
+
|
|
330
|
+
# Simple should be human-readable
|
|
331
|
+
assert "Gaussian Filter" in simple_result
|
|
332
|
+
|
|
333
|
+
# Placeholder should use function name as-is
|
|
334
|
+
assert func_name in placeholder_result
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
if __name__ == "__main__":
|
|
338
|
+
pytest.main([__file__])
|