microeye 2.3.2__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 (190) hide show
  1. microEye/__init__.py +47 -0
  2. microEye/_version.py +2 -0
  3. microEye/analysis/__init__.py +1 -0
  4. microEye/analysis/checklist_dialog.py +143 -0
  5. microEye/analysis/cmosMaps.py +228 -0
  6. microEye/analysis/filters/__init__.py +9 -0
  7. microEye/analysis/filters/base.py +21 -0
  8. microEye/analysis/filters/spatial.py +338 -0
  9. microEye/analysis/filters/temporal.py +76 -0
  10. microEye/analysis/fitting/__init__.py +0 -0
  11. microEye/analysis/fitting/fit.py +680 -0
  12. microEye/analysis/fitting/nena.py +375 -0
  13. microEye/analysis/fitting/phasor_fit.py +90 -0
  14. microEye/analysis/fitting/processing.py +317 -0
  15. microEye/analysis/fitting/psf/__init__.py +6 -0
  16. microEye/analysis/fitting/psf/extract.py +1129 -0
  17. microEye/analysis/fitting/psf/rubost_mean.py +150 -0
  18. microEye/analysis/fitting/psf/spline.py +167 -0
  19. microEye/analysis/fitting/psf/stats/__init__.py +12 -0
  20. microEye/analysis/fitting/psf/stats/core.py +295 -0
  21. microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
  22. microEye/analysis/fitting/psf/stats/io.py +104 -0
  23. microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
  24. microEye/analysis/fitting/psf/temp.py +147 -0
  25. microEye/analysis/fitting/psf/test.py +47 -0
  26. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
  27. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
  28. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
  29. microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
  30. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
  31. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
  32. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
  33. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
  34. microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
  35. microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
  36. microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
  37. microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
  38. microEye/analysis/fitting/results.py +917 -0
  39. microEye/analysis/fitting/results_stats.py +251 -0
  40. microEye/analysis/fitting/tardis.py +209 -0
  41. microEye/analysis/multi_viewer.py +568 -0
  42. microEye/analysis/processing/__init__.py +1 -0
  43. microEye/analysis/processing/frc.py +120 -0
  44. microEye/analysis/rendering/__init__.py +3 -0
  45. microEye/analysis/rendering/base.py +452 -0
  46. microEye/analysis/rendering/cloud.py +215 -0
  47. microEye/analysis/rendering/core.py +47 -0
  48. microEye/analysis/rendering/volumetric.py +198 -0
  49. microEye/analysis/tools/__init__.py +0 -0
  50. microEye/analysis/tools/kymograms.py +1079 -0
  51. microEye/analysis/tools/roi_selectors.py +556 -0
  52. microEye/analysis/utils/__init__.py +3 -0
  53. microEye/analysis/utils/coordinates.py +18 -0
  54. microEye/analysis/utils/images.py +68 -0
  55. microEye/analysis/utils/windows.py +19 -0
  56. microEye/analysis/viewer/__init__.py +3 -0
  57. microEye/analysis/viewer/image_options_widget.py +615 -0
  58. microEye/analysis/viewer/images.py +1225 -0
  59. microEye/analysis/viewer/layers_widget.py +415 -0
  60. microEye/analysis/viewer/localizations.py +1304 -0
  61. microEye/analysis/viewer/psf.py +1003 -0
  62. microEye/analysis/viewer/volume.py +456 -0
  63. microEye/hardware/__init__.py +3 -0
  64. microEye/hardware/cams/__init__.py +29 -0
  65. microEye/hardware/cams/camera_calibration.py +99 -0
  66. microEye/hardware/cams/camera_list.py +528 -0
  67. microEye/hardware/cams/camera_options.py +694 -0
  68. microEye/hardware/cams/camera_panel.py +941 -0
  69. microEye/hardware/cams/dummy/__init__.py +1 -0
  70. microEye/hardware/cams/dummy/dummy_panel.py +759 -0
  71. microEye/hardware/cams/jobs.py +497 -0
  72. microEye/hardware/cams/line_profiler.py +99 -0
  73. microEye/hardware/cams/linescan/IR_Cam.py +474 -0
  74. microEye/hardware/cams/linescan/__init__.py +1 -0
  75. microEye/hardware/cams/micam.py +524 -0
  76. microEye/hardware/cams/pco/__init__.py +58 -0
  77. microEye/hardware/cams/pco/enums.py +382 -0
  78. microEye/hardware/cams/pco/pco_cam.py +761 -0
  79. microEye/hardware/cams/pco/pco_panel.py +477 -0
  80. microEye/hardware/cams/shortcuts.py +312 -0
  81. microEye/hardware/cams/thorlabs/__init__.py +1 -0
  82. microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
  83. microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
  84. microEye/hardware/cams/ueye/__init__.py +1 -0
  85. microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
  86. microEye/hardware/cams/ueye/ueye_panel.py +861 -0
  87. microEye/hardware/cams/vimba/__init__.py +1 -0
  88. microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
  89. microEye/hardware/cams/vimba/vimba_panel.py +813 -0
  90. microEye/hardware/device.py +60 -0
  91. microEye/hardware/lasers/__init__.py +13 -0
  92. microEye/hardware/lasers/io_matchbox.py +791 -0
  93. microEye/hardware/lasers/io_params.py +85 -0
  94. microEye/hardware/lasers/io_single_laser.py +742 -0
  95. microEye/hardware/lasers/laser_relay.py +594 -0
  96. microEye/hardware/mieye/__init__.py +1 -0
  97. microEye/hardware/mieye/acquisition_manager.py +467 -0
  98. microEye/hardware/mieye/devices_manager.py +533 -0
  99. microEye/hardware/mieye/miEye.py +659 -0
  100. microEye/hardware/misc/__init__.py +0 -0
  101. microEye/hardware/misc/acquisition_view.py +71 -0
  102. microEye/hardware/misc/reglo.py +761 -0
  103. microEye/hardware/misc/temp.py +188 -0
  104. microEye/hardware/port_config.py +59 -0
  105. microEye/hardware/protocols/__init__.py +2 -0
  106. microEye/hardware/protocols/actions.py +402 -0
  107. microEye/hardware/protocols/actions_items.py +703 -0
  108. microEye/hardware/protocols/designer.py +244 -0
  109. microEye/hardware/protocols/scene_manager.py +191 -0
  110. microEye/hardware/protocols/serialization.py +97 -0
  111. microEye/hardware/pycromanager/__init__.py +16 -0
  112. microEye/hardware/pycromanager/core.py +1433 -0
  113. microEye/hardware/pycromanager/devices.py +461 -0
  114. microEye/hardware/pycromanager/enums.py +107 -0
  115. microEye/hardware/pycromanager/headless.py +153 -0
  116. microEye/hardware/pycromanager/utils.py +34 -0
  117. microEye/hardware/pycromanager/widgets/__init__.py +5 -0
  118. microEye/hardware/pycromanager/widgets/bridges.py +407 -0
  119. microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
  120. microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
  121. microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
  122. microEye/hardware/stages/__init__.py +18 -0
  123. microEye/hardware/stages/elliptec/__init__.py +5 -0
  124. microEye/hardware/stages/elliptec/baseDevice.py +314 -0
  125. microEye/hardware/stages/elliptec/device.py +384 -0
  126. microEye/hardware/stages/elliptec/deviceID.py +212 -0
  127. microEye/hardware/stages/elliptec/devicePort.py +379 -0
  128. microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
  129. microEye/hardware/stages/elliptec/devicesView.py +706 -0
  130. microEye/hardware/stages/elliptec/ellDevices.py +134 -0
  131. microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
  132. microEye/hardware/stages/elliptec/motorInfo.py +153 -0
  133. microEye/hardware/stages/elliptec/stage.py +62 -0
  134. microEye/hardware/stages/elliptec/test.py +139 -0
  135. microEye/hardware/stages/kinesis/__init__.py +1 -0
  136. microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
  137. microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
  138. microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
  139. microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
  140. microEye/hardware/stages/kinesis/kinesis.py +776 -0
  141. microEye/hardware/stages/piezo_concept.py +607 -0
  142. microEye/hardware/stages/stabilizer.py +785 -0
  143. microEye/hardware/stages/stage.py +89 -0
  144. microEye/hardware/widgets/__init__.py +10 -0
  145. microEye/hardware/widgets/controller.py +246 -0
  146. microEye/hardware/widgets/devices.py +133 -0
  147. microEye/hardware/widgets/focusWidget.py +264 -0
  148. microEye/hardware/widgets/qlist_slider.py +113 -0
  149. microEye/hardware/widgets/scan_acquisition.py +424 -0
  150. microEye/icons/1024.png +0 -0
  151. microEye/icons/128.png +0 -0
  152. microEye/icons/16.png +0 -0
  153. microEye/icons/24.png +0 -0
  154. microEye/icons/256.png +0 -0
  155. microEye/icons/32.png +0 -0
  156. microEye/icons/48.png +0 -0
  157. microEye/icons/512.png +0 -0
  158. microEye/icons/64.png +0 -0
  159. microEye/icons/__init__.py +0 -0
  160. microEye/icons/close.svg +88 -0
  161. microEye/icons/mieye.png +0 -0
  162. microEye/icons/min.svg +83 -0
  163. microEye/icons/viewer.png +0 -0
  164. microEye/launcher.py +42 -0
  165. microEye/qt.py +181 -0
  166. microEye/utils/__init__.py +2 -0
  167. microEye/utils/enum_encoder.py +10 -0
  168. microEye/utils/expandable_groupbox.py +93 -0
  169. microEye/utils/gui_helper.py +457 -0
  170. microEye/utils/hid/__init__.py +8 -0
  171. microEye/utils/hid/controller.py +153 -0
  172. microEye/utils/hid/device.py +63 -0
  173. microEye/utils/hid/enums.py +140 -0
  174. microEye/utils/hid/utils.py +58 -0
  175. microEye/utils/labelled_slider.py +134 -0
  176. microEye/utils/metadata.py +599 -0
  177. microEye/utils/metadata_tree.py +718 -0
  178. microEye/utils/micro_launcher.py +245 -0
  179. microEye/utils/parameter_tree.py +325 -0
  180. microEye/utils/pyscripting.py +444 -0
  181. microEye/utils/retry_exec.py +35 -0
  182. microEye/utils/start_gui.py +112 -0
  183. microEye/utils/thread_worker.py +115 -0
  184. microEye/utils/uImage.py +1512 -0
  185. microeye-2.3.2.dist-info/METADATA +401 -0
  186. microeye-2.3.2.dist-info/RECORD +190 -0
  187. microeye-2.3.2.dist-info/WHEEL +5 -0
  188. microeye-2.3.2.dist-info/entry_points.txt +2 -0
  189. microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
  190. microeye-2.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,615 @@
1
+ import json
2
+ from enum import Enum
3
+ from typing import Optional
4
+
5
+ from pyqtgraph.parametertree import Parameter, ParameterTree
6
+ from pyqtgraph.parametertree.parameterTypes import GroupParameter
7
+
8
+ from microEye.analysis.filters import (
9
+ BANDPASS_TYPES,
10
+ AbstractFilter,
11
+ BandpassFilter,
12
+ DoG_Filter,
13
+ PassFilter,
14
+ )
15
+ from microEye.analysis.fitting.fit import AbstractDetector, CV_BlobDetector
16
+ from microEye.analysis.fitting.results import FittingMethod
17
+ from microEye.qt import QApplication, QtWidgets, Signal, Slot
18
+ from microEye.utils import Tree
19
+
20
+ DETECTORS = {'OpenCV Blob Detector': CV_BlobDetector}
21
+
22
+ FILTERS = {
23
+ 'None': PassFilter,
24
+ 'Difference of Gaussians': DoG_Filter,
25
+ 'Fourier Bandpass Filter': BandpassFilter,
26
+ }
27
+
28
+
29
+ class Parameters(Enum):
30
+ AUTO_STRETCH = 'Options.Auto-Stretch'
31
+ CHANNEL = 'Options.Channel'
32
+ ENABLE_ROI = 'Options.Enable ROI'
33
+ ROI_X = 'Options.ROI.x'
34
+ ROI_Y = 'Options.ROI.y'
35
+ ROI_WIDTH = 'Options.ROI.width'
36
+ ROI_HEIGHT = 'Options.ROI.height'
37
+ REALTIME_LOCALIZATION = 'Options.Realtime Localization'
38
+ SAVE_CROPPED_IMAGE = 'Options.Save Cropped Image'
39
+ TM_FILTER_ENABLED = 'Filters.Temporal Median Filter.Filter Enabled'
40
+ TM_FILTER_WINDOW_SIZE = 'Filters.Temporal Median Filter.Window Size'
41
+ FILTER = 'Filters.Filter'
42
+ SIGMA = 'Filters.Difference of Gaussians.Sigma'
43
+ FACTOR = 'Filters.Difference of Gaussians.Factor'
44
+ FILTER_TYPE = 'Filters.Fourier Bandpass Filter.Type'
45
+ CENTER = 'Filters.Fourier Bandpass Filter.Center'
46
+ WIDTH = 'Filters.Fourier Bandpass Filter.Width'
47
+ SHOW_FILTER = 'Filters.Fourier Bandpass Filter.Show Filter'
48
+ PREFIT_DETECTOR = 'Prefit.Prefit Detector'
49
+ MIN_AREA = 'Prefit.OpenCV Blob Detector.Min Area'
50
+ MAX_AREA = 'Prefit.OpenCV Blob Detector.Max Area'
51
+ RELATIVE_THRESHOLD_MIN = 'Prefit.Relative Threshold.min'
52
+ RELATIVE_THRESHOLD_MAX = 'Prefit.Relative Threshold.max'
53
+ FITTING_METHOD = 'Localization.Fitting Method'
54
+ ROI_SIZE = 'Localization.ROI Size'
55
+ PIXEL_SIZE = 'Localization.Pixel-size'
56
+ INITIAL_SIGMA = 'Localization.Initial Sigma'
57
+ LOCALIZE_GPU = 'Localization.GPU'
58
+ LOCALIZE = 'Localization.Localize'
59
+ FIELD_DEPENDENCY = 'PSF.Field Dependency'
60
+ FIELD_GRID_SIZE = 'PSF.Field Dependency.Grid Size'
61
+ FIELD_ENABLED = 'PSF.Field Dependency.Enabled'
62
+ ZERO_PLANE = 'PSF.Zero Plane'
63
+ Z0_ENABLED = 'PSF.Zero Plane.Enabled'
64
+ Z0_METHOD = 'PSF.Zero Plane.Method'
65
+ PSF_ZSTEP = 'PSF.Z Step'
66
+ PSF_UPSAMPLE = 'PSF.Upsample'
67
+ EXTRACT_PSF = 'PSF.Extract'
68
+ EXPORT_STATE = 'Settings.Export'
69
+ IMPORT_STATE = 'Settings.Import'
70
+
71
+
72
+ class FittingOptions(Tree):
73
+ saveCropped = Signal()
74
+ localizeData = Signal()
75
+ extractPSF = Signal()
76
+ paramsChanged = Signal()
77
+ roiEnabled = Signal()
78
+ roiChanged = Signal(tuple)
79
+
80
+ DETECTORS = list(DETECTORS.keys())
81
+ FILTERS = list(FILTERS.keys())
82
+ FITTING_METHODS = FittingMethod.get_strings()
83
+ BANDPASS_TYPES = BANDPASS_TYPES.values()
84
+
85
+ def __init__(
86
+ self,
87
+ shape: tuple = (1, 1, 1, 128, 128),
88
+ debug=False,
89
+ parent: Optional['QtWidgets.QWidget'] = None,
90
+ ):
91
+ self._debug = debug
92
+
93
+ if shape is None or len(shape) < 5:
94
+ raise ValueError('Shape must be a 5-tuple')
95
+
96
+ self._shape = shape
97
+
98
+ self._filters = {key: None for key in self.FILTERS}
99
+ self._detectors = {key: None for key in self.DETECTORS}
100
+
101
+ super().__init__(parent)
102
+
103
+ @property
104
+ def imageWidth(self):
105
+ length = len(self._shape)
106
+ return self._shape[length - 1]
107
+
108
+ @property
109
+ def imageHeight(self):
110
+ length = len(self._shape)
111
+ return self._shape[length - 2]
112
+
113
+ @property
114
+ def imageDepth(self):
115
+ length = len(self._shape)
116
+ return self._shape[length - 3]
117
+
118
+ @property
119
+ def imageChannels(self):
120
+ length = len(self._shape)
121
+ return self._shape[length - 4]
122
+
123
+ @property
124
+ def imageFrames(self):
125
+ return self._shape[0]
126
+
127
+ def create_parameters(self):
128
+ # Create an initial parameter tree with a Layers group
129
+ params = [
130
+ {
131
+ 'name': 'Options',
132
+ 'type': 'group',
133
+ 'children': [
134
+ {'name': 'Auto-Stretch', 'type': 'bool', 'value': True},
135
+ {'name': 'Enable ROI', 'type': 'bool', 'value': False},
136
+ {
137
+ 'name': 'ROI',
138
+ 'type': 'group',
139
+ 'children': [
140
+ {
141
+ 'name': 'x',
142
+ 'type': 'int',
143
+ 'value': 0,
144
+ 'limits': [0, self.imageWidth],
145
+ 'step': 1,
146
+ 'suffix': 'pixel',
147
+ },
148
+ {
149
+ 'name': 'y',
150
+ 'type': 'int',
151
+ 'value': 0,
152
+ 'limits': [0, self.imageHeight],
153
+ 'step': 1,
154
+ 'suffix': 'pixel',
155
+ },
156
+ {
157
+ 'name': 'width',
158
+ 'type': 'int',
159
+ 'value': self.imageWidth,
160
+ 'limits': [0, self.imageWidth],
161
+ 'step': 1,
162
+ 'suffix': 'pixel',
163
+ },
164
+ {
165
+ 'name': 'height',
166
+ 'type': 'int',
167
+ 'value': self.imageHeight,
168
+ 'limits': [0, self.imageHeight],
169
+ 'step': 1,
170
+ 'suffix': 'pixel',
171
+ },
172
+ ],
173
+ },
174
+ {'name': 'Realtime Localization', 'type': 'bool', 'value': False},
175
+ {'name': 'Save Cropped Image', 'type': 'action'},
176
+ ],
177
+ },
178
+ {
179
+ 'name': 'Filters',
180
+ 'type': 'group',
181
+ 'children': [
182
+ {
183
+ 'name': 'Temporal Median Filter',
184
+ 'type': 'group',
185
+ 'children': [
186
+ {'name': 'Filter Enabled', 'type': 'bool', 'value': False},
187
+ {
188
+ 'name': 'Window Size',
189
+ 'type': 'int',
190
+ 'value': 3,
191
+ 'limits': [1, 2048],
192
+ 'step': 1,
193
+ 'suffix': 'frame',
194
+ },
195
+ ],
196
+ },
197
+ {
198
+ 'name': 'Filter',
199
+ 'type': 'list',
200
+ 'limits': self.FILTERS,
201
+ 'value': self.FILTERS[0],
202
+ 'tip': 'Select the type of filter to apply',
203
+ },
204
+ {
205
+ 'name': self.FILTERS[0],
206
+ 'type': 'group',
207
+ 'children': [
208
+ {
209
+ 'name': 'Sigma',
210
+ 'type': 'float',
211
+ 'value': 1.0,
212
+ 'limits': [0.0, 100.0],
213
+ 'step': 0.1,
214
+ 'decimals': 2,
215
+ 'tip': 'Standard deviation (\u03c3) min for the filter',
216
+ },
217
+ {
218
+ 'name': 'Factor',
219
+ 'type': 'float',
220
+ 'value': 2.5,
221
+ 'limits': [0.0, 100.0],
222
+ 'step': 0.1,
223
+ 'decimals': 2,
224
+ 'tip': 'Ratio of max \u03c3 to min \u03c3.',
225
+ },
226
+ ],
227
+ },
228
+ {
229
+ 'name': self.FILTERS[1],
230
+ 'type': 'group',
231
+ 'children': [
232
+ {
233
+ 'name': 'Type',
234
+ 'type': 'list',
235
+ 'limits': self.BANDPASS_TYPES,
236
+ 'value': self.BANDPASS_TYPES[0],
237
+ 'tip': 'Select the type of bandpass filter',
238
+ },
239
+ {
240
+ 'name': 'Center',
241
+ 'type': 'float',
242
+ 'value': 40.0,
243
+ 'limits': [0.0, 2096.0],
244
+ 'step': 0.5,
245
+ 'decimals': 2,
246
+ 'tip': 'Center frequency in pixels.',
247
+ },
248
+ {
249
+ 'name': 'Width',
250
+ 'type': 'float',
251
+ 'value': 90.0,
252
+ 'limits': [0.0, 2096.0],
253
+ 'step': 0.5,
254
+ 'decimals': 2,
255
+ 'tip': 'The width of the band in pixels',
256
+ },
257
+ {
258
+ 'name': 'Show Filter',
259
+ 'type': 'bool',
260
+ 'value': False,
261
+ 'tip': 'Toggle to show or hide the filter',
262
+ },
263
+ ],
264
+ },
265
+ ],
266
+ },
267
+ {
268
+ 'name': 'Prefit',
269
+ 'type': 'group',
270
+ 'children': [
271
+ {
272
+ 'name': 'Prefit Detector',
273
+ 'type': 'list',
274
+ 'limits': self.DETECTORS,
275
+ 'value': self.DETECTORS[0],
276
+ },
277
+ {
278
+ 'name': self.DETECTORS[0],
279
+ 'type': 'group',
280
+ 'children': [
281
+ {
282
+ 'name': 'Min Area',
283
+ 'type': 'float',
284
+ 'value': 1.5,
285
+ 'limits': [0.0, 1000.0],
286
+ 'step': 0.1,
287
+ 'decimals': 2,
288
+ 'tip': 'Minimum area of the detected object.',
289
+ },
290
+ {
291
+ 'name': 'Max Area',
292
+ 'type': 'float',
293
+ 'value': 80.0,
294
+ 'limits': [0.0, 1000.0],
295
+ 'step': 0.1,
296
+ 'decimals': 2,
297
+ 'tip': 'Maximum area of the detected object.',
298
+ },
299
+ ],
300
+ },
301
+ {
302
+ 'name': 'Relative Threshold',
303
+ 'type': 'group',
304
+ 'children': [
305
+ {
306
+ 'name': 'min',
307
+ 'type': 'float',
308
+ 'value': 0.5,
309
+ 'limits': [0.0, 1.0],
310
+ 'step': 0.01,
311
+ 'decimals': 3,
312
+ 'tip': 'Minimum relative threshold for detection.',
313
+ },
314
+ {
315
+ 'name': 'max',
316
+ 'type': 'float',
317
+ 'value': 1.0,
318
+ 'limits': [0.0, 1.0],
319
+ 'step': 0.01,
320
+ 'decimals': 3,
321
+ 'tip': 'Maximum relative threshold for detection.',
322
+ },
323
+ ],
324
+ },
325
+ ],
326
+ },
327
+ {
328
+ 'name': 'Localization',
329
+ 'type': 'group',
330
+ 'children': [
331
+ {
332
+ 'name': 'Fitting Method',
333
+ 'type': 'list',
334
+ 'limits': self.FITTING_METHODS,
335
+ 'value': self.FITTING_METHODS[0],
336
+ },
337
+ {
338
+ 'name': 'GPU',
339
+ 'type': 'bool',
340
+ 'value': True,
341
+ 'tip': 'Try GPU-accelerated fitting, if available.',
342
+ },
343
+ {
344
+ 'name': 'ROI Size',
345
+ 'type': 'int',
346
+ 'value': 13,
347
+ 'limits': [7, 201],
348
+ 'step': 2,
349
+ 'suffix': 'pixel',
350
+ 'tip': 'Size of the fitting box in pixels.',
351
+ },
352
+ {
353
+ 'name': 'Pixel-size',
354
+ 'type': 'float',
355
+ 'value': 114.17,
356
+ 'limits': [0.0, 20000],
357
+ 'step': 1,
358
+ 'decimals': 2,
359
+ 'suffix': 'nm',
360
+ 'tip': 'Size of a pixel in nanometers.',
361
+ },
362
+ {
363
+ 'name': 'Initial Sigma',
364
+ 'type': 'float',
365
+ 'value': 1.0,
366
+ 'limits': [0.4, 10],
367
+ 'step': 0.1,
368
+ 'decimals': 3,
369
+ 'suffix': 'pixel',
370
+ 'tip': 'Initial sigma for fitting.',
371
+ },
372
+ {'name': 'Localize', 'type': 'action'},
373
+ ],
374
+ },
375
+ {
376
+ 'name': 'PSF',
377
+ 'type': 'group',
378
+ 'children': [
379
+ {
380
+ 'name': 'Field Dependency',
381
+ 'type': 'group',
382
+ 'children': [
383
+ {
384
+ 'name': 'Enabled',
385
+ 'type': 'bool',
386
+ 'value': False,
387
+ 'tip': 'Toggle to enable or disable this option',
388
+ },
389
+ {
390
+ 'name': 'Grid Size',
391
+ 'type': 'int',
392
+ 'value': 3,
393
+ 'limits': [3, 15],
394
+ 'step': 1,
395
+ 'suffix': 'pixel',
396
+ 'tip': 'Size of the grid for field dependency.',
397
+ },
398
+ ],
399
+ },
400
+ {
401
+ 'name': 'Zero Plane',
402
+ 'type': 'group',
403
+ 'children': [
404
+ {
405
+ 'name': 'Enabled',
406
+ 'type': 'bool',
407
+ 'value': False,
408
+ 'tip': 'Toggle to enable or disable this option',
409
+ },
410
+ {
411
+ 'name': 'Method',
412
+ 'type': 'list',
413
+ 'limits': [
414
+ 'All',
415
+ 'Intensity',
416
+ 'Min Sigma',
417
+ 'Min Sum Sigma',
418
+ ],
419
+ 'value': 'All',
420
+ 'tip': 'Select the method to determine the zero plane.',
421
+ },
422
+ ],
423
+ 'tip': (
424
+ 'If checked, the algorithm searches for Z=0 and ROIs are'
425
+ + ' extracted up and down; otherwise, they are extracted'
426
+ + ' from each slice localizations.'
427
+ ),
428
+ },
429
+ {
430
+ 'name': 'Z Step',
431
+ 'type': 'int',
432
+ 'value': 10,
433
+ 'limits': [1, 1000],
434
+ 'step': 1,
435
+ 'suffix': 'nm',
436
+ 'tip': 'Step size along the Z axis.',
437
+ },
438
+ {
439
+ 'name': 'Upsample',
440
+ 'type': 'int',
441
+ 'value': 1,
442
+ 'limits': [1, 200],
443
+ 'step': 1,
444
+ 'tip': 'Factor by which to upsample the image.',
445
+ },
446
+ {
447
+ 'name': 'Extract',
448
+ 'type': 'action',
449
+ 'tip': 'Extract the PSF data from the stack.',
450
+ },
451
+ ],
452
+ },
453
+ {
454
+ 'name': 'Settings',
455
+ 'type': 'group',
456
+ 'children': [
457
+ {
458
+ 'name': 'Export',
459
+ 'type': 'action',
460
+ 'tip': 'Export the current settings.',
461
+ },
462
+ {
463
+ 'name': 'Import',
464
+ 'type': 'action',
465
+ 'tip': 'Import settings from a file.',
466
+ },
467
+ ],
468
+ },
469
+ ]
470
+
471
+ self.param_tree = Parameter.create(name='', type='group', children=params)
472
+ self.param_tree.sigTreeStateChanged.connect(self.change)
473
+ self.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)
474
+
475
+ self.get_param(Parameters.LOCALIZE).sigActivated.connect(self.onLocalize)
476
+ self.get_param(Parameters.EXTRACT_PSF).sigActivated.connect(self.onExtractPSF)
477
+ self.get_param(Parameters.IMPORT_STATE).sigActivated.connect(self.load_json)
478
+ self.get_param(Parameters.EXPORT_STATE).sigActivated.connect(self.export_json)
479
+ self.get_param(Parameters.SAVE_CROPPED_IMAGE).sigActivated.connect(
480
+ self.onCropped
481
+ )
482
+
483
+ @Slot(object)
484
+ def onCropped(self, action: Parameter):
485
+ self.saveCropped.emit()
486
+
487
+ @Slot(object)
488
+ def onLocalize(self, action: Parameter):
489
+ self.localizeData.emit()
490
+
491
+ @Slot(object)
492
+ def onExtractPSF(self, action: Parameter):
493
+ self.extractPSF.emit()
494
+
495
+ @Slot(object, object)
496
+ def change(self, param: Parameter, changes: list):
497
+ # Check if 'Enable ROI' parameter is changed
498
+ if any('Enable ROI' in param.name() for param, _, _ in changes):
499
+ self.roiEnabled.emit()
500
+
501
+ if any(
502
+ param.name() in ['x', 'y', 'width', 'height'] for param, _, _ in changes
503
+ ):
504
+ self.roiChanged.emit(
505
+ (
506
+ self.get_param_value(Parameters.ROI_X),
507
+ self.get_param_value(Parameters.ROI_Y),
508
+ self.get_param_value(Parameters.ROI_WIDTH),
509
+ self.get_param_value(Parameters.ROI_HEIGHT),
510
+ )
511
+ )
512
+
513
+ if any(
514
+ 'Localization' not in self.get_param_path(param)
515
+ and 'Settings' not in self.get_param_path(param)
516
+ for param, _, _ in changes
517
+ ):
518
+ self.paramsChanged.emit()
519
+
520
+ if not self._debug:
521
+ return
522
+
523
+ print('tree changes:')
524
+ for param, change, data in changes:
525
+ path = self.get_param_path(param)
526
+ if len(path) > 1:
527
+ print(f' parameter: {path[-1]}')
528
+ print(f' parent: {path[-2]}')
529
+ else:
530
+ childName = '.'.join(path) if path is not None else param.name()
531
+ print(f' parameter: {childName}')
532
+ print(f' change: {change}')
533
+ print(f' data: {str(data)}')
534
+ print(' ----------')
535
+
536
+ def set_roi(self, x: int, y: int, width: int, height: int):
537
+ try:
538
+ self.param_tree.sigTreeStateChanged.disconnect(self.change)
539
+ self.set_param_value(Parameters.ROI_X, x)
540
+ self.set_param_value(Parameters.ROI_Y, y)
541
+ self.set_param_value(Parameters.ROI_WIDTH, width)
542
+ self.set_param_value(Parameters.ROI_HEIGHT, height)
543
+ finally:
544
+ self.param_tree.sigTreeStateChanged.connect(self.change)
545
+
546
+ def get_image_filter(self) -> AbstractFilter:
547
+ value = self.get_param_value(Parameters.FILTER)
548
+ filter_class = FILTERS.get(value)
549
+ if filter_class is DoG_Filter:
550
+ filter: DoG_Filter = self._filters.get(value, None)
551
+ if filter is None:
552
+ self._filters[value] = DoG_Filter(
553
+ self.get_param_value(Parameters.SIGMA),
554
+ self.get_param_value(Parameters.FACTOR),
555
+ )
556
+ else:
557
+ filter.set_params(
558
+ self.get_param_value(Parameters.SIGMA),
559
+ self.get_param_value(Parameters.FACTOR),
560
+ )
561
+ return self._filters[value]
562
+ elif filter_class is BandpassFilter:
563
+ filter: BandpassFilter = self._filters.get(value, None)
564
+ if filter is None:
565
+ self._filters[value] = BandpassFilter(
566
+ self.get_param_value(Parameters.CENTER),
567
+ self.get_param_value(Parameters.WIDTH),
568
+ self.get_param_value(Parameters.FILTER_TYPE),
569
+ self.get_param_value(Parameters.SHOW_FILTER),
570
+ )
571
+ else:
572
+ filter.set_params(
573
+ self.get_param_value(Parameters.CENTER),
574
+ self.get_param_value(Parameters.WIDTH),
575
+ self.get_param_value(Parameters.FILTER_TYPE),
576
+ self.get_param_value(Parameters.SHOW_FILTER),
577
+ )
578
+ return self._filters[value]
579
+ elif filter_class is PassFilter:
580
+ filter: PassFilter = self._filters.get(value, None)
581
+ if filter is None:
582
+ self._filters[value] = PassFilter()
583
+ return filter
584
+ else:
585
+ return AbstractFilter()
586
+
587
+ def get_detector(self) -> AbstractDetector:
588
+ value = self.get_param_value(Parameters.PREFIT_DETECTOR)
589
+ detector_class = DETECTORS.get(value)
590
+ if detector_class is CV_BlobDetector:
591
+ detector: CV_BlobDetector = self._detectors.get(value, None)
592
+ if detector is None:
593
+ self._detectors[value] = CV_BlobDetector(
594
+ minArea=self.get_param_value(Parameters.MIN_AREA),
595
+ maxArea=self.get_param_value(Parameters.MAX_AREA),
596
+ )
597
+ else:
598
+ detector.set_blob_detector_params(
599
+ minArea=self.get_param_value(Parameters.MIN_AREA),
600
+ maxArea=self.get_param_value(Parameters.MAX_AREA),
601
+ )
602
+ return self._detectors[value]
603
+ else:
604
+ return AbstractDetector()
605
+
606
+ def get_fitting_method(self):
607
+ value = self.get_param_value(Parameters.FITTING_METHOD)
608
+ return FittingMethod.from_string(value)
609
+
610
+
611
+ if __name__ == '__main__':
612
+ app = QApplication([])
613
+ my_app = FittingOptions()
614
+ my_app.show()
615
+ app.exec()