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.
Files changed (264) hide show
  1. sigima/__init__.py +142 -2
  2. sigima/client/__init__.py +105 -0
  3. sigima/client/base.py +780 -0
  4. sigima/client/remote.py +469 -0
  5. sigima/client/stub.py +814 -0
  6. sigima/client/utils.py +90 -0
  7. sigima/config.py +444 -0
  8. sigima/data/logo/Sigima.svg +135 -0
  9. sigima/data/tests/annotations.json +798 -0
  10. sigima/data/tests/curve_fitting/exponential_fit.txt +511 -0
  11. sigima/data/tests/curve_fitting/gaussian_fit.txt +100 -0
  12. sigima/data/tests/curve_fitting/piecewiseexponential_fit.txt +1022 -0
  13. sigima/data/tests/curve_fitting/polynomial_fit.txt +100 -0
  14. sigima/data/tests/curve_fitting/twohalfgaussian_fit.txt +1000 -0
  15. sigima/data/tests/curve_formats/bandwidth.txt +201 -0
  16. sigima/data/tests/curve_formats/boxcar.npy +0 -0
  17. sigima/data/tests/curve_formats/datetime.txt +1001 -0
  18. sigima/data/tests/curve_formats/dynamic_parameters.txt +4000 -0
  19. sigima/data/tests/curve_formats/fw1e2.txt +301 -0
  20. sigima/data/tests/curve_formats/fwhm.txt +319 -0
  21. sigima/data/tests/curve_formats/multiple_curves.csv +29 -0
  22. sigima/data/tests/curve_formats/noised_saw.mat +0 -0
  23. sigima/data/tests/curve_formats/oscilloscope.csv +111 -0
  24. sigima/data/tests/curve_formats/other/other2/recursive2.txt +5 -0
  25. sigima/data/tests/curve_formats/other/recursive1.txt +5 -0
  26. sigima/data/tests/curve_formats/paracetamol.npy +0 -0
  27. sigima/data/tests/curve_formats/paracetamol.txt +1010 -0
  28. sigima/data/tests/curve_formats/paracetamol_dx_dy.csv +1000 -0
  29. sigima/data/tests/curve_formats/paracetamol_dy.csv +1001 -0
  30. sigima/data/tests/curve_formats/pulse1.npy +0 -0
  31. sigima/data/tests/curve_formats/pulse2.npy +0 -0
  32. sigima/data/tests/curve_formats/simple.txt +5 -0
  33. sigima/data/tests/curve_formats/spectrum.mca +2139 -0
  34. sigima/data/tests/curve_formats/square2.npy +0 -0
  35. sigima/data/tests/curve_formats/step.npy +0 -0
  36. sigima/data/tests/fabry-perot1.jpg +0 -0
  37. sigima/data/tests/fabry-perot2.jpg +0 -0
  38. sigima/data/tests/flower.npy +0 -0
  39. sigima/data/tests/image_formats/NF 180338201.scor-data +11003 -0
  40. sigima/data/tests/image_formats/binary_image.npy +0 -0
  41. sigima/data/tests/image_formats/binary_image.png +0 -0
  42. sigima/data/tests/image_formats/centroid_test.npy +0 -0
  43. sigima/data/tests/image_formats/coordinated_text/complex_image.txt +10011 -0
  44. sigima/data/tests/image_formats/coordinated_text/complex_ref_image.txt +10010 -0
  45. sigima/data/tests/image_formats/coordinated_text/image.txt +15 -0
  46. sigima/data/tests/image_formats/coordinated_text/image2.txt +14 -0
  47. sigima/data/tests/image_formats/coordinated_text/image_no_unit_no_label.txt +14 -0
  48. sigima/data/tests/image_formats/coordinated_text/image_with_nan.txt +15 -0
  49. sigima/data/tests/image_formats/coordinated_text/image_with_unit.txt +14 -0
  50. sigima/data/tests/image_formats/fiber.csv +480 -0
  51. sigima/data/tests/image_formats/fiber.jpg +0 -0
  52. sigima/data/tests/image_formats/fiber.png +0 -0
  53. sigima/data/tests/image_formats/fiber.txt +480 -0
  54. sigima/data/tests/image_formats/gaussian_spot_with_noise.npy +0 -0
  55. sigima/data/tests/image_formats/mr-brain.dcm +0 -0
  56. sigima/data/tests/image_formats/noised_gaussian.mat +0 -0
  57. sigima/data/tests/image_formats/sif_reader/nd_lum_image_no_glue.sif +0 -0
  58. sigima/data/tests/image_formats/sif_reader/raman1.sif +0 -0
  59. sigima/data/tests/image_formats/tiling.txt +10 -0
  60. sigima/data/tests/image_formats/uint16.tiff +0 -0
  61. sigima/data/tests/image_formats/uint8.tiff +0 -0
  62. sigima/data/tests/laser_beam/TEM00_z_13.jpg +0 -0
  63. sigima/data/tests/laser_beam/TEM00_z_18.jpg +0 -0
  64. sigima/data/tests/laser_beam/TEM00_z_23.jpg +0 -0
  65. sigima/data/tests/laser_beam/TEM00_z_30.jpg +0 -0
  66. sigima/data/tests/laser_beam/TEM00_z_35.jpg +0 -0
  67. sigima/data/tests/laser_beam/TEM00_z_40.jpg +0 -0
  68. sigima/data/tests/laser_beam/TEM00_z_45.jpg +0 -0
  69. sigima/data/tests/laser_beam/TEM00_z_50.jpg +0 -0
  70. sigima/data/tests/laser_beam/TEM00_z_55.jpg +0 -0
  71. sigima/data/tests/laser_beam/TEM00_z_60.jpg +0 -0
  72. sigima/data/tests/laser_beam/TEM00_z_65.jpg +0 -0
  73. sigima/data/tests/laser_beam/TEM00_z_70.jpg +0 -0
  74. sigima/data/tests/laser_beam/TEM00_z_75.jpg +0 -0
  75. sigima/data/tests/laser_beam/TEM00_z_80.jpg +0 -0
  76. sigima/enums.py +195 -0
  77. sigima/io/__init__.py +123 -0
  78. sigima/io/base.py +311 -0
  79. sigima/io/common/__init__.py +5 -0
  80. sigima/io/common/basename.py +164 -0
  81. sigima/io/common/converters.py +189 -0
  82. sigima/io/common/objmeta.py +181 -0
  83. sigima/io/common/textreader.py +58 -0
  84. sigima/io/convenience.py +157 -0
  85. sigima/io/enums.py +17 -0
  86. sigima/io/ftlab.py +395 -0
  87. sigima/io/image/__init__.py +9 -0
  88. sigima/io/image/base.py +177 -0
  89. sigima/io/image/formats.py +1016 -0
  90. sigima/io/image/funcs.py +414 -0
  91. sigima/io/signal/__init__.py +9 -0
  92. sigima/io/signal/base.py +129 -0
  93. sigima/io/signal/formats.py +290 -0
  94. sigima/io/signal/funcs.py +723 -0
  95. sigima/objects/__init__.py +260 -0
  96. sigima/objects/base.py +937 -0
  97. sigima/objects/image/__init__.py +88 -0
  98. sigima/objects/image/creation.py +556 -0
  99. sigima/objects/image/object.py +524 -0
  100. sigima/objects/image/roi.py +904 -0
  101. sigima/objects/scalar/__init__.py +57 -0
  102. sigima/objects/scalar/common.py +215 -0
  103. sigima/objects/scalar/geometry.py +502 -0
  104. sigima/objects/scalar/table.py +784 -0
  105. sigima/objects/shape.py +290 -0
  106. sigima/objects/signal/__init__.py +133 -0
  107. sigima/objects/signal/constants.py +27 -0
  108. sigima/objects/signal/creation.py +1428 -0
  109. sigima/objects/signal/object.py +444 -0
  110. sigima/objects/signal/roi.py +274 -0
  111. sigima/params.py +405 -0
  112. sigima/proc/__init__.py +96 -0
  113. sigima/proc/base.py +381 -0
  114. sigima/proc/decorator.py +330 -0
  115. sigima/proc/image/__init__.py +513 -0
  116. sigima/proc/image/arithmetic.py +335 -0
  117. sigima/proc/image/base.py +260 -0
  118. sigima/proc/image/detection.py +519 -0
  119. sigima/proc/image/edges.py +329 -0
  120. sigima/proc/image/exposure.py +406 -0
  121. sigima/proc/image/extraction.py +458 -0
  122. sigima/proc/image/filtering.py +219 -0
  123. sigima/proc/image/fourier.py +147 -0
  124. sigima/proc/image/geometry.py +661 -0
  125. sigima/proc/image/mathops.py +340 -0
  126. sigima/proc/image/measurement.py +195 -0
  127. sigima/proc/image/morphology.py +155 -0
  128. sigima/proc/image/noise.py +107 -0
  129. sigima/proc/image/preprocessing.py +182 -0
  130. sigima/proc/image/restoration.py +235 -0
  131. sigima/proc/image/threshold.py +217 -0
  132. sigima/proc/image/transformations.py +393 -0
  133. sigima/proc/signal/__init__.py +376 -0
  134. sigima/proc/signal/analysis.py +206 -0
  135. sigima/proc/signal/arithmetic.py +551 -0
  136. sigima/proc/signal/base.py +262 -0
  137. sigima/proc/signal/extraction.py +60 -0
  138. sigima/proc/signal/features.py +310 -0
  139. sigima/proc/signal/filtering.py +484 -0
  140. sigima/proc/signal/fitting.py +276 -0
  141. sigima/proc/signal/fourier.py +259 -0
  142. sigima/proc/signal/mathops.py +420 -0
  143. sigima/proc/signal/processing.py +580 -0
  144. sigima/proc/signal/stability.py +175 -0
  145. sigima/proc/title_formatting.py +227 -0
  146. sigima/proc/validation.py +272 -0
  147. sigima/tests/__init__.py +7 -0
  148. sigima/tests/common/__init__.py +0 -0
  149. sigima/tests/common/arithmeticparam_unit_test.py +26 -0
  150. sigima/tests/common/basename_unit_test.py +126 -0
  151. sigima/tests/common/client_unit_test.py +412 -0
  152. sigima/tests/common/converters_unit_test.py +77 -0
  153. sigima/tests/common/decorator_unit_test.py +176 -0
  154. sigima/tests/common/examples_unit_test.py +104 -0
  155. sigima/tests/common/kernel_normalization_unit_test.py +242 -0
  156. sigima/tests/common/roi_basic_unit_test.py +73 -0
  157. sigima/tests/common/roi_geometry_unit_test.py +171 -0
  158. sigima/tests/common/scalar_builder_unit_test.py +142 -0
  159. sigima/tests/common/scalar_unit_test.py +991 -0
  160. sigima/tests/common/shape_unit_test.py +183 -0
  161. sigima/tests/common/stat_unit_test.py +138 -0
  162. sigima/tests/common/title_formatting_unit_test.py +338 -0
  163. sigima/tests/common/tools_coordinates_unit_test.py +60 -0
  164. sigima/tests/common/transformations_unit_test.py +178 -0
  165. sigima/tests/common/validation_unit_test.py +205 -0
  166. sigima/tests/conftest.py +129 -0
  167. sigima/tests/data.py +998 -0
  168. sigima/tests/env.py +280 -0
  169. sigima/tests/guiutils.py +163 -0
  170. sigima/tests/helpers.py +532 -0
  171. sigima/tests/image/__init__.py +28 -0
  172. sigima/tests/image/binning_unit_test.py +128 -0
  173. sigima/tests/image/blob_detection_unit_test.py +312 -0
  174. sigima/tests/image/centroid_unit_test.py +170 -0
  175. sigima/tests/image/check_2d_array_unit_test.py +63 -0
  176. sigima/tests/image/contour_unit_test.py +172 -0
  177. sigima/tests/image/convolution_unit_test.py +178 -0
  178. sigima/tests/image/datatype_unit_test.py +67 -0
  179. sigima/tests/image/edges_unit_test.py +155 -0
  180. sigima/tests/image/enclosingcircle_unit_test.py +88 -0
  181. sigima/tests/image/exposure_unit_test.py +223 -0
  182. sigima/tests/image/fft2d_unit_test.py +189 -0
  183. sigima/tests/image/filtering_unit_test.py +166 -0
  184. sigima/tests/image/geometry_unit_test.py +654 -0
  185. sigima/tests/image/hough_circle_unit_test.py +147 -0
  186. sigima/tests/image/imageobj_unit_test.py +737 -0
  187. sigima/tests/image/morphology_unit_test.py +71 -0
  188. sigima/tests/image/noise_unit_test.py +57 -0
  189. sigima/tests/image/offset_correction_unit_test.py +72 -0
  190. sigima/tests/image/operation_unit_test.py +518 -0
  191. sigima/tests/image/peak2d_limits_unit_test.py +41 -0
  192. sigima/tests/image/peak2d_unit_test.py +133 -0
  193. sigima/tests/image/profile_unit_test.py +159 -0
  194. sigima/tests/image/projections_unit_test.py +121 -0
  195. sigima/tests/image/restoration_unit_test.py +141 -0
  196. sigima/tests/image/roi2dparam_unit_test.py +53 -0
  197. sigima/tests/image/roi_advanced_unit_test.py +588 -0
  198. sigima/tests/image/roi_grid_unit_test.py +279 -0
  199. sigima/tests/image/spectrum2d_unit_test.py +40 -0
  200. sigima/tests/image/threshold_unit_test.py +91 -0
  201. sigima/tests/io/__init__.py +0 -0
  202. sigima/tests/io/addnewformat_unit_test.py +125 -0
  203. sigima/tests/io/convenience_funcs_unit_test.py +470 -0
  204. sigima/tests/io/coordinated_text_format_unit_test.py +495 -0
  205. sigima/tests/io/datetime_csv_unit_test.py +198 -0
  206. sigima/tests/io/imageio_formats_test.py +41 -0
  207. sigima/tests/io/ioregistry_unit_test.py +69 -0
  208. sigima/tests/io/objmeta_unit_test.py +87 -0
  209. sigima/tests/io/readobj_unit_test.py +130 -0
  210. sigima/tests/io/readwriteobj_unit_test.py +67 -0
  211. sigima/tests/signal/__init__.py +0 -0
  212. sigima/tests/signal/analysis_unit_test.py +135 -0
  213. sigima/tests/signal/check_1d_arrays_unit_test.py +169 -0
  214. sigima/tests/signal/convolution_unit_test.py +404 -0
  215. sigima/tests/signal/datetime_unit_test.py +176 -0
  216. sigima/tests/signal/fft1d_unit_test.py +303 -0
  217. sigima/tests/signal/filters_unit_test.py +403 -0
  218. sigima/tests/signal/fitting_unit_test.py +929 -0
  219. sigima/tests/signal/fwhm_unit_test.py +111 -0
  220. sigima/tests/signal/noise_unit_test.py +128 -0
  221. sigima/tests/signal/offset_correction_unit_test.py +34 -0
  222. sigima/tests/signal/operation_unit_test.py +489 -0
  223. sigima/tests/signal/peakdetection_unit_test.py +145 -0
  224. sigima/tests/signal/processing_unit_test.py +657 -0
  225. sigima/tests/signal/pulse/__init__.py +112 -0
  226. sigima/tests/signal/pulse/crossing_times_unit_test.py +123 -0
  227. sigima/tests/signal/pulse/plateau_detection_unit_test.py +102 -0
  228. sigima/tests/signal/pulse/pulse_unit_test.py +1824 -0
  229. sigima/tests/signal/roi_advanced_unit_test.py +392 -0
  230. sigima/tests/signal/signalobj_unit_test.py +603 -0
  231. sigima/tests/signal/stability_unit_test.py +431 -0
  232. sigima/tests/signal/uncertainty_unit_test.py +611 -0
  233. sigima/tests/vistools.py +1030 -0
  234. sigima/tools/__init__.py +59 -0
  235. sigima/tools/checks.py +290 -0
  236. sigima/tools/coordinates.py +308 -0
  237. sigima/tools/datatypes.py +26 -0
  238. sigima/tools/image/__init__.py +97 -0
  239. sigima/tools/image/detection.py +451 -0
  240. sigima/tools/image/exposure.py +77 -0
  241. sigima/tools/image/extraction.py +48 -0
  242. sigima/tools/image/fourier.py +260 -0
  243. sigima/tools/image/geometry.py +190 -0
  244. sigima/tools/image/preprocessing.py +165 -0
  245. sigima/tools/signal/__init__.py +86 -0
  246. sigima/tools/signal/dynamic.py +254 -0
  247. sigima/tools/signal/features.py +135 -0
  248. sigima/tools/signal/filtering.py +171 -0
  249. sigima/tools/signal/fitting.py +1171 -0
  250. sigima/tools/signal/fourier.py +466 -0
  251. sigima/tools/signal/interpolation.py +70 -0
  252. sigima/tools/signal/peakdetection.py +126 -0
  253. sigima/tools/signal/pulse.py +1626 -0
  254. sigima/tools/signal/scaling.py +50 -0
  255. sigima/tools/signal/stability.py +258 -0
  256. sigima/tools/signal/windowing.py +90 -0
  257. sigima/worker.py +79 -0
  258. sigima-1.0.0.dist-info/METADATA +233 -0
  259. sigima-1.0.0.dist-info/RECORD +262 -0
  260. {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/licenses/LICENSE +29 -29
  261. sigima-0.0.1.dev0.dist-info/METADATA +0 -60
  262. sigima-0.0.1.dev0.dist-info/RECORD +0 -6
  263. {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/WHEEL +0 -0
  264. {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,88 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ Image objects subpackage
5
+ ========================
6
+
7
+ This subpackage provides image data structures and utilities.
8
+
9
+ The subpackage is organized into the following modules:
10
+
11
+ - `roi`: Region of Interest (ROI) classes and parameters
12
+ - `object`: Main ImageObj class for handling 2D image data
13
+ - `creation`: Image creation utilities and parameter classes
14
+
15
+ All classes and functions are re-exported at the subpackage level for backward
16
+ compatibility. Existing imports like `from sigima.objects.image import ImageObj`
17
+ will continue to work.
18
+ """
19
+
20
+ # Import all public classes and functions from submodules
21
+ from .creation import (
22
+ # Constants
23
+ DEFAULT_TITLE,
24
+ Gauss2DParam,
25
+ # Enums
26
+ ImageDatatypes,
27
+ ImageTypes,
28
+ # Base parameter classes
29
+ NewImageParam,
30
+ NormalDistribution2DParam,
31
+ PoissonDistribution2DParam,
32
+ Ramp2DParam,
33
+ UniformDistribution2DParam,
34
+ # Specific parameter classes
35
+ Zero2DParam,
36
+ check_all_image_parameters_classes,
37
+ # Core creation function
38
+ create_image,
39
+ create_image_from_param,
40
+ # Factory and utility functions
41
+ create_image_parameters,
42
+ get_next_image_number,
43
+ # Registration functions
44
+ register_image_parameters_class,
45
+ )
46
+ from .object import (
47
+ ImageObj,
48
+ )
49
+ from .roi import (
50
+ # ROI classes
51
+ BaseSingleImageROI,
52
+ CircularROI,
53
+ # Specific ROI types
54
+ ImageROI,
55
+ PolygonalROI,
56
+ RectangularROI,
57
+ ROI2DParam,
58
+ # ROI utility function
59
+ create_image_roi,
60
+ )
61
+
62
+ # Define __all__ for explicit public API
63
+ __all__ = [
64
+ "DEFAULT_TITLE",
65
+ "BaseSingleImageROI",
66
+ "CircularROI",
67
+ "Gauss2DParam",
68
+ "ImageDatatypes",
69
+ "ImageObj",
70
+ "ImageROI",
71
+ "ImageTypes",
72
+ "NewImageParam",
73
+ "NormalDistribution2DParam",
74
+ "PoissonDistribution2DParam",
75
+ "PolygonalROI",
76
+ "ROI2DParam",
77
+ "Ramp2DParam",
78
+ "RectangularROI",
79
+ "UniformDistribution2DParam",
80
+ "Zero2DParam",
81
+ "check_all_image_parameters_classes",
82
+ "create_image",
83
+ "create_image_from_param",
84
+ "create_image_parameters",
85
+ "create_image_roi",
86
+ "get_next_image_number",
87
+ "register_image_parameters_class",
88
+ ]
@@ -0,0 +1,556 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ Image creation utilities
5
+ ========================
6
+
7
+ This module provides functions and parameter classes for creating new images.
8
+
9
+ The module includes:
10
+
11
+ - `create_image`: Factory function for creating ImageObj instances
12
+ - `ImageDatatypes`: Enumeration of supported image data types
13
+ - `ImageTypes`: Enumeration of supported image generation types
14
+ - `NewImageParam` and subclasses: Parameter classes for image generation
15
+ - Factory functions and registration utilities
16
+
17
+ These utilities support creating images from various sources:
18
+ - Raw NumPy arrays
19
+ - Synthetic data (zeros, random distributions, analytical functions)
20
+ - Parameterized image generation
21
+ """
22
+
23
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
24
+ # pylint: disable=duplicate-code
25
+
26
+ from __future__ import annotations
27
+
28
+ from typing import Type
29
+
30
+ import guidata.dataset as gds
31
+ import numpy as np
32
+
33
+ from sigima.config import _
34
+ from sigima.objects import base
35
+ from sigima.objects.image.object import ImageObj
36
+ from sigima.tools.image import scale_data_to_min_max
37
+
38
+
39
+ def create_image(
40
+ title: str,
41
+ data: np.ndarray | None = None,
42
+ metadata: dict | None = None,
43
+ units: tuple | None = None,
44
+ labels: tuple | None = None,
45
+ ) -> ImageObj:
46
+ """Create a new Image object
47
+
48
+ Args:
49
+ title: image title
50
+ data: image data
51
+ metadata: image metadata
52
+ units: X, Y, Z units (tuple of strings)
53
+ labels: X, Y, Z labels (tuple of strings)
54
+
55
+ Returns:
56
+ Image object
57
+ """
58
+ assert isinstance(title, str)
59
+ assert data is None or isinstance(data, np.ndarray)
60
+ image = ImageObj(title=title)
61
+ image.title = title
62
+ image.data = data
63
+ if units is not None:
64
+ image.xunit, image.yunit, image.zunit = units
65
+ if labels is not None:
66
+ image.xlabel, image.ylabel, image.zlabel = labels
67
+ if metadata is not None:
68
+ image.metadata.update(metadata)
69
+ return image
70
+
71
+
72
+ class ImageDatatypes(gds.LabeledEnum):
73
+ """Image data types"""
74
+
75
+ @classmethod
76
+ def from_numpy_dtype(cls: type[ImageDatatypes], dtype: np.dtype) -> ImageDatatypes:
77
+ """Return ImageDatatypes member from NumPy dtype
78
+
79
+ Args:
80
+ dtype: NumPy dtype object
81
+
82
+ Returns:
83
+ Corresponding ImageDatatypes member
84
+ """
85
+ dtype_str = str(dtype)
86
+ for member in cls:
87
+ if member.value == dtype_str:
88
+ return member
89
+ return cls.UINT8 # Default fallback
90
+
91
+ def to_numpy_dtype(self) -> np.dtype:
92
+ """Return the corresponding NumPy dtype object.
93
+
94
+ This is the symmetrical counterpart to from_numpy_dtype().
95
+
96
+ Returns:
97
+ NumPy dtype object that can be used directly with numpy functions.
98
+ """
99
+ return np.dtype(self.value)
100
+
101
+ @classmethod
102
+ def check(cls: type[ImageDatatypes]) -> None:
103
+ """Check if data types are valid"""
104
+ for member in cls:
105
+ assert hasattr(np, member.value)
106
+
107
+ #: Unsigned integer number stored with 8 bits
108
+ UINT8 = "uint8"
109
+ #: Unsigned integer number stored with 16 bits
110
+ UINT16 = "uint16"
111
+ #: Signed integer number stored with 16 bits
112
+ INT16 = "int16"
113
+ #: Float number stored with 32 bits
114
+ FLOAT32 = "float32"
115
+ #: Float number stored with 64 bits
116
+ FLOAT64 = "float64"
117
+
118
+
119
+ ImageDatatypes.check()
120
+
121
+
122
+ class ImageTypes(gds.LabeledEnum):
123
+ """Image types."""
124
+
125
+ #: Image filled with zero
126
+ ZEROS = "zero", _("Zero")
127
+ #: Image filled with random data (normal distribution)
128
+ NORMAL_DISTRIBUTION = "normal_distribution", _("Normal distribution")
129
+ #: Image filled with random data (Poisson distribution)
130
+ POISSON_DISTRIBUTION = "poisson_distribution", _("Poisson distribution")
131
+ #: Image filled with random data (uniform distribution)
132
+ UNIFORM_DISTRIBUTION = "uniform_distribution", _("Uniform distribution")
133
+ #: 2D Gaussian image
134
+ GAUSS = "gauss", _("Gaussian")
135
+ #: Bilinear form image
136
+ RAMP = "ramp", _("2D ramp")
137
+
138
+
139
+ DEFAULT_TITLE = _("Untitled image")
140
+
141
+
142
+ class NewImageParam(gds.DataSet):
143
+ """New image dataset.
144
+
145
+ Subclasses can optionally implement a ``generate_title()`` method to provide
146
+ automatic title generation based on their parameters. This method should return
147
+ a string containing the generated title, or an empty string if no title can be
148
+ generated.
149
+
150
+ Example::
151
+
152
+ def generate_title(self) -> str:
153
+ '''Generate a title based on current parameters.'''
154
+ return f"MyImage(param1={self.param1},param2={self.param2})"
155
+ """
156
+
157
+ hide_height = False
158
+ hide_width = False
159
+ hide_dtype = False
160
+ hide_type = False
161
+
162
+ title = gds.StringItem(_("Title"), default=DEFAULT_TITLE)
163
+ height = gds.IntItem(
164
+ _("Height"), default=1024, help=_("Image height: number of rows"), min=1
165
+ ).set_prop("display", hide=gds.GetAttrProp("hide_height"))
166
+ width = gds.IntItem(
167
+ _("Width"), default=1024, help=_("Image width: number of columns"), min=1
168
+ ).set_prop("display", col=1, hide=gds.GetAttrProp("hide_width"))
169
+ dtype = gds.ChoiceItem(
170
+ _("Type"),
171
+ ImageDatatypes,
172
+ default=ImageDatatypes.FLOAT64,
173
+ help=_("Image data type"),
174
+ ).set_prop("display", hide=gds.GetAttrProp("hide_dtype"))
175
+ xlabel = gds.StringItem(_("X label"), default="")
176
+ xunit = gds.StringItem(_("X unit"), default="").set_prop("display", col=1)
177
+ ylabel = gds.StringItem(_("Y label"), default="")
178
+ yunit = gds.StringItem(_("Y unit"), default="").set_prop("display", col=1)
179
+ zlabel = gds.StringItem(_("Z label"), default="")
180
+ zunit = gds.StringItem(_("Z unit"), default="").set_prop("display", col=1)
181
+
182
+ def generate_2d_data(self, shape: tuple[int, int]) -> np.ndarray:
183
+ """Generate 2D data based on current parameters.
184
+
185
+ Args:
186
+ shape: Tuple (height, width) for the output array.
187
+
188
+ Returns:
189
+ 2D data array
190
+ """
191
+ return np.zeros(shape, dtype=self.dtype.to_numpy_dtype())
192
+
193
+
194
+ IMAGE_TYPE_PARAM_CLASSES = {}
195
+
196
+
197
+ def register_image_parameters_class(itype: ImageTypes, param_class) -> None:
198
+ """Register a parameters class for a given image type.
199
+
200
+ Args:
201
+ itype: image type
202
+ param_class: parameters class
203
+ """
204
+ IMAGE_TYPE_PARAM_CLASSES[itype] = param_class
205
+
206
+
207
+ def __get_image_parameters_class(itype: ImageTypes) -> Type[NewImageParam]:
208
+ """Get parameters class for a given image type.
209
+
210
+ Args:
211
+ itype: image type
212
+
213
+ Returns:
214
+ Parameters class
215
+
216
+ Raises:
217
+ ValueError: if no parameters class is registered for the given image type
218
+ """
219
+ try:
220
+ return IMAGE_TYPE_PARAM_CLASSES[itype]
221
+ except KeyError as exc:
222
+ raise ValueError(
223
+ f"Image type {itype} has no parameters class registered"
224
+ ) from exc
225
+
226
+
227
+ def check_all_image_parameters_classes() -> None:
228
+ """Check all registered parameters classes."""
229
+ for itype, param_class in IMAGE_TYPE_PARAM_CLASSES.items():
230
+ assert __get_image_parameters_class(itype) is param_class
231
+
232
+
233
+ def create_image_parameters(
234
+ itype: ImageTypes,
235
+ title: str | None = None,
236
+ height: int | None = None,
237
+ width: int | None = None,
238
+ idtype: ImageDatatypes | None = None,
239
+ xlabel: str | None = None,
240
+ ylabel: str | None = None,
241
+ zlabel: str | None = None,
242
+ xunit: str | None = None,
243
+ yunit: str | None = None,
244
+ zunit: str | None = None,
245
+ **kwargs: dict,
246
+ ) -> NewImageParam:
247
+ """Create parameters for a given image type.
248
+
249
+ Args:
250
+ itype: image type
251
+ title: image title
252
+ height: image height (number of rows)
253
+ width: image width (number of columns)
254
+ idtype: image data type (`ImageDatatypes` member)
255
+ xlabel: X axis label
256
+ ylabel: Y axis label
257
+ zlabel: Z axis label
258
+ xunit: X axis unit
259
+ yunit: Y axis unit
260
+ zunit: Z axis unit
261
+ **kwargs: additional parameters (specific to the image type)
262
+
263
+ Returns:
264
+ Parameters object for the given image type
265
+ """
266
+ pclass = __get_image_parameters_class(itype)
267
+ p = pclass.create(**kwargs)
268
+ if title is not None:
269
+ p.title = title
270
+ if height is not None:
271
+ p.height = height
272
+ if width is not None:
273
+ p.width = width
274
+ if idtype is not None:
275
+ assert isinstance(idtype, ImageDatatypes)
276
+ p.dtype = idtype
277
+ if xlabel is not None:
278
+ p.xlabel = xlabel
279
+ if ylabel is not None:
280
+ p.ylabel = ylabel
281
+ if zlabel is not None:
282
+ p.zlabel = zlabel
283
+ if xunit is not None:
284
+ p.xunit = xunit
285
+ if yunit is not None:
286
+ p.yunit = yunit
287
+ if zunit is not None:
288
+ p.zunit = zunit
289
+ return p
290
+
291
+
292
+ class Zero2DParam(NewImageParam, title=_("Zero")):
293
+ """Image parameters for a 2D image filled with zero."""
294
+
295
+ def generate_2d_data(self, shape: tuple[int, int]) -> np.ndarray:
296
+ """Generate 2D data based on current parameters.
297
+
298
+ Args:
299
+ shape: Tuple (height, width) for the output array.
300
+
301
+ Returns:
302
+ 2D data array
303
+ """
304
+ return np.zeros(shape, dtype=self.dtype.to_numpy_dtype())
305
+
306
+
307
+ register_image_parameters_class(ImageTypes.ZEROS, Zero2DParam)
308
+
309
+
310
+ class UniformDistribution2DParam(
311
+ NewImageParam, base.UniformDistributionParam, title=_("Uniform distribution")
312
+ ):
313
+ """Uniform-distribution image parameters."""
314
+
315
+ def generate_2d_data(self, shape: tuple[int, int]) -> np.ndarray:
316
+ """Generate 2D data based on current parameters.
317
+
318
+ Args:
319
+ shape: Tuple (height, width) for the output array.
320
+
321
+ Returns:
322
+ 2D data array
323
+ """
324
+ rng = np.random.default_rng(self.seed)
325
+ assert self.vmin is not None
326
+ assert self.vmax is not None
327
+ data = scale_data_to_min_max(rng.random(shape), self.vmin, self.vmax)
328
+ return data.astype(self.dtype.to_numpy_dtype())
329
+
330
+
331
+ register_image_parameters_class(
332
+ ImageTypes.UNIFORM_DISTRIBUTION, UniformDistribution2DParam
333
+ )
334
+
335
+
336
+ class NormalDistribution2DParam(
337
+ NewImageParam, base.NormalDistributionParam, title=_("Normal distribution")
338
+ ):
339
+ """Normal-distribution image parameters."""
340
+
341
+ def generate_2d_data(self, shape: tuple[int, int]) -> np.ndarray:
342
+ """Generate 2D data based on current parameters.
343
+
344
+ Args:
345
+ shape: Tuple (height, width) for the output array.
346
+
347
+ Returns:
348
+ 2D data array.
349
+ """
350
+ rng = np.random.default_rng(self.seed)
351
+ assert self.mu is not None
352
+ assert self.sigma is not None
353
+ data: np.ndarray = rng.normal(self.mu, self.sigma, shape)
354
+ return data.astype(self.dtype.to_numpy_dtype())
355
+
356
+
357
+ register_image_parameters_class(
358
+ ImageTypes.NORMAL_DISTRIBUTION, NormalDistribution2DParam
359
+ )
360
+
361
+
362
+ class PoissonDistribution2DParam(
363
+ NewImageParam, base.PoissonDistributionParam, title=_("Poisson distribution")
364
+ ):
365
+ """Poisson-distribution image parameters."""
366
+
367
+ def generate_2d_data(self, shape: tuple[int, int]) -> np.ndarray:
368
+ """Generate 2D data based on current parameters.
369
+
370
+ Args:
371
+ shape: Tuple (height, width) for the output array.
372
+
373
+ Returns:
374
+ 2D data array.
375
+ """
376
+ rng = np.random.default_rng(self.seed)
377
+ assert self.lam is not None
378
+ data: np.ndarray = rng.poisson(lam=self.lam, size=shape)
379
+ return data.astype(self.dtype.to_numpy_dtype())
380
+
381
+
382
+ register_image_parameters_class(
383
+ ImageTypes.POISSON_DISTRIBUTION, PoissonDistribution2DParam
384
+ )
385
+
386
+
387
+ class Gauss2DParam(
388
+ NewImageParam,
389
+ title=_("Gaussian"),
390
+ comment="z = A exp(-((√((x - x<sub>0</sub>)<sup>2</sup> + "
391
+ "(y - y<sub>0</sub>)<sup>2</sup>) - μ)<sup>2</sup>) / (2 σ<sup>2</sup>))",
392
+ ):
393
+ """2D Gaussian parameters."""
394
+
395
+ a = gds.FloatItem("A", default=None, check=False)
396
+ xmin = gds.FloatItem("x<sub>min</sub>", default=-10.0).set_pos(col=1)
397
+ sigma = gds.FloatItem("σ", default=1.0)
398
+ xmax = gds.FloatItem("x<sub>max</sub>", default=10.0).set_pos(col=1)
399
+ mu = gds.FloatItem("μ", default=0.0)
400
+ ymin = gds.FloatItem("y<sub>min</sub>", default=-10.0).set_pos(col=1)
401
+ x0 = gds.FloatItem("x<sub>0</sub>", default=0.0)
402
+ ymax = gds.FloatItem("y<sub>max</sub>", default=10.0).set_pos(col=1)
403
+ y0 = gds.FloatItem("y<sub>0</sub>", default=0.0).set_pos(col=0, colspan=1)
404
+
405
+ def generate_title(self) -> str:
406
+ """Generate a title based on current parameters."""
407
+ return (
408
+ f"Gauss(a={self.a:g},μ={self.mu:g},"
409
+ f"σ={self.sigma:g}),x0={self.x0:g},y0={self.y0:g})"
410
+ )
411
+
412
+ def generate_2d_data(self, shape: tuple[int, int]) -> np.ndarray:
413
+ """Generate 2D data based on current parameters.
414
+
415
+ Args:
416
+ shape: Tuple (height, width) for the output array.
417
+
418
+ Returns:
419
+ 2D data array
420
+ """
421
+ if self.a is None:
422
+ try:
423
+ self.a = np.iinfo(self.dtype.to_numpy_dtype()).max / 2.0
424
+ except ValueError:
425
+ self.a = 10.0
426
+ x, y = np.meshgrid(
427
+ np.linspace(self.xmin, self.xmax, shape[1]),
428
+ np.linspace(self.ymin, self.ymax, shape[0]),
429
+ )
430
+ data = self.a * np.exp(
431
+ -((np.sqrt((x - self.x0) ** 2 + (y - self.y0) ** 2) - self.mu) ** 2)
432
+ / (2.0 * self.sigma**2)
433
+ )
434
+ return np.array(data, dtype=self.dtype.to_numpy_dtype())
435
+
436
+
437
+ register_image_parameters_class(ImageTypes.GAUSS, Gauss2DParam)
438
+
439
+
440
+ class Ramp2DParam(
441
+ NewImageParam,
442
+ title=_("2D ramp"),
443
+ comment="z = A (x - x<sub>0</sub>) + B (y - y<sub>0</sub>) + C",
444
+ ):
445
+ """Define the parameters of a 2D ramp (planar ramp)."""
446
+
447
+ _g0_begin = gds.BeginGroup(_("Coefficients"))
448
+ a = gds.FloatItem("A", default=1.0).set_pos(col=0)
449
+ b = gds.FloatItem("B", default=1.0).set_pos(col=1)
450
+ c = gds.FloatItem("C", default=0.0).set_pos(colspan=1)
451
+ x0 = gds.FloatItem("x<sub>0</sub>", default=0.0).set_pos(col=0)
452
+ y0 = gds.FloatItem("y<sub>0</sub>", default=0.0).set_pos(col=1)
453
+ _g0_end = gds.EndGroup("")
454
+ _g1_begin = gds.BeginGroup(_("Domain"))
455
+ xmin = gds.FloatItem("x<sub>min</sub>", default=-1.0).set_pos(col=0)
456
+ xmax = gds.FloatItem("x<sub>max</sub>", default=1.0).set_pos(col=1)
457
+ ymin = gds.FloatItem("y<sub>min</sub>", default=-1.0).set_pos(col=0)
458
+ ymax = gds.FloatItem("y<sub>max</sub>", default=1.0).set_pos(col=1)
459
+ _g1_end = gds.EndGroup("")
460
+
461
+ def generate_title(self) -> str:
462
+ """Generate a title based on current parameters."""
463
+ terms = [] # Build terms list for non-zero coefficients
464
+ if self.a != 0.0:
465
+ if self.x0 == 0.0:
466
+ x_part = f"{self.a:g} x"
467
+ else:
468
+ x_part = f"{self.a:g} (x - {self.x0:g})"
469
+ terms.append(x_part)
470
+ if self.b != 0.0:
471
+ if self.y0 == 0.0:
472
+ y_part = f"{self.b:g} y"
473
+ else:
474
+ y_part = f"{self.b:g} (y - {self.y0:g})"
475
+ terms.append(y_part)
476
+ if self.c != 0.0 or not terms: # Include c if it's the only term
477
+ terms.append(f"{self.c:g}")
478
+ return f"z = {' + '.join(terms)}"
479
+
480
+ def generate_2d_data(self, shape: tuple[int, int]) -> np.ndarray:
481
+ """Generate 2D data based on current parameters.
482
+
483
+ Args:
484
+ shape: Tuple (height, width) for the output array.
485
+
486
+ Returns:
487
+ 2D data array
488
+ """
489
+ x = np.linspace(self.xmin, self.xmax, shape[1])
490
+ y = np.linspace(self.ymin, self.ymax, shape[0])
491
+ xx, yy = np.meshgrid(x, y)
492
+ data = self.a * (xx - self.x0) + self.b * (yy - self.y0) + self.c
493
+ return np.array(data, dtype=self.dtype.to_numpy_dtype())
494
+
495
+
496
+ register_image_parameters_class(ImageTypes.RAMP, Ramp2DParam)
497
+ check_all_image_parameters_classes()
498
+
499
+ IMG_NB = 0
500
+
501
+
502
+ def get_next_image_number():
503
+ """Get the next image number.
504
+
505
+ This function is used to keep track of the number of signals created.
506
+ It is typically used to generate unique titles for new signals.
507
+
508
+ Returns:
509
+ int: new image number
510
+ """
511
+ global IMG_NB # pylint: disable=global-statement
512
+ IMG_NB += 1
513
+ return IMG_NB
514
+
515
+
516
+ def create_image_from_param(param: NewImageParam) -> ImageObj:
517
+ """Create a new Image object from parameters.
518
+
519
+ Args:
520
+ param: new image parameters
521
+
522
+ Returns:
523
+ Image object
524
+
525
+ Raises:
526
+ NotImplementedError: if the image type is not supported
527
+ """
528
+ if param.height is None:
529
+ param.height = 1024
530
+ if param.width is None:
531
+ param.width = 1024
532
+ if param.dtype is None:
533
+ param.dtype = ImageDatatypes.UINT16
534
+ # Generate data first, as some `generate_title()` methods may depend on it:
535
+ shape = (param.height, param.width)
536
+ data = param.generate_2d_data(shape)
537
+ # Check if user has customized the title or left it as default/empty
538
+ use_generated_title = not param.title or param.title == DEFAULT_TITLE
539
+ if use_generated_title:
540
+ # Try to generate a descriptive title
541
+ gen_title = getattr(param, "generate_title", lambda: "")()
542
+ if gen_title:
543
+ title = gen_title
544
+ else:
545
+ # No generated title available, use default with number
546
+ title = f"{DEFAULT_TITLE} {get_next_image_number()}"
547
+ else:
548
+ # User has set a custom title, use it as-is
549
+ title = param.title
550
+ image = create_image(
551
+ title,
552
+ data,
553
+ units=(param.xunit, param.yunit, param.zunit),
554
+ labels=(param.xlabel, param.ylabel, param.zlabel),
555
+ )
556
+ return image