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.
- microEye/__init__.py +47 -0
- microEye/_version.py +2 -0
- microEye/analysis/__init__.py +1 -0
- microEye/analysis/checklist_dialog.py +143 -0
- microEye/analysis/cmosMaps.py +228 -0
- microEye/analysis/filters/__init__.py +9 -0
- microEye/analysis/filters/base.py +21 -0
- microEye/analysis/filters/spatial.py +338 -0
- microEye/analysis/filters/temporal.py +76 -0
- microEye/analysis/fitting/__init__.py +0 -0
- microEye/analysis/fitting/fit.py +680 -0
- microEye/analysis/fitting/nena.py +375 -0
- microEye/analysis/fitting/phasor_fit.py +90 -0
- microEye/analysis/fitting/processing.py +317 -0
- microEye/analysis/fitting/psf/__init__.py +6 -0
- microEye/analysis/fitting/psf/extract.py +1129 -0
- microEye/analysis/fitting/psf/rubost_mean.py +150 -0
- microEye/analysis/fitting/psf/spline.py +167 -0
- microEye/analysis/fitting/psf/stats/__init__.py +12 -0
- microEye/analysis/fitting/psf/stats/core.py +295 -0
- microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
- microEye/analysis/fitting/psf/stats/io.py +104 -0
- microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
- microEye/analysis/fitting/psf/temp.py +147 -0
- microEye/analysis/fitting/psf/test.py +47 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
- microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
- microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
- microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
- microEye/analysis/fitting/results.py +917 -0
- microEye/analysis/fitting/results_stats.py +251 -0
- microEye/analysis/fitting/tardis.py +209 -0
- microEye/analysis/multi_viewer.py +568 -0
- microEye/analysis/processing/__init__.py +1 -0
- microEye/analysis/processing/frc.py +120 -0
- microEye/analysis/rendering/__init__.py +3 -0
- microEye/analysis/rendering/base.py +452 -0
- microEye/analysis/rendering/cloud.py +215 -0
- microEye/analysis/rendering/core.py +47 -0
- microEye/analysis/rendering/volumetric.py +198 -0
- microEye/analysis/tools/__init__.py +0 -0
- microEye/analysis/tools/kymograms.py +1079 -0
- microEye/analysis/tools/roi_selectors.py +556 -0
- microEye/analysis/utils/__init__.py +3 -0
- microEye/analysis/utils/coordinates.py +18 -0
- microEye/analysis/utils/images.py +68 -0
- microEye/analysis/utils/windows.py +19 -0
- microEye/analysis/viewer/__init__.py +3 -0
- microEye/analysis/viewer/image_options_widget.py +615 -0
- microEye/analysis/viewer/images.py +1225 -0
- microEye/analysis/viewer/layers_widget.py +415 -0
- microEye/analysis/viewer/localizations.py +1304 -0
- microEye/analysis/viewer/psf.py +1003 -0
- microEye/analysis/viewer/volume.py +456 -0
- microEye/hardware/__init__.py +3 -0
- microEye/hardware/cams/__init__.py +29 -0
- microEye/hardware/cams/camera_calibration.py +99 -0
- microEye/hardware/cams/camera_list.py +528 -0
- microEye/hardware/cams/camera_options.py +694 -0
- microEye/hardware/cams/camera_panel.py +941 -0
- microEye/hardware/cams/dummy/__init__.py +1 -0
- microEye/hardware/cams/dummy/dummy_panel.py +759 -0
- microEye/hardware/cams/jobs.py +497 -0
- microEye/hardware/cams/line_profiler.py +99 -0
- microEye/hardware/cams/linescan/IR_Cam.py +474 -0
- microEye/hardware/cams/linescan/__init__.py +1 -0
- microEye/hardware/cams/micam.py +524 -0
- microEye/hardware/cams/pco/__init__.py +58 -0
- microEye/hardware/cams/pco/enums.py +382 -0
- microEye/hardware/cams/pco/pco_cam.py +761 -0
- microEye/hardware/cams/pco/pco_panel.py +477 -0
- microEye/hardware/cams/shortcuts.py +312 -0
- microEye/hardware/cams/thorlabs/__init__.py +1 -0
- microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
- microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
- microEye/hardware/cams/ueye/__init__.py +1 -0
- microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
- microEye/hardware/cams/ueye/ueye_panel.py +861 -0
- microEye/hardware/cams/vimba/__init__.py +1 -0
- microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
- microEye/hardware/cams/vimba/vimba_panel.py +813 -0
- microEye/hardware/device.py +60 -0
- microEye/hardware/lasers/__init__.py +13 -0
- microEye/hardware/lasers/io_matchbox.py +791 -0
- microEye/hardware/lasers/io_params.py +85 -0
- microEye/hardware/lasers/io_single_laser.py +742 -0
- microEye/hardware/lasers/laser_relay.py +594 -0
- microEye/hardware/mieye/__init__.py +1 -0
- microEye/hardware/mieye/acquisition_manager.py +467 -0
- microEye/hardware/mieye/devices_manager.py +533 -0
- microEye/hardware/mieye/miEye.py +659 -0
- microEye/hardware/misc/__init__.py +0 -0
- microEye/hardware/misc/acquisition_view.py +71 -0
- microEye/hardware/misc/reglo.py +761 -0
- microEye/hardware/misc/temp.py +188 -0
- microEye/hardware/port_config.py +59 -0
- microEye/hardware/protocols/__init__.py +2 -0
- microEye/hardware/protocols/actions.py +402 -0
- microEye/hardware/protocols/actions_items.py +703 -0
- microEye/hardware/protocols/designer.py +244 -0
- microEye/hardware/protocols/scene_manager.py +191 -0
- microEye/hardware/protocols/serialization.py +97 -0
- microEye/hardware/pycromanager/__init__.py +16 -0
- microEye/hardware/pycromanager/core.py +1433 -0
- microEye/hardware/pycromanager/devices.py +461 -0
- microEye/hardware/pycromanager/enums.py +107 -0
- microEye/hardware/pycromanager/headless.py +153 -0
- microEye/hardware/pycromanager/utils.py +34 -0
- microEye/hardware/pycromanager/widgets/__init__.py +5 -0
- microEye/hardware/pycromanager/widgets/bridges.py +407 -0
- microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
- microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
- microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
- microEye/hardware/stages/__init__.py +18 -0
- microEye/hardware/stages/elliptec/__init__.py +5 -0
- microEye/hardware/stages/elliptec/baseDevice.py +314 -0
- microEye/hardware/stages/elliptec/device.py +384 -0
- microEye/hardware/stages/elliptec/deviceID.py +212 -0
- microEye/hardware/stages/elliptec/devicePort.py +379 -0
- microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
- microEye/hardware/stages/elliptec/devicesView.py +706 -0
- microEye/hardware/stages/elliptec/ellDevices.py +134 -0
- microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
- microEye/hardware/stages/elliptec/motorInfo.py +153 -0
- microEye/hardware/stages/elliptec/stage.py +62 -0
- microEye/hardware/stages/elliptec/test.py +139 -0
- microEye/hardware/stages/kinesis/__init__.py +1 -0
- microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
- microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
- microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
- microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
- microEye/hardware/stages/kinesis/kinesis.py +776 -0
- microEye/hardware/stages/piezo_concept.py +607 -0
- microEye/hardware/stages/stabilizer.py +785 -0
- microEye/hardware/stages/stage.py +89 -0
- microEye/hardware/widgets/__init__.py +10 -0
- microEye/hardware/widgets/controller.py +246 -0
- microEye/hardware/widgets/devices.py +133 -0
- microEye/hardware/widgets/focusWidget.py +264 -0
- microEye/hardware/widgets/qlist_slider.py +113 -0
- microEye/hardware/widgets/scan_acquisition.py +424 -0
- microEye/icons/1024.png +0 -0
- microEye/icons/128.png +0 -0
- microEye/icons/16.png +0 -0
- microEye/icons/24.png +0 -0
- microEye/icons/256.png +0 -0
- microEye/icons/32.png +0 -0
- microEye/icons/48.png +0 -0
- microEye/icons/512.png +0 -0
- microEye/icons/64.png +0 -0
- microEye/icons/__init__.py +0 -0
- microEye/icons/close.svg +88 -0
- microEye/icons/mieye.png +0 -0
- microEye/icons/min.svg +83 -0
- microEye/icons/viewer.png +0 -0
- microEye/launcher.py +42 -0
- microEye/qt.py +181 -0
- microEye/utils/__init__.py +2 -0
- microEye/utils/enum_encoder.py +10 -0
- microEye/utils/expandable_groupbox.py +93 -0
- microEye/utils/gui_helper.py +457 -0
- microEye/utils/hid/__init__.py +8 -0
- microEye/utils/hid/controller.py +153 -0
- microEye/utils/hid/device.py +63 -0
- microEye/utils/hid/enums.py +140 -0
- microEye/utils/hid/utils.py +58 -0
- microEye/utils/labelled_slider.py +134 -0
- microEye/utils/metadata.py +599 -0
- microEye/utils/metadata_tree.py +718 -0
- microEye/utils/micro_launcher.py +245 -0
- microEye/utils/parameter_tree.py +325 -0
- microEye/utils/pyscripting.py +444 -0
- microEye/utils/retry_exec.py +35 -0
- microEye/utils/start_gui.py +112 -0
- microEye/utils/thread_worker.py +115 -0
- microEye/utils/uImage.py +1512 -0
- microeye-2.3.2.dist-info/METADATA +401 -0
- microeye-2.3.2.dist-info/RECORD +190 -0
- microeye-2.3.2.dist-info/WHEEL +5 -0
- microeye-2.3.2.dist-info/entry_points.txt +2 -0
- microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
- microeye-2.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
from queue import Queue
|
|
6
|
+
from typing import Callable
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import ome_types.model as om
|
|
10
|
+
import tifffile as tf
|
|
11
|
+
import zarr
|
|
12
|
+
|
|
13
|
+
from microEye.hardware.cams.camera_calibration import dark_calibration
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AcquisitionJob:
|
|
17
|
+
'''A class holding cross-thread info required for an Acquisition Job.
|
|
18
|
+
|
|
19
|
+
Attributes
|
|
20
|
+
----------
|
|
21
|
+
temp_queue : Queue[float]
|
|
22
|
+
The queue for temporary data.
|
|
23
|
+
display_queue : Queue[np.ndarray]
|
|
24
|
+
The queue for display data.
|
|
25
|
+
save_queue : Queue[tuple[np.ndarray, float]]
|
|
26
|
+
The queue for data to be saved along with associated timestamps.
|
|
27
|
+
path : str
|
|
28
|
+
The base path where files will be saved.
|
|
29
|
+
height : int
|
|
30
|
+
The height of the image frames.
|
|
31
|
+
width : int
|
|
32
|
+
The width of the image frames.
|
|
33
|
+
biggTiff : bool, optional
|
|
34
|
+
Flag indicating whether to use big TIFF format, by default True.
|
|
35
|
+
bytes_per_pixel : int, optional
|
|
36
|
+
The number of bytes per pixel, by default 1.
|
|
37
|
+
exposure : float, optional
|
|
38
|
+
The exposure time in milliseconds, by default 50.0.
|
|
39
|
+
frames : int, optional
|
|
40
|
+
The number of frames, by default 100.
|
|
41
|
+
full_tif_meta : bool, optional
|
|
42
|
+
Flag indicating whether to include full TIFF metadata, by default True.
|
|
43
|
+
is_dark_cal : bool, optional
|
|
44
|
+
Flag indicating whether dark calibration is enabled, by default False.
|
|
45
|
+
meta_file : str, optional
|
|
46
|
+
Additional metadata file path, by default ''.
|
|
47
|
+
meta_func : Callable, optional
|
|
48
|
+
A function to generate metadata, by default a lambda function.
|
|
49
|
+
name : str, optional
|
|
50
|
+
The name of the acquisition job, by default 'Camera'.
|
|
51
|
+
prefix : str, optional
|
|
52
|
+
A prefix for filenames, by default ''.
|
|
53
|
+
save : bool, optional
|
|
54
|
+
Flag indicating whether to save data, by default False.
|
|
55
|
+
Zarr : bool, optional
|
|
56
|
+
Flag indicating whether to use Zarr format, by default False.
|
|
57
|
+
rois : list[list[int]], optional
|
|
58
|
+
List of regions of interest, by default empty list.
|
|
59
|
+
seperate_rois : bool, optional
|
|
60
|
+
Flag indicating whether to save ROIs into seperate files,
|
|
61
|
+
by default False.
|
|
62
|
+
(For Zarr format different ROIs are treated as channels)
|
|
63
|
+
flip_rois : bool, optional
|
|
64
|
+
Flag indicating whether to flip n-th ROIs horizontally for n > 1,
|
|
65
|
+
by default True.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
Methods
|
|
69
|
+
-------
|
|
70
|
+
getExt()
|
|
71
|
+
Return the file extension based on the Zarr attribute.
|
|
72
|
+
getFilename()
|
|
73
|
+
Generate the filename based on the current state.
|
|
74
|
+
getTempFilename()
|
|
75
|
+
Generate the temperatures filename based on the current state.
|
|
76
|
+
addDarkFrame(frame)
|
|
77
|
+
Add a dark frame for calibration.
|
|
78
|
+
addFrame(frame)
|
|
79
|
+
Add a frame to the acquisition job.
|
|
80
|
+
addTemp(temp)
|
|
81
|
+
Add temperature data to the acquisition job.
|
|
82
|
+
getMetaFilename()
|
|
83
|
+
Return the metadata file path.
|
|
84
|
+
writeMetaFile()
|
|
85
|
+
Write metadata to a file.
|
|
86
|
+
getTiffWriter()
|
|
87
|
+
Return a TiffWriter instance for saving TIFF files.
|
|
88
|
+
getZarrArray()
|
|
89
|
+
Return the Zarr array for saving Zarr files.
|
|
90
|
+
saveMetadata()
|
|
91
|
+
Save metadata to the current file.
|
|
92
|
+
finalize()
|
|
93
|
+
Finalize and clean up resources.
|
|
94
|
+
setDone(index, value)
|
|
95
|
+
Set the completion status for a specific stage.
|
|
96
|
+
'''
|
|
97
|
+
|
|
98
|
+
def __init__(self, temp_queue: Queue[float], display_queue: Queue[np.ndarray],
|
|
99
|
+
save_queue: Queue[tuple[np.ndarray, float]], path: str,
|
|
100
|
+
height: int, width: int, **kwargs) -> None:
|
|
101
|
+
'''Initialize the AcquisitionJob instance.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
temp_queue : Queue[float]
|
|
106
|
+
The queue for temporary data.
|
|
107
|
+
display_queue : Queue[np.ndarray]
|
|
108
|
+
The queue for display data.
|
|
109
|
+
save_queue : Queue[tuple[np.ndarray, float]]
|
|
110
|
+
The queue for data to be saved along with associated timestamps.
|
|
111
|
+
path : str
|
|
112
|
+
The base path where files will be saved.
|
|
113
|
+
height : int
|
|
114
|
+
The height of the image frames.
|
|
115
|
+
width : int
|
|
116
|
+
The width of the image frames.
|
|
117
|
+
**kwargs
|
|
118
|
+
Optional keyword arguments for customization.
|
|
119
|
+
|
|
120
|
+
Optional Parameters
|
|
121
|
+
-------------------
|
|
122
|
+
biggTiff : bool, optional
|
|
123
|
+
Flag indicating whether to use big TIFF format, by default True.
|
|
124
|
+
bytes_per_pixel : int, optional
|
|
125
|
+
The number of bytes per pixel, by default 1.
|
|
126
|
+
exposure : float, optional
|
|
127
|
+
The exposure time in milliseconds, by default 50.0.
|
|
128
|
+
frames : int, optional
|
|
129
|
+
The number of frames, by default 100.
|
|
130
|
+
full_tif_meta : bool, optional
|
|
131
|
+
Flag indicating whether to include full TIFF metadata, by default True.
|
|
132
|
+
is_dark_cal : bool, optional
|
|
133
|
+
Flag indicating whether dark calibration is enabled, by default False.
|
|
134
|
+
meta_file : str, optional
|
|
135
|
+
Additional metadata file path, by default ''.
|
|
136
|
+
meta_func : Callable, optional
|
|
137
|
+
A function to generate metadata, by default a lambda function.
|
|
138
|
+
name : str, optional
|
|
139
|
+
The name of the acquisition job, by default 'Camera'.
|
|
140
|
+
prefix : str, optional
|
|
141
|
+
A prefix for filenames, by default ''.
|
|
142
|
+
save : bool, optional
|
|
143
|
+
Flag indicating whether to save data, by default False.
|
|
144
|
+
Zarr : bool, optional
|
|
145
|
+
Flag indicating whether to use Zarr format, by default False.
|
|
146
|
+
rois : list[list[int]], optional
|
|
147
|
+
List of regions of interest, by default empty list.
|
|
148
|
+
seperate_rois : bool, optional
|
|
149
|
+
Flag indicating whether to save ROIs into seperate files,
|
|
150
|
+
by default False.
|
|
151
|
+
(For Zarr format different ROIs are treated as channels)
|
|
152
|
+
flip_rois : bool, optional
|
|
153
|
+
Flag indicating whether to flip n-th ROIs horizontally for n > 1,
|
|
154
|
+
by default True.
|
|
155
|
+
'''
|
|
156
|
+
self.display_queue = display_queue
|
|
157
|
+
self.height = height
|
|
158
|
+
self.cam_height = height
|
|
159
|
+
self.index = 0
|
|
160
|
+
self.lock = threading.Lock()
|
|
161
|
+
self.major = 0
|
|
162
|
+
self.path = path
|
|
163
|
+
self.save_queue = save_queue
|
|
164
|
+
self.temp_queue = temp_queue
|
|
165
|
+
self.timestamp = time.strftime('_%Y_%m_%d_%H%M%S')
|
|
166
|
+
self.width = width
|
|
167
|
+
self.cam_width = width
|
|
168
|
+
self.channels = 1
|
|
169
|
+
self.zarr_array = None
|
|
170
|
+
|
|
171
|
+
self.biggTiff: bool = kwargs.get('biggTiff', True)
|
|
172
|
+
self.bytes_per_pixel: int = kwargs.get('bytes_per_pixel', 1)
|
|
173
|
+
self.exposure: float = kwargs.get('exposure', 50.0)
|
|
174
|
+
self.frames: int = kwargs.get('frames', 100)
|
|
175
|
+
self.full_tif_meta: bool = kwargs.get('full_tif_meta', True)
|
|
176
|
+
self.is_dark_cal: bool = kwargs.get('is_dark_cal', False)
|
|
177
|
+
self.meta_file: str = kwargs.get('meta_file', '')
|
|
178
|
+
self.meta_func: Callable = kwargs.get('meta_func', lambda *args: '')
|
|
179
|
+
self.name: str = kwargs.get('name', 'Camera')
|
|
180
|
+
self.prefix: str = kwargs.get('prefix', '')
|
|
181
|
+
self.save: bool = kwargs.get('save', False)
|
|
182
|
+
'''cross-thread attribute (use lock)'''
|
|
183
|
+
self.zarr: bool = kwargs.get('Zarr', False)
|
|
184
|
+
self.rois: list[list[int]] = kwargs.get('rois', [])
|
|
185
|
+
self.seperate_rois: bool = kwargs.get('seperate_rois', False)
|
|
186
|
+
self.flip_rois: bool = kwargs.get('flip_rois', True)
|
|
187
|
+
|
|
188
|
+
if self.rois:
|
|
189
|
+
same_width = all(
|
|
190
|
+
[self.rois[0][2] == roi[2] for roi in self.rois])
|
|
191
|
+
same_height = all(
|
|
192
|
+
[self.rois[0][3] == roi[3] for roi in self.rois])
|
|
193
|
+
if not same_width or not same_height:
|
|
194
|
+
self.rois = None
|
|
195
|
+
else:
|
|
196
|
+
self.width = self.rois[0][2]
|
|
197
|
+
self.height = self.rois[0][3]
|
|
198
|
+
self.channels = len(self.rois)
|
|
199
|
+
|
|
200
|
+
self.capture_done = False
|
|
201
|
+
'''cross-thread attribute (use lock)'''
|
|
202
|
+
self.capture_time = 0.0
|
|
203
|
+
'''cross-thread attribute (use lock)'''
|
|
204
|
+
self.display_done = False
|
|
205
|
+
'''cross-thread attribute (use lock)'''
|
|
206
|
+
self.display_time = 0.0
|
|
207
|
+
'''cross-thread attribute (use lock)'''
|
|
208
|
+
self.frame = None
|
|
209
|
+
'''cross-thread attribute (use lock)'''
|
|
210
|
+
self.frames_captured = 0
|
|
211
|
+
'''cross-thread attribute (use lock)'''
|
|
212
|
+
self.frames_saved = 0
|
|
213
|
+
'''cross-thread attribute (use lock)'''
|
|
214
|
+
self.save_time = 0.0
|
|
215
|
+
'''cross-thread attribute (use lock)'''
|
|
216
|
+
self.stop_threads = False
|
|
217
|
+
'''cross-thread attribute (use lock)'''
|
|
218
|
+
|
|
219
|
+
self.c_event = threading.Event()
|
|
220
|
+
self.s_event = kwargs.get('s_event', threading.Event())
|
|
221
|
+
|
|
222
|
+
self.tiffWriter: list[tf.TiffWriter] = []
|
|
223
|
+
self.dark_cal = None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if self.save:
|
|
227
|
+
if not os.path.exists(self.path):
|
|
228
|
+
os.makedirs(self.path)
|
|
229
|
+
|
|
230
|
+
while os.path.exists(self.getFilename()):
|
|
231
|
+
self.major += 1
|
|
232
|
+
|
|
233
|
+
self.writeMetaFile()
|
|
234
|
+
|
|
235
|
+
def getExt(self):
|
|
236
|
+
'''Return the file extension based on the Zarr attribute.
|
|
237
|
+
|
|
238
|
+
Returns
|
|
239
|
+
-------
|
|
240
|
+
str
|
|
241
|
+
File extension ('ome.tif' or 'zarr').
|
|
242
|
+
'''
|
|
243
|
+
return 'ome.tif' if not self.zarr else 'zarr'
|
|
244
|
+
|
|
245
|
+
def getFilename(self, roi_index: int=None) -> str:
|
|
246
|
+
'''Generate the filename based on the current state.
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
roi_index : int, optional
|
|
251
|
+
the roi index for file name suffix, if None then not added. Default is None.
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
str
|
|
256
|
+
Generated filename.
|
|
257
|
+
'''
|
|
258
|
+
roi_suffix = '' if roi_index is None else f'_ROI_{roi_index:02d}'
|
|
259
|
+
return self.path + \
|
|
260
|
+
f'{self.major:02d}_{self.prefix}_image_{self.index:05d}{roi_suffix}.{self.getExt()}'
|
|
261
|
+
|
|
262
|
+
def getTempFilename(self) -> str:
|
|
263
|
+
'''Generate the temperature log filename based on the current state.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
str
|
|
268
|
+
Generated temperature log filename.
|
|
269
|
+
'''
|
|
270
|
+
return self.path + \
|
|
271
|
+
f'{self.major:02d}_{self.prefix}_temp_log.csv'
|
|
272
|
+
|
|
273
|
+
def addDarkFrame(self, frame: np.ndarray):
|
|
274
|
+
'''Add a dark frame for calibration.
|
|
275
|
+
|
|
276
|
+
Parameters
|
|
277
|
+
----------
|
|
278
|
+
frame : np.ndarray
|
|
279
|
+
Dark frame data.
|
|
280
|
+
'''
|
|
281
|
+
if self.is_dark_cal:
|
|
282
|
+
if self.dark_cal is None:
|
|
283
|
+
self.dark_cal = dark_calibration(
|
|
284
|
+
frame.shape, self.exposure)
|
|
285
|
+
self.dark_cal.addFrame(frame)
|
|
286
|
+
|
|
287
|
+
def addFrame(self, frame: np.ndarray):
|
|
288
|
+
'''Add a frame to the acquisition job.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
frame : np.ndarray
|
|
293
|
+
Image frame data.
|
|
294
|
+
'''
|
|
295
|
+
if self.zarr:
|
|
296
|
+
if self.rois:
|
|
297
|
+
for idx, roi in enumerate(self.rois):
|
|
298
|
+
roi_data = frame[
|
|
299
|
+
roi[1]: roi[1] + roi[3],
|
|
300
|
+
roi[0]: roi[0] + roi[2]]
|
|
301
|
+
if idx > 0 and self.flip_rois:
|
|
302
|
+
roi_data = np.fliplr(roi_data)
|
|
303
|
+
|
|
304
|
+
self.getZarrArray()[self.frames_saved, idx, 0] = roi_data
|
|
305
|
+
else:
|
|
306
|
+
self.getZarrArray()[self.frames_saved, 0, 0] = frame
|
|
307
|
+
else:
|
|
308
|
+
if not self.tiffWriter:
|
|
309
|
+
if self.rois:
|
|
310
|
+
if self.seperate_rois:
|
|
311
|
+
for idx in range(len(self.rois)):
|
|
312
|
+
self.tiffWriter.append(
|
|
313
|
+
self.getTiffWriter(idx))
|
|
314
|
+
else:
|
|
315
|
+
self.tiffWriter.append(self.getTiffWriter())
|
|
316
|
+
else:
|
|
317
|
+
self.tiffWriter.append(self.getTiffWriter())
|
|
318
|
+
# append frame to tiff
|
|
319
|
+
if self.rois:
|
|
320
|
+
if self.seperate_rois:
|
|
321
|
+
for idx, roi in enumerate(self.rois):
|
|
322
|
+
roi_data = frame[
|
|
323
|
+
roi[1]: roi[1] + roi[3],
|
|
324
|
+
roi[0]: roi[0] + roi[2]]
|
|
325
|
+
if idx > 0 and self.flip_rois:
|
|
326
|
+
roi_data = np.fliplr(roi_data)
|
|
327
|
+
|
|
328
|
+
self.tiffWriter[idx] = self.writeTiffFrame(
|
|
329
|
+
self.tiffWriter[idx],
|
|
330
|
+
roi_data, idx)
|
|
331
|
+
else:
|
|
332
|
+
data = np.zeros(
|
|
333
|
+
(len(self.rois),
|
|
334
|
+
self.rois[0][3], self.rois[0][2]), dtype=frame.dtype)
|
|
335
|
+
for idx, roi in enumerate(self.rois):
|
|
336
|
+
data[idx] = frame[
|
|
337
|
+
roi[1]: roi[1] + roi[3],
|
|
338
|
+
roi[0]: roi[0] + roi[2]]
|
|
339
|
+
if idx > 0 and self.flip_rois:
|
|
340
|
+
data[idx] = np.fliplr(data[idx])
|
|
341
|
+
|
|
342
|
+
self.tiffWriter[0] = self.writeTiffFrame(
|
|
343
|
+
self.tiffWriter[0], data)
|
|
344
|
+
else:
|
|
345
|
+
self.tiffWriter[0] = self.writeTiffFrame(
|
|
346
|
+
self.tiffWriter[0], frame)
|
|
347
|
+
|
|
348
|
+
def writeTiffFrame(
|
|
349
|
+
self,
|
|
350
|
+
writer: tf.TiffWriter, frame: np.ndarray, roi_index=None):
|
|
351
|
+
'''writes an image into a Tiff file.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
writer : tf.TiffWriter
|
|
356
|
+
the TiffWriter class of the Tiff file.
|
|
357
|
+
frame : np.ndarray
|
|
358
|
+
Image frame data.
|
|
359
|
+
roi_index : int, optional
|
|
360
|
+
the roi index for file name suffix, if None then not added. Default is None.
|
|
361
|
+
'''
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
writer.write(
|
|
365
|
+
data=frame[np.newaxis, np.newaxis, np.newaxis, ...
|
|
366
|
+
] if frame.ndim == 2 else frame[
|
|
367
|
+
np.newaxis, :, np.newaxis, ...],
|
|
368
|
+
photometric='minisblack')
|
|
369
|
+
except ValueError as ve:
|
|
370
|
+
if str(ve) == \
|
|
371
|
+
'data too large for standard TIFF file':
|
|
372
|
+
writer.close()
|
|
373
|
+
self.saveMetadata(roi_index)
|
|
374
|
+
self.frames_saved = 0
|
|
375
|
+
self.index += 1
|
|
376
|
+
writer = self.getTiffWriter(roi_index)
|
|
377
|
+
writer.write(
|
|
378
|
+
data=frame[np.newaxis, np.newaxis, np.newaxis, ...
|
|
379
|
+
] if frame.ndim == 2 else frame[
|
|
380
|
+
np.newaxis, :, np.newaxis, ...],
|
|
381
|
+
photometric='minisblack')
|
|
382
|
+
else:
|
|
383
|
+
raise ve
|
|
384
|
+
|
|
385
|
+
return writer
|
|
386
|
+
|
|
387
|
+
def addTemp(self, temp: float):
|
|
388
|
+
'''Add temperature data to the log file.
|
|
389
|
+
|
|
390
|
+
Parameters
|
|
391
|
+
----------
|
|
392
|
+
temp : float
|
|
393
|
+
Temperature data.
|
|
394
|
+
'''
|
|
395
|
+
# open csv file and append sensor temp and close
|
|
396
|
+
with open(self.getTempFilename(), 'ab') as f:
|
|
397
|
+
np.savetxt(f, [temp], delimiter=';')
|
|
398
|
+
|
|
399
|
+
def getMetaFilename(self) -> str:
|
|
400
|
+
'''Return the metadata file path.
|
|
401
|
+
|
|
402
|
+
Returns
|
|
403
|
+
-------
|
|
404
|
+
str
|
|
405
|
+
Metadata file path.
|
|
406
|
+
'''
|
|
407
|
+
return self.path + \
|
|
408
|
+
f'{self.major:02d}_{self.prefix}' + self.name.replace(
|
|
409
|
+
' ', '_') + self.timestamp + '.json'
|
|
410
|
+
|
|
411
|
+
def writeMetaFile(self):
|
|
412
|
+
'''Write metadata to a file.'''
|
|
413
|
+
with open(self.getMetaFilename(), 'w+') as metaFile:
|
|
414
|
+
json.dump(self.meta_file, metaFile, indent=2)
|
|
415
|
+
|
|
416
|
+
def getTiffWriter(self, roi_index: int =None) -> tf.TiffWriter:
|
|
417
|
+
'''Return a TiffWriter instance for saving TIFF files.
|
|
418
|
+
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
roi_index : int, optional
|
|
422
|
+
the roi index for file name suffix, if None then not added. Default is None.
|
|
423
|
+
|
|
424
|
+
Returns
|
|
425
|
+
-------
|
|
426
|
+
tf.TiffWriter
|
|
427
|
+
TiffWriter instance.
|
|
428
|
+
'''
|
|
429
|
+
return tf.TiffWriter(
|
|
430
|
+
self.getFilename(roi_index), append=False,
|
|
431
|
+
bigtiff=self.biggTiff, ome=False)
|
|
432
|
+
|
|
433
|
+
def getZarrArray(self) -> zarr.Array:
|
|
434
|
+
'''Return the Zarr array for saving Zarr files.
|
|
435
|
+
|
|
436
|
+
Returns
|
|
437
|
+
-------
|
|
438
|
+
zarr.Array
|
|
439
|
+
Zarr array.
|
|
440
|
+
'''
|
|
441
|
+
if self.zarr_array is None:
|
|
442
|
+
self.zarr_array = zarr.open_array(
|
|
443
|
+
self.getFilename(),
|
|
444
|
+
shape=(self.frames, self.channels, 1, self.height, self.width),
|
|
445
|
+
compressor=None,
|
|
446
|
+
chunks=(1, 1, 1, self.height, self.width),
|
|
447
|
+
dtype=np.uint16)
|
|
448
|
+
return self.zarr_array
|
|
449
|
+
|
|
450
|
+
def saveMetadata(self, roi_index: int =None):
|
|
451
|
+
'''
|
|
452
|
+
Save metadata to the current file.
|
|
453
|
+
|
|
454
|
+
Parameters
|
|
455
|
+
----------
|
|
456
|
+
roi_index : int, optional
|
|
457
|
+
the roi index for file name suffix, if None then not added. Default is None.
|
|
458
|
+
'''
|
|
459
|
+
ome: om.OME = self.meta_func(
|
|
460
|
+
self.frames_saved, self.width, self.height,
|
|
461
|
+
1 if self.seperate_rois else self.channels)
|
|
462
|
+
|
|
463
|
+
tf.tiffcomment(self.getFilename(roi_index), ome.to_xml())
|
|
464
|
+
|
|
465
|
+
def finalize(self):
|
|
466
|
+
'''Finalize and clean up resources.'''
|
|
467
|
+
if self.dark_cal is not None:
|
|
468
|
+
if self.dark_cal._counter > 1:
|
|
469
|
+
self.dark_cal.saveResults(
|
|
470
|
+
self.path,
|
|
471
|
+
f'{self.major:02d}_{self.prefix}')
|
|
472
|
+
|
|
473
|
+
if self.tiffWriter:
|
|
474
|
+
if self.rois and self.seperate_rois:
|
|
475
|
+
for idx, writer in enumerate(self.tiffWriter):
|
|
476
|
+
writer.close()
|
|
477
|
+
if self.save and not self.zarr:
|
|
478
|
+
self.saveMetadata(idx)
|
|
479
|
+
else:
|
|
480
|
+
self.tiffWriter[0].close()
|
|
481
|
+
if self.save and not self.zarr:
|
|
482
|
+
self.saveMetadata()
|
|
483
|
+
|
|
484
|
+
def setDone(self, index: int, value: bool):
|
|
485
|
+
'''Set the completion status for a specific stage.
|
|
486
|
+
|
|
487
|
+
Parameters
|
|
488
|
+
----------
|
|
489
|
+
index : int
|
|
490
|
+
The index representing the stage (0 for capture, 1 for display, etc.).
|
|
491
|
+
value : bool
|
|
492
|
+
The completion status.
|
|
493
|
+
'''
|
|
494
|
+
if index == 0:
|
|
495
|
+
self.capture_done = value
|
|
496
|
+
elif index == 1:
|
|
497
|
+
self.display_done = value
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pyqtgraph as pg
|
|
3
|
+
import qdarkstyle
|
|
4
|
+
from pyqtgraph.multiprocess import proxy
|
|
5
|
+
|
|
6
|
+
from microEye.analysis.tools.kymograms import get_kymogram_row
|
|
7
|
+
from microEye.qt import QApplication, QtWidgets, Signal, Slot
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LineProfiler(QtWidgets.QWidget):
|
|
11
|
+
imageUpdate = Signal(np.ndarray)
|
|
12
|
+
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
super().__init__()
|
|
15
|
+
|
|
16
|
+
self.setWindowTitle('Line Profiler')
|
|
17
|
+
|
|
18
|
+
self.lineProfiles = []
|
|
19
|
+
self.image = np.random.normal(50, 2, (512, 512))
|
|
20
|
+
|
|
21
|
+
layout = QtWidgets.QFormLayout()
|
|
22
|
+
self.setLayout(layout)
|
|
23
|
+
|
|
24
|
+
params = QtWidgets.QHBoxLayout()
|
|
25
|
+
|
|
26
|
+
self.average = QtWidgets.QSpinBox()
|
|
27
|
+
self.average.setMinimum(1)
|
|
28
|
+
self.average.setMaximum(128)
|
|
29
|
+
self.average.setValue(1)
|
|
30
|
+
|
|
31
|
+
# params.addWidget(
|
|
32
|
+
# QLabel('Average [frames]'))
|
|
33
|
+
# params.addWidget(
|
|
34
|
+
# self.average)
|
|
35
|
+
self.data = np.random.normal(size=(512, 512))
|
|
36
|
+
self.remote_view = pg.GraphicsView()
|
|
37
|
+
pg.setConfigOptions(antialias=True, imageAxisOrder='row-major')
|
|
38
|
+
self.remote_plt = pg.ViewBox(invertY=True)
|
|
39
|
+
self.remote_view.setCentralItem(self.remote_plt)
|
|
40
|
+
self.remote_plt.setAspectLocked()
|
|
41
|
+
self.remote_img = pg.ImageItem(axisOrder='row-major')
|
|
42
|
+
self.remote_img.setImage(self.data)
|
|
43
|
+
|
|
44
|
+
self.roi = pg.ROI([10, 10], [0, 128], angle=0, pen='r')
|
|
45
|
+
self.roi.addTranslateHandle([0, 0], [0, 1])
|
|
46
|
+
self.roi.addScaleRotateHandle([0, 1], [0, 0])
|
|
47
|
+
self.roi.setZValue(10)
|
|
48
|
+
|
|
49
|
+
self.X, self.Y = self.getRoiCoords()
|
|
50
|
+
self.line_width = 16
|
|
51
|
+
|
|
52
|
+
self.remote_plt.addItem(self.remote_img)
|
|
53
|
+
self.remote_plt.addItem(self.roi)
|
|
54
|
+
|
|
55
|
+
self.line = pg.GraphicsView()
|
|
56
|
+
self.line.setMaximumHeight(250)
|
|
57
|
+
self.lineP = pg.PlotItem()
|
|
58
|
+
self.line.setCentralItem(self.lineP)
|
|
59
|
+
self.lineP_ref = self.lineP.plot()
|
|
60
|
+
|
|
61
|
+
def updateRoiCoords(cls=None):
|
|
62
|
+
self.X, self.Y = self.getRoiCoords()
|
|
63
|
+
|
|
64
|
+
self.roi.sigRegionChangeFinished.connect(updateRoiCoords)
|
|
65
|
+
|
|
66
|
+
self.setMinimumHeight(700)
|
|
67
|
+
layout.addRow(params)
|
|
68
|
+
layout.addRow(self.remote_view)
|
|
69
|
+
layout.addRow(self.line)
|
|
70
|
+
|
|
71
|
+
def setData(data: np.ndarray):
|
|
72
|
+
# if self.data.shape == data.shape:
|
|
73
|
+
# self.data[:, :] = data
|
|
74
|
+
# else:
|
|
75
|
+
# self.data = data.copy()
|
|
76
|
+
self.remote_img.setImage(data)
|
|
77
|
+
|
|
78
|
+
line_roi = get_kymogram_row(
|
|
79
|
+
data, self.X, self.Y, self.line_width)
|
|
80
|
+
self.lineP_ref.setData(line_roi)
|
|
81
|
+
|
|
82
|
+
self.imageUpdate.connect(setData)
|
|
83
|
+
|
|
84
|
+
def getRoiCoords(self):
|
|
85
|
+
dy = self.roi.size()[1] * np.cos(np.pi * self.roi.angle() / 180)
|
|
86
|
+
dx = self.roi.size()[1] * np.sin(np.pi * self.roi.angle() / 180)
|
|
87
|
+
x = self.roi.x()
|
|
88
|
+
y = self.roi.y()
|
|
89
|
+
return np.array([x, x - dx]), np.array([y, y + dy])
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == '__main__':
|
|
93
|
+
app = QApplication([])
|
|
94
|
+
app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt5'))
|
|
95
|
+
|
|
96
|
+
win = LineProfiler()
|
|
97
|
+
win.show()
|
|
98
|
+
|
|
99
|
+
app.exec()
|