pymodaq_plugins_utils 5.0.3__tar.gz → 5.0.4__tar.gz
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.
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/PKG-INFO +1 -1
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/hardware/camera_base_pylablib.py +145 -69
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.gitattributes +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.github/workflows/Test.yml +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.github/workflows/Testbase.yml +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.github/workflows/compatibility.yml +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.github/workflows/python-publish.yml +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.github/workflows/updater.yml +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.gitignore +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/LICENSE +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/README.rst +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/hatch_build.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/icon.ico +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/pyproject.toml +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/app/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/exporters/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/extensions/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/extensions/custom_extension_template.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/hardware/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/models/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/resources/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/resources/config_template.toml +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/scanners/__init__.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/utils.py +0 -0
- {pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/tests/test_plugin_package_structure.py +0 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Type
|
|
3
|
+
|
|
1
4
|
import cv2
|
|
5
|
+
|
|
6
|
+
from pymodaq_data import DataToExport
|
|
2
7
|
from pymodaq_utils.logger import set_logger, get_module_name
|
|
3
8
|
from pymodaq_utils.utils import ThreadCommand
|
|
4
9
|
from pymodaq_gui.parameter import Parameter
|
|
@@ -10,15 +15,20 @@ except ImportError:
|
|
|
10
15
|
from pymodaq.utils.data import DataFromPlugins, Axis
|
|
11
16
|
from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main
|
|
12
17
|
|
|
18
|
+
from pylablib.devices.interface.camera import trim_frames
|
|
19
|
+
|
|
13
20
|
from qtpy import QtWidgets, QtCore
|
|
14
21
|
import numpy as np
|
|
15
22
|
from time import perf_counter
|
|
16
23
|
|
|
17
24
|
|
|
25
|
+
logger = set_logger(get_module_name(__file__))
|
|
26
|
+
|
|
27
|
+
|
|
18
28
|
cam_params = [
|
|
19
29
|
{'title': 'Camera name:', 'name': 'camera_name', 'type': 'str', 'value': '', 'readonly': True},
|
|
20
|
-
{'title': '
|
|
21
|
-
|
|
30
|
+
{'title': 'Color Conversion:', 'name': 'color_conversion', 'type': 'list',
|
|
31
|
+
'limits': ['None', 'RGB2GRAY', 'BAYER_BG2RGB', 'BAYER_BG2GRAY']},
|
|
22
32
|
{'title': 'ROI', 'name': 'roi', 'type': 'group', 'children': [
|
|
23
33
|
{'title': 'Update ROI from Viewer', 'name': 'update_roi', 'type': 'led', 'value': False},
|
|
24
34
|
{'title': 'Apply ROI', 'name': 'apply_roi', 'type': 'led', 'value': False},
|
|
@@ -42,6 +52,60 @@ cam_params = [
|
|
|
42
52
|
]
|
|
43
53
|
|
|
44
54
|
|
|
55
|
+
@dataclasses.dataclass
|
|
56
|
+
class Grab:
|
|
57
|
+
do_acquisition: bool = True
|
|
58
|
+
snap: bool = False
|
|
59
|
+
since: str = 'now'
|
|
60
|
+
nframes: int = 1
|
|
61
|
+
n_average: int = 1
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CameraCallback(QtCore.QObject):
|
|
65
|
+
"""Callback object """
|
|
66
|
+
data_sig = QtCore.Signal(np.ndarray)
|
|
67
|
+
error = QtCore.Signal()
|
|
68
|
+
|
|
69
|
+
def __init__(self, controller):
|
|
70
|
+
super().__init__()
|
|
71
|
+
# Set the wait function
|
|
72
|
+
self.controller = controller
|
|
73
|
+
self.do_acquisition = True
|
|
74
|
+
|
|
75
|
+
def set_do_grab(self, mode: Grab):
|
|
76
|
+
self.do_acquisition = mode.do_acquisition
|
|
77
|
+
if mode.do_acquisition:
|
|
78
|
+
self.wait_for_acquisition(mode)
|
|
79
|
+
|
|
80
|
+
def wait_for_acquisition(self, mode: Grab):
|
|
81
|
+
while self.do_acquisition:
|
|
82
|
+
try:
|
|
83
|
+
ind_average = 0
|
|
84
|
+
while ind_average < mode.n_average:
|
|
85
|
+
ind_frames = 0
|
|
86
|
+
while ind_frames < mode.nframes:
|
|
87
|
+
self.controller.wait_for_frame(since='now')
|
|
88
|
+
new_frames, rng = self.controller.read_multiple_images(missing_frame='skip', return_rng=True)
|
|
89
|
+
if ind_average == 0 and ind_frames == 0:
|
|
90
|
+
shape = list(new_frames.shape[1:])
|
|
91
|
+
shape = [mode.n_average, mode.nframes] + shape
|
|
92
|
+
frames = np.zeros(shape, dtype=new_frames.dtype)
|
|
93
|
+
nacq = rng[1] - rng[0]
|
|
94
|
+
frames[ind_average, ind_frames:nacq, ...] = new_frames
|
|
95
|
+
ind_frames += nacq
|
|
96
|
+
ind_average += 1
|
|
97
|
+
self.data_sig.emit(frames)
|
|
98
|
+
QtCore.QThread.msleep(10)
|
|
99
|
+
except Exception as e:
|
|
100
|
+
logger.exception(str(e))
|
|
101
|
+
self.error.emit()
|
|
102
|
+
break
|
|
103
|
+
QtWidgets.QApplication.processEvents()
|
|
104
|
+
if not self.do_acquisition or mode.snap:
|
|
105
|
+
break
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
45
109
|
class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
46
110
|
"""
|
|
47
111
|
Base implementation for Camera using pylablib framework. Works for TSI and uc480 thorlabs camera and rpobaly others
|
|
@@ -52,13 +116,15 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
52
116
|
|
|
53
117
|
params = comon_parameters + serial_params + cam_params
|
|
54
118
|
|
|
55
|
-
callback_signal = QtCore.Signal(
|
|
119
|
+
callback_signal = QtCore.Signal(Grab)
|
|
56
120
|
live_mode_available = True
|
|
57
|
-
|
|
121
|
+
hardware_averaging = True
|
|
58
122
|
|
|
59
123
|
def ini_attributes(self):
|
|
60
124
|
self.controller = None
|
|
61
125
|
self.callback_thread: QtCore.QThread = None
|
|
126
|
+
self.is_live: bool = False
|
|
127
|
+
self.Naverage: int = 1
|
|
62
128
|
|
|
63
129
|
self.x_axis: Axis = None
|
|
64
130
|
self.y_axis: Axis = None
|
|
@@ -135,16 +201,16 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
135
201
|
if param.name() == "exposure_time":
|
|
136
202
|
self.controller.set_exposure(param.value()/1000)
|
|
137
203
|
|
|
138
|
-
|
|
204
|
+
elif param.name() == "fps_on":
|
|
139
205
|
self.settings.child('timing_opts', 'fps').setOpts(visible=param.value())
|
|
140
206
|
|
|
141
|
-
|
|
207
|
+
elif param.name() == "apply_roi":
|
|
142
208
|
if param.value(): # Switching on ROI
|
|
143
209
|
self.apply_roi()
|
|
144
210
|
else:
|
|
145
211
|
self.clear_roi()
|
|
146
212
|
|
|
147
|
-
|
|
213
|
+
elif param.name() in ['x_binning', 'y_binning']:
|
|
148
214
|
# We handle ROI and binning separately for clarity
|
|
149
215
|
(x0, w, y0, h, *_) = self.controller.get_roi() # Get current ROI
|
|
150
216
|
xbin = self.settings['roi', 'x_binning']
|
|
@@ -152,14 +218,11 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
152
218
|
new_roi = (x0, w, xbin, y0, h, ybin)
|
|
153
219
|
self.update_rois(new_roi)
|
|
154
220
|
|
|
155
|
-
|
|
221
|
+
elif param.name() == "clear_roi":
|
|
156
222
|
if param.value(): # Switching on ROI
|
|
157
223
|
self.clear_roi()
|
|
158
224
|
param.setValue(False)
|
|
159
225
|
|
|
160
|
-
if param.name() == 'sensor':
|
|
161
|
-
self.get_set_color()
|
|
162
|
-
|
|
163
226
|
def ini_detector_custom(self, controller=None):
|
|
164
227
|
raise NotImplementedError
|
|
165
228
|
|
|
@@ -178,12 +241,13 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
178
241
|
initialized: bool
|
|
179
242
|
False if initialization failed otherwise True
|
|
180
243
|
"""
|
|
244
|
+
|
|
181
245
|
self.ini_detector_custom(controller)
|
|
182
246
|
|
|
183
247
|
self.get_device_info()
|
|
184
|
-
self.get_set_color()
|
|
185
248
|
self.get_set_main_parameters()
|
|
186
249
|
self.setup_callback_thread()
|
|
250
|
+
self.controller.set_frame_format("array")
|
|
187
251
|
|
|
188
252
|
info = "Initialized camera"
|
|
189
253
|
initialized = True
|
|
@@ -199,13 +263,6 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
199
263
|
elif hasattr(device_info, 'model'):
|
|
200
264
|
self.settings.child('camera_name').setValue(device_info.model)
|
|
201
265
|
|
|
202
|
-
def get_set_color(self):
|
|
203
|
-
if 'monochrome' in self.settings['sensor'].lower():
|
|
204
|
-
self.settings.child('output_color').setValue('MonoChrome')
|
|
205
|
-
self.settings.child('output_color').setOpts(visible=False)
|
|
206
|
-
else:
|
|
207
|
-
self.settings.child('output_color').setOpts(visible=True)
|
|
208
|
-
|
|
209
266
|
def get_set_main_parameters(self):
|
|
210
267
|
# Set exposure time
|
|
211
268
|
self.controller.set_exposure(self.settings['timing_opts', 'exposure_time']/1000)
|
|
@@ -227,18 +284,28 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
227
284
|
self.settings.child('roi', 'roi_slices').setValue(str(slices))
|
|
228
285
|
self.compute_axes()
|
|
229
286
|
|
|
287
|
+
@property
|
|
288
|
+
def callback(self) -> Type[CameraCallback]:
|
|
289
|
+
""" Return the class handling the wait for acquisition and signal emission
|
|
290
|
+
|
|
291
|
+
Should be reimplement as well as CameraCallback if needed
|
|
292
|
+
"""
|
|
293
|
+
return CameraCallback
|
|
294
|
+
|
|
230
295
|
def setup_callback_thread(self):
|
|
231
296
|
# Way to define a wait function with arguments
|
|
232
297
|
wait_func = lambda: self.controller.wait_for_frame(since=self.settings['buffer', 'mode'],
|
|
233
298
|
nframes=1, timeout=20.0)
|
|
234
|
-
callback = CameraCallback(
|
|
299
|
+
callback = CameraCallback(self.controller)
|
|
235
300
|
self.settings.child('buffer', 'mode').setReadonly(True)
|
|
236
301
|
|
|
237
302
|
|
|
238
303
|
self.callback_thread = QtCore.QThread() # creation of a Qt5 thread
|
|
239
304
|
callback.moveToThread(self.callback_thread) # callback object will live within this thread
|
|
305
|
+
|
|
240
306
|
callback.data_sig.connect(
|
|
241
307
|
self.emit_data) # when the wait for acquisition returns (with data taken), emit_data will be fired
|
|
308
|
+
callback.error.connect(self.handle_error)
|
|
242
309
|
|
|
243
310
|
self.callback_signal.connect(callback.set_do_grab)
|
|
244
311
|
self.callback_thread.callback = callback
|
|
@@ -246,6 +313,8 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
246
313
|
|
|
247
314
|
self._prepare_view()
|
|
248
315
|
|
|
316
|
+
def handle_error(self):
|
|
317
|
+
self.stop()
|
|
249
318
|
|
|
250
319
|
def _prepare_view(self):
|
|
251
320
|
"""Preparing a data viewer by emitting temporary data. Typically, needs to be called whenever the
|
|
@@ -280,14 +349,20 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
280
349
|
"""
|
|
281
350
|
try:
|
|
282
351
|
# Warning, acquisition_in_progress returns 1,0 and not a real bool
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
self.
|
|
352
|
+
self.is_live = kwargs.get('live', False)
|
|
353
|
+
self.Naverage = Naverage
|
|
354
|
+
|
|
355
|
+
self.n_frames = 1
|
|
356
|
+
|
|
357
|
+
if not self.controller.acquisition_in_progress():
|
|
358
|
+
self.controller.clear_acquisition()
|
|
359
|
+
self.controller.start_acquisition(nframes=self.n_frames)
|
|
360
|
+
#Then start the acquisition
|
|
361
|
+
self.callback_signal.emit(Grab(do_acquisition=True,
|
|
362
|
+
snap=not self.is_live,
|
|
363
|
+
n_average=Naverage,
|
|
364
|
+
nframes=self.n_frames,
|
|
365
|
+
since=self.settings['buffer', 'mode']))
|
|
291
366
|
|
|
292
367
|
except Exception as e:
|
|
293
368
|
self.emit_status(ThreadCommand('Update_Status', [str(e), "log"]))
|
|
@@ -309,21 +384,48 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
309
384
|
if frame is None:
|
|
310
385
|
frame = self.controller.read_newest_image()
|
|
311
386
|
# Emit the frame.
|
|
312
|
-
if frame is not None:
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
387
|
+
if frame is not None:
|
|
388
|
+
conversion_str = self.settings['color_conversion']
|
|
389
|
+
if conversion_str != "None":
|
|
390
|
+
for ind_average in range(frame.shape[0]):
|
|
391
|
+
for ind_frame in range(frame.shape[1]):
|
|
392
|
+
if ind_frame == 0 and ind_average == 0:
|
|
393
|
+
new_frame = cv2.cvtColor(frame[ind_average, ind_frame, ...],
|
|
394
|
+
getattr(cv2, f'COLOR_{conversion_str}'))
|
|
395
|
+
shape = [frame.shape[0], frame.shape[1]] + list(new_frame.shape)
|
|
396
|
+
out_frames = np.zeros(shape, dtype=new_frame.dtype)
|
|
397
|
+
out_frames[ind_average, ind_frame, ...] = new_frame
|
|
398
|
+
else:
|
|
399
|
+
cv2.cvtColor(frame[ind_average, ind_frame, ...],
|
|
400
|
+
getattr(cv2, f'COLOR_{conversion_str}'),
|
|
401
|
+
out_frames[ind_average, ind_frame, ...])
|
|
402
|
+
else:
|
|
403
|
+
out_frames = frame
|
|
404
|
+
if self.Naverage > 1:
|
|
405
|
+
out_frames = np.sum(out_frames, axis=0) / self.Naverage
|
|
406
|
+
else:
|
|
407
|
+
out_frames = out_frames[0, ...]
|
|
408
|
+
|
|
409
|
+
if self.n_frames > 1:
|
|
410
|
+
pass
|
|
411
|
+
#todo handle chunks of frames in ND data
|
|
412
|
+
else:
|
|
413
|
+
out_frames = out_frames[0, ...]
|
|
414
|
+
|
|
415
|
+
if out_frames.shape[-1] == 3:
|
|
416
|
+
data_arrays = [np.atleast_1d(out_frames[..., ind]) for ind in range(3)]
|
|
417
|
+
labels = ['Red', 'Green', 'Blue']
|
|
316
418
|
else:
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
419
|
+
labels = ['Intensity']
|
|
420
|
+
data_arrays = [out_frames]
|
|
421
|
+
|
|
422
|
+
self.dte_signal.emit(
|
|
423
|
+
DataToExport('Camera',
|
|
424
|
+
data=[DataFromPlugins(name='Camera',
|
|
425
|
+
data=data_arrays,
|
|
426
|
+
dim=self.data_shape,
|
|
427
|
+
labels=labels,
|
|
428
|
+
axes=[self.y_axis, self.x_axis])]))
|
|
327
429
|
if self.settings.child('timing_opts', 'fps_on').value():
|
|
328
430
|
self.update_fps()
|
|
329
431
|
|
|
@@ -367,37 +469,11 @@ class CameraBasePyLabLib(DAQ_Viewer_base):
|
|
|
367
469
|
|
|
368
470
|
def stop(self):
|
|
369
471
|
"""Stop the acquisition."""
|
|
370
|
-
self.callback_signal.emit(False)
|
|
472
|
+
self.callback_signal.emit(Grab(do_acquisition=False))
|
|
371
473
|
QtWidgets.QApplication.processEvents()
|
|
372
|
-
|
|
373
474
|
self.controller.clear_acquisition()
|
|
374
475
|
return ''
|
|
375
476
|
|
|
376
477
|
|
|
377
|
-
class CameraCallback(QtCore.QObject):
|
|
378
|
-
"""Callback object """
|
|
379
|
-
data_sig = QtCore.Signal()
|
|
380
|
-
|
|
381
|
-
def __init__(self, wait_fn):
|
|
382
|
-
super().__init__()
|
|
383
|
-
# Set the wait function
|
|
384
|
-
self.wait_fn = wait_fn
|
|
385
|
-
self.do_grab = True
|
|
386
|
-
|
|
387
|
-
def set_do_grab(self, do_grab=True):
|
|
388
|
-
self.do_grab = do_grab
|
|
389
|
-
if do_grab:
|
|
390
|
-
self.wait_for_acquisition()
|
|
391
|
-
|
|
392
|
-
def wait_for_acquisition(self):
|
|
393
|
-
while self.do_grab:
|
|
394
|
-
try:
|
|
395
|
-
new_data = self.wait_fn()
|
|
396
|
-
if new_data is not False: # will be returned if the main thread called CancelWait
|
|
397
|
-
self.data_sig.emit()
|
|
398
|
-
except Exception as e:
|
|
399
|
-
pass
|
|
400
|
-
QtWidgets.QApplication.processEvents()
|
|
401
|
-
|
|
402
478
|
|
|
403
479
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.github/workflows/compatibility.yml
RENAMED
|
File without changes
|
{pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/.github/workflows/python-publish.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/src/pymodaq_plugins_utils/utils.py
RENAMED
|
File without changes
|
{pymodaq_plugins_utils-5.0.3 → pymodaq_plugins_utils-5.0.4}/tests/test_plugin_package_structure.py
RENAMED
|
File without changes
|