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,461 @@
1
+ import time
2
+ from queue import Queue
3
+ from typing import Union
4
+
5
+ import numpy as np
6
+ from pycromanager import (
7
+ Acquisition,
8
+ multi_d_acquisition_events,
9
+ start_headless,
10
+ stop_headless,
11
+ )
12
+
13
+ from microEye.hardware.cams.micam import miCamera
14
+ from microEye.hardware.pycromanager.core import DEFAULT_BRIDGE_PORT, PycroCore
15
+ from microEye.hardware.pycromanager.enums import DeviceType, PropertyType
16
+
17
+
18
+ class PycroDevice:
19
+ def __init__(
20
+ self, label: str, type: DeviceType, port: int = DEFAULT_BRIDGE_PORT
21
+ ) -> None:
22
+ if not isinstance(type, DeviceType):
23
+ raise ValueError(f'Invalid device type: {type}')
24
+
25
+ self._port = port
26
+
27
+ if label is None or not isinstance(label, str):
28
+ if type == DeviceType.CameraDevice:
29
+ label = self._core.get_camera_device()
30
+ elif type == DeviceType.StageDevice:
31
+ label = self._core.get_focus_device()
32
+ elif type == DeviceType.XYStageDevice:
33
+ label = self._core.get_xy_stage_device()
34
+ elif type == DeviceType.AutoFocusDevice:
35
+ label = self._core.get_auto_focus_device()
36
+ elif type == DeviceType.GalvoDevice:
37
+ label = self._core.get_galvo_device()
38
+ elif type == DeviceType.SLMDevice:
39
+ label = self._core.get_slm_device()
40
+ elif type == DeviceType.ShutterDevice:
41
+ label = self._core.get_shutter_device()
42
+ else:
43
+ raise ValueError(
44
+ f'Unsupported device type: {type}. Please provide a valid label.'
45
+ )
46
+
47
+ if self._core.get_device_type(label) != type:
48
+ raise ValueError(f'Label "{label}" is not a {type.name} device')
49
+
50
+ self._label = label
51
+ self._type = type
52
+
53
+ @property
54
+ def _core(self) -> PycroCore:
55
+ return PycroCore.instance(self._port)
56
+
57
+ @property
58
+ def device_type(self) -> DeviceType:
59
+ return self._type
60
+
61
+ @property
62
+ def label(self) -> str:
63
+ return self._label
64
+
65
+ @property
66
+ def busy(self) -> bool:
67
+ return self._core.device_busy(self._label)
68
+
69
+ @property
70
+ def name(self) -> float:
71
+ return self._core.get_device_name(self._label)
72
+
73
+ @name.setter
74
+ def name(self, value: str):
75
+ return
76
+ print('Setting PycroDevice name is not supported')
77
+
78
+ def wait_for_device(self):
79
+ self._core.wait_for_device(self._label)
80
+
81
+ def get_prop_names(self) -> list[str]:
82
+ return self._core.get_device_property_names(self._label)
83
+
84
+ def get_prop_values(self) -> dict:
85
+ return self._core.get_system_state()[self._label]
86
+
87
+ def has_property(self, prop_name: str) -> bool:
88
+ return self._core.has_property(self._label, prop_name)
89
+
90
+ def get_property(self, prop_name: str):
91
+ prop_value = self._core.get_property(self.label, prop_name)
92
+ prop_type = self.get_property_type(prop_name)
93
+
94
+ if prop_type == PropertyType.Undef:
95
+ return prop_value
96
+
97
+ python_type = prop_type.to_python()
98
+
99
+ return python_type(prop_value)
100
+
101
+ def get_property_type(self, prop_name: str) -> PropertyType:
102
+ return self._core.get_property_type(self.label, prop_name)
103
+
104
+ def set_property(self, prop_name: str, prop_value):
105
+ self._core.set_property(self.label, prop_name, prop_value)
106
+
107
+ def is_property_read_only(self, prop_name: str) -> bool:
108
+ return self._core.is_property_read_only(self.label, prop_name)
109
+
110
+ def get_allowed_property_values(self, prop_name: str) -> list:
111
+ return self._core.get_allowed_property_values(self.label, prop_name)
112
+
113
+ def get_property_limits(self, prop_name: str) -> tuple:
114
+ return self._core.get_property_limits(self.label, prop_name)
115
+
116
+ def property_tree(self):
117
+ tree = []
118
+ for prop in self.get_prop_names():
119
+ prop_type = self.get_property_type(prop)
120
+
121
+ if prop_type == PropertyType.Undef:
122
+ continue
123
+
124
+ python_type = prop_type.to_python()
125
+
126
+ prop_value = python_type(self.get_property(prop))
127
+
128
+ prop_read_only = self.is_property_read_only(prop)
129
+
130
+ prop_limits = self.get_property_limits(prop)
131
+
132
+ prop_allowed_values = self.get_allowed_property_values(prop)
133
+
134
+ # if any prop_limits is None, it means that the property has no limits
135
+ if any(limit is None for limit in prop_limits):
136
+ prop_limits = None
137
+
138
+ if prop_allowed_values:
139
+ prop_limits = [python_type(value) for value in prop_allowed_values]
140
+ python_type = list
141
+
142
+ tree.append(
143
+ {
144
+ 'name': prop,
145
+ 'type': python_type.__name__,
146
+ 'value': prop_value,
147
+ 'enabled': not prop_read_only,
148
+ 'limits': prop_limits,
149
+ }
150
+ )
151
+
152
+ return tree
153
+
154
+ def __str__(self) -> str:
155
+ return (
156
+ f'Device Type: {self.device_type.name}, '
157
+ f'Label: {self.label}, Name: {self.name}'
158
+ )
159
+
160
+
161
+ class PycroCamera(PycroDevice, miCamera):
162
+ def __init__(self, label: str = None, port: int = DEFAULT_BRIDGE_PORT) -> None:
163
+ PycroDevice.__init__(self, label, DeviceType.CameraDevice, port)
164
+ miCamera.__init__(self)
165
+ # self._last_image = np.zeros((self.height, self.width))
166
+ # self._buffer = buffer if buffer is not None else Queue()
167
+
168
+ @property
169
+ def image_count(self) -> int:
170
+ return self._core.get_remaining_image_count()
171
+
172
+ @property
173
+ def width(self) -> int:
174
+ return self._core.get_image_width()
175
+
176
+ @width.setter
177
+ def width(self, value: int):
178
+ raise NotImplementedError('Cannot set camera width')
179
+
180
+ @property
181
+ def height(self) -> int:
182
+ return self._core.get_image_height()
183
+
184
+ @height.setter
185
+ def height(self, value: int):
186
+ raise NotImplementedError('Cannot set camera height')
187
+
188
+ @property
189
+ def ROI(self) -> tuple[int, int, int, int]:
190
+ return self._core.get_roi()
191
+
192
+ def set_roi(self, roi_x, roi_y, roi_width, roi_height):
193
+ self._core.set_roi(roi_x, roi_y, roi_width, roi_height, self.label)
194
+
195
+ def reset_roi(self):
196
+ self._core.clear_roi()
197
+
198
+ @property
199
+ def bit_depth(self) -> int:
200
+ return self._core.get_image_bit_depth()
201
+
202
+ @property
203
+ def bytes_per_pixel(self) -> int:
204
+ return int(np.ceil(self.bit_depth / 8))
205
+
206
+ @bytes_per_pixel.setter
207
+ def bytes_per_pixel(self, value: int):
208
+ pass
209
+
210
+ @property
211
+ def exposure_current(self) -> float:
212
+ return self._core.get_exposure(self._label)
213
+
214
+ @exposure_current.setter
215
+ def exposure_current(self, value: float):
216
+ self._core.set_exposure(value, self.label)
217
+
218
+ def setExposure(self, exposure: float):
219
+ self._core.set_exposure(exposure, self.label)
220
+
221
+ @property
222
+ def exposure_range(self) -> tuple[float, float]:
223
+ try:
224
+ return self.get_property_limits('Exposure')
225
+ except Exception as e:
226
+ return (0.1, 10000)
227
+
228
+ @exposure_range.setter
229
+ def exposure_range(self, value: tuple[float, float]):
230
+ return
231
+ print('PycroCamera: Setting exposure range is not supported')
232
+
233
+ def get_temperature(self) -> float:
234
+ try:
235
+ for prop in self.get_prop_names():
236
+ if 'temperature' in prop.lower():
237
+ return float(self.get_property(prop))
238
+ except Exception as e:
239
+ print(f'Error getting temperature: {e}')
240
+ finally:
241
+ return -127.0 # noqa: B012
242
+
243
+ @property
244
+ def pixel_size_um(self) -> float:
245
+ return self._core.get_pixel_size_um()
246
+
247
+ def get_property(self, prop_name: str):
248
+ return self._core.get_property(self.label, prop_name)
249
+
250
+ def set_property(self, prop_name: str, prop_value):
251
+ self._core.set_property(self.label, prop_name, prop_value)
252
+
253
+ def get_image(self) -> np.ndarray:
254
+ return self._core.get_image().reshape((self.height, self.width))
255
+
256
+ # def get_image_buffer(self) -> np.ndarray:
257
+ # return self._buffer.get().reshape((cam.height, cam.width))
258
+
259
+ def pop_image(self) -> np.ndarray:
260
+ return self._core.pop_next_image().reshape((self.height, self.width))
261
+
262
+ def get_tagged_image(self) -> tuple[np.ndarray, dict[str, str]]:
263
+ tagged = self._core.get_tagged_image()
264
+ return tagged.pix, tagged.tags
265
+
266
+ def snap_image(self, buffered=False):
267
+ if self.is_sequence_running():
268
+ print('Sequence already running')
269
+ return None
270
+
271
+ self.start_sequence_acquisition(0, 1)
272
+
273
+ # add timeout to avoid infinite loop
274
+ start = time.time()
275
+ while self.image_count == 0:
276
+ time.sleep(0.001)
277
+
278
+ if time.time() - start > 5:
279
+ print('Timeout: Could not snap image')
280
+ return None
281
+
282
+ img: np.ndarray = self.pop_image()
283
+ # if self._last_image is None or not np.allclose(self._last_image, img):
284
+ # self._last_image = img.copy()
285
+ # if buffered:
286
+ # self._buffer.put(img)
287
+
288
+ return img
289
+
290
+ def start_continuous_acquisition(self, interval_ms: int = 0):
291
+ if self._core.is_sequence_running():
292
+ print('Sequence already running')
293
+ return
294
+
295
+ self._core.start_continuous_sequence_acquisition(interval_ms)
296
+
297
+ def is_sequence_running(self) -> bool:
298
+ return self._core.is_sequence_running(self._label)
299
+
300
+ def start_sequence_acquisition(self, interval_ms: float, num_time_points: int):
301
+ if self.is_sequence_running():
302
+ print('Sequence already running')
303
+ return
304
+
305
+ self._core.prepare_sequence_acquisition(self._label)
306
+ self._core.start_sequence_acquisition(
307
+ num_time_points, interval_ms, True, self._label
308
+ )
309
+
310
+ def stop_sequence_acquisition(self):
311
+ self._core.stop_sequence_acquisition(self._label)
312
+
313
+ def set_as_camera_device(self):
314
+ self._core.set_camera_device(self._label)
315
+
316
+ def populate_status(self):
317
+ self.status['Camera'] = {
318
+ 'Class': self.__class__.__name__,
319
+ 'Name': self._label,
320
+ 'Type': self._type.name,
321
+ }
322
+
323
+ self.status['Exposure'] = {
324
+ 'Value': self.exposure_current,
325
+ 'Unit': 'ms',
326
+ }
327
+
328
+
329
+ class PycroStage(PycroDevice):
330
+ def __init__(self, label: str = None, port: int = DEFAULT_BRIDGE_PORT) -> None:
331
+ super().__init__(label, DeviceType.StageDevice, port=port)
332
+
333
+ @property
334
+ def position(self) -> float:
335
+ return self._core.get_position(self.label)
336
+
337
+ @position.setter
338
+ def position(self, value: float):
339
+ self._core.set_position(value, self.label)
340
+
341
+ def move_rel(self, step: float):
342
+ self._core.set_relative_position(step, self.label)
343
+
344
+ def move_abs(self, position: float):
345
+ self._core.set_position(position, self.label)
346
+
347
+ def home(self):
348
+ return self._core.home(self.label)
349
+
350
+ def stop(self):
351
+ return self._core.stop(self.label)
352
+
353
+ def set_adapter_origin_z(self, new_z_um: float):
354
+ self._core.set_adapter_origin(new_z_um, self.label)
355
+
356
+ def set_origin(self):
357
+ self._core.set_origin(self.label)
358
+
359
+
360
+ class PycroXYStage(PycroDevice):
361
+ def __init__(self, label: str = None, port: int = DEFAULT_BRIDGE_PORT) -> None:
362
+ super().__init__(label, DeviceType.XYStageDevice, port=port)
363
+
364
+ @property
365
+ def X(self) -> float:
366
+ return self._core.get_x_position(self.label)
367
+
368
+ @property
369
+ def Y(self) -> float:
370
+ return self._core.get_y_position(self.label)
371
+
372
+ @X.setter
373
+ def X(self, value: float):
374
+ self._core.set_xy_position(value, self.Y, self.label)
375
+
376
+ @Y.setter
377
+ def Y(self, value: float):
378
+ self._core.set_xy_position(self.X, value, self.label)
379
+
380
+ def move_rel(self, x: float, y: float):
381
+ self._core.set_relative_xy_position(x, y, self.label)
382
+
383
+ def move_abs(self, x: float, y: float):
384
+ self._core.set_xy_position(x, y, self.label)
385
+
386
+ def home(self):
387
+ return self._core.home(self.label)
388
+
389
+ def stop(self):
390
+ return self._core.stop(self.label)
391
+
392
+ def set_adapter_origin(self, new_x_um: float, new_y_um: float):
393
+ self._core.set_adapter_origin_xy(new_x_um, new_y_um, self.label)
394
+
395
+ def set_origin(self):
396
+ self._core.set_origin_xy(self.label)
397
+
398
+ def set_as_xy_stage_device(self):
399
+ self._core.set_xy_stage_device(self.label)
400
+
401
+
402
+ if __name__ == '__main__':
403
+ import os
404
+ import time
405
+
406
+ import cv2
407
+
408
+ mm_path = 'C:/Program Files/Micro-Manager-2.0'
409
+ config = 'MMConfig_demo.cfg'
410
+ port = 1234
411
+
412
+ try:
413
+ start_headless(
414
+ mm_path,
415
+ config,
416
+ port=port,
417
+ # python_backend=True,
418
+ )
419
+
420
+ cam = PycroCamera('Camera', port=port)
421
+ cam.exposure_current = 50
422
+
423
+
424
+ cam.start_sequence_acquisition(0, 500) # 0 ms interval for fastest acquisition
425
+
426
+ # def show_image(image, metadata):
427
+ # cv2.imshow('Image', image)
428
+ # cv2.waitKey(1)
429
+
430
+ # with Acquisition(show_display=False, image_process_fn=show_image) as acq:
431
+ # events = multi_d_acquisition_events(
432
+ # 2000,
433
+ # ) # events for 2000 time points
434
+ # for dic in events:
435
+ # dic['camera'] = 'DCam_1'
436
+ # acq.acquire(events)
437
+
438
+ i = 0
439
+ while cam.is_sequence_running() or cam.image_count > 0:
440
+ # Check if there are images before trying to get one
441
+ if cam.image_count > 0:
442
+ img = cam.pop_image()
443
+ i += 1
444
+ cv2.imshow('', img)
445
+
446
+ key = cv2.waitKey(1)
447
+ if key == ord('q'):
448
+ cam.stop_sequence_acquisition()
449
+
450
+ print(
451
+ f'Remaining images: {cam.image_count:04d}',
452
+ f'| Iterations : {i:04d}',
453
+ end='\r',
454
+ )
455
+
456
+ cv2.destroyAllWindows()
457
+ except Exception as e:
458
+ import traceback
459
+ traceback.print_exc()
460
+ finally:
461
+ PycroCore._instances[port].close()
@@ -0,0 +1,107 @@
1
+ from enum import Enum
2
+
3
+
4
+ # ActionType
5
+ class ActionType(Enum):
6
+ NoAction = 0
7
+ BeforeGet = 1
8
+ AfterSet = 2
9
+ IsSequenceable = 3
10
+ AfterLoadSequence = 4
11
+ StartSequence = 5
12
+ StopSequence = 6
13
+
14
+ # DeviceType
15
+ class DeviceType(Enum):
16
+ UnknownType = 0
17
+ AnyType = 1
18
+ CameraDevice = 2
19
+ ShutterDevice = 3
20
+ StateDevice = 4
21
+ StageDevice = 5
22
+ XYStageDevice = 6
23
+ SerialDevice = 7
24
+ GenericDevice = 8
25
+ AutoFocusDevice = 9
26
+ CoreDevice = 10
27
+ ImageProcessorDevice = 11
28
+ SignalIODevice = 12
29
+ MagnifierDevice = 13
30
+ SLMDevice = 14
31
+ HubDevice = 15
32
+ GalvoDevice = 16
33
+
34
+ # PropertyType
35
+ class PropertyType(Enum):
36
+ Undef = 0
37
+ String = 1
38
+ Float = 2
39
+ Integer = 3
40
+
41
+ def to_python(self):
42
+ if self == PropertyType.String:
43
+ return str
44
+ elif self == PropertyType.Float:
45
+ return float
46
+ elif self == PropertyType.Integer:
47
+ return int
48
+ else:
49
+ return None
50
+
51
+ @classmethod
52
+ def to_dict(cls):
53
+ return {member.value: member for member in cls}
54
+
55
+ @classmethod
56
+ def from_java(cls, prop_jv):
57
+ value = prop_jv.swig_value() if hasattr(prop_jv, 'swig_value') else int(prop_jv)
58
+ members = cls.to_dict()
59
+ if value in members:
60
+ return members[value]
61
+ else:
62
+ raise ValueError('Invalid PropertyType value')
63
+
64
+ # PortType
65
+ class PortType(Enum):
66
+ InvalidPort = 0
67
+ SerialPort = 1
68
+ USBPort = 2
69
+ HIDPort = 3
70
+
71
+ # FocusDirection
72
+ class FocusDirection(Enum):
73
+ Unknown = 0
74
+ TowardSample = 1
75
+ AwayFromSample = 2
76
+
77
+ # DeviceNotification
78
+ class DeviceNotification(Enum):
79
+ Attention = 0
80
+ Done = 1
81
+ StatusChanged = 2
82
+
83
+ # DeviceDetectionStatus
84
+ class DeviceDetectionStatus(Enum):
85
+ Unimplemented = -2
86
+ Misconfigured = -1
87
+ CanNotCommunicate = 0
88
+ CanCommunicate = 1
89
+
90
+ # DeviceInitializationState
91
+ class DeviceInitializationState(Enum):
92
+ Uninitialized = 0
93
+ InitializedSuccessfully = 1
94
+ InitializationFailed = 2
95
+
96
+
97
+ # MetadataProfiles
98
+ class MetadataProfile(Enum):
99
+ Essential = 0
100
+ CameraLabel = 1
101
+ BitDepth = 2
102
+ ROIAndBinning = 3
103
+ Timing = 4
104
+ CameraSpecificTags = 5
105
+ PixelSize = 6
106
+ LegacyAcqEngMetadata = 7
107
+ SystemStateCache = 8
@@ -0,0 +1,153 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+ from typing import Optional
4
+
5
+ import yaml
6
+ from mmpycorex.launcher import (
7
+ _JAVA_HEADLESS_SUBPROCESSES,
8
+ _PYMMCORES,
9
+ active_java_ports,
10
+ is_java_port_allocated,
11
+ logger,
12
+ server_terminated,
13
+ terminate_java_instances,
14
+ )
15
+ from pycromanager.headless import (
16
+ DEFAULT_BRIDGE_PORT,
17
+ start_headless,
18
+ stop_headless,
19
+ )
20
+
21
+
22
+ @dataclass
23
+ class HeadlessInstance:
24
+ '''Data class to store information about a headless instance.'''
25
+
26
+ port: int
27
+ mm_app_path: str
28
+ config_file: Optional[str] = None
29
+ java_loc: Optional[str] = None
30
+ python_backend: bool = False
31
+ core_log_path: str = ''
32
+ buffer_size_mb: int = 1024
33
+ max_memory_mb: int = 2000
34
+ debug: bool = False
35
+ name: str = 'Unnamed Instance'
36
+
37
+ def __post_init__(self):
38
+ '''Generate a name if none was provided.'''
39
+ if self.name == 'Unnamed Instance':
40
+ backend = 'Python' if self.python_backend else 'Java'
41
+ self.name = f'{backend}_Backend_{self.port}'
42
+
43
+
44
+ class HeadlessManager:
45
+ '''Manager class for headless Micro-Manager instances.'''
46
+
47
+ # Singleton instance
48
+ _instance = None
49
+
50
+ def __new__(cls):
51
+ if cls._instance is None:
52
+ cls._instance = super().__new__(cls)
53
+ cls._instance._initialized = False
54
+ return cls._instance
55
+
56
+ def __init__(self):
57
+ if self._initialized:
58
+ return
59
+ self._initialized = True
60
+ self._instances: dict[int, HeadlessInstance] = {}
61
+
62
+ @property
63
+ def instances(self) -> dict[int, HeadlessInstance]:
64
+ '''Get a dictionary of all running instances.'''
65
+ return self._instances.copy()
66
+
67
+ @property
68
+ def ports(self) -> list[int]:
69
+ '''Get a list of all ports in use.'''
70
+ return list(self._instances.keys())
71
+
72
+ def is_port_used(self, port: int) -> bool:
73
+ '''Check if a port is already in use.'''
74
+ return port in self._instances
75
+
76
+ def start_instance(self, instance_config: HeadlessInstance) -> bool:
77
+ '''Start a new headless instance with the given configuration.'''
78
+ if self.is_port_used(instance_config.port):
79
+ return False
80
+
81
+ try:
82
+ start_headless(
83
+ mm_app_path=instance_config.mm_app_path,
84
+ config_file=instance_config.config_file,
85
+ java_loc=instance_config.java_loc,
86
+ python_backend=False, # instance_config.python_backend,
87
+ core_log_path=instance_config.core_log_path,
88
+ buffer_size_mb=instance_config.buffer_size_mb,
89
+ max_memory_mb=instance_config.max_memory_mb,
90
+ port=instance_config.port,
91
+ debug=instance_config.debug,
92
+ )
93
+ self._instances[instance_config.port] = instance_config
94
+ return True
95
+ except Exception as e:
96
+ print(f'Failed to start headless instance: {e}')
97
+ return False
98
+
99
+ def stop_instance(self, port: int) -> bool:
100
+ '''Stop a specific headless instance by port.'''
101
+ if not self.is_port_used(port):
102
+ return False
103
+
104
+ try:
105
+ terminate_java_instances(debug=self._instances[port].debug, port=port)
106
+ del self._instances[port]
107
+ return True
108
+ except Exception as e:
109
+ print(f'Failed to stop headless instance: {e}')
110
+ return False
111
+
112
+ def stop_all_instances(self, debug=False) -> None:
113
+ '''Stop all running headless instances.'''
114
+ try:
115
+ terminate_java_instances(debug=debug)
116
+ self._instances.clear()
117
+ return True
118
+ except Exception as e:
119
+ print(f'Failed to stop headless instances: {e}')
120
+ return False
121
+
122
+ def get_instance(self, port: int) -> Optional[HeadlessInstance]:
123
+ '''Get instance information for a specific port.'''
124
+ return self._instances.get(port)
125
+
126
+ def save_config(self, filepath: str) -> bool:
127
+ '''Save the current configuration to a file.'''
128
+ # Implementation for saving configs to JSON/YAML
129
+ try:
130
+ with open(filepath, 'w') as f:
131
+ yaml.dump(self._instances, f)
132
+ return True
133
+ except Exception as e:
134
+ print(f'Failed to save configuration: {e}')
135
+ return False
136
+
137
+ def load_config(self, filepath: str) -> bool:
138
+ '''Load configuration from a file.'''
139
+ # Implementation for loading configs from JSON/YAML
140
+ # Each instance will be a dictionary with the port as the key
141
+ # They should be started and added if not confilicting with existing ones
142
+ try:
143
+ with open(filepath) as f:
144
+ _instances = yaml.load(f, Loader=yaml.FullLoader)
145
+
146
+ for port, instance in _instances.items():
147
+ if not self.is_port_used(port):
148
+ self.start_instance(HeadlessInstance(**instance))
149
+
150
+ return True
151
+ except Exception as e:
152
+ print(f'Failed to load configuration: {e}')
153
+ return False