pymodaq_plugins_beam_shaping 5.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. pymodaq_plugins_beam_shaping/__init__.py +80 -0
  2. pymodaq_plugins_beam_shaping/algorithms/__init__.py +2 -0
  3. pymodaq_plugins_beam_shaping/algorithms/algo_base.py +378 -0
  4. pymodaq_plugins_beam_shaping/algorithms/algo_config.py +16 -0
  5. pymodaq_plugins_beam_shaping/algorithms/algorithm_app.py +721 -0
  6. pymodaq_plugins_beam_shaping/algorithms/algorithms/__init__.py +0 -0
  7. pymodaq_plugins_beam_shaping/algorithms/algorithms/checker_board.py +100 -0
  8. pymodaq_plugins_beam_shaping/algorithms/algorithms/diffractive.py +252 -0
  9. pymodaq_plugins_beam_shaping/algorithms/algorithms/direct.py +65 -0
  10. pymodaq_plugins_beam_shaping/algorithms/algorithms/minimizer.py +124 -0
  11. pymodaq_plugins_beam_shaping/algorithms/algorithms/optim.py +103 -0
  12. pymodaq_plugins_beam_shaping/algorithms/algorithms/projections.py +149 -0
  13. pymodaq_plugins_beam_shaping/algorithms/algorithms/torch_base.py +175 -0
  14. pymodaq_plugins_beam_shaping/algorithms/factory.py +88 -0
  15. pymodaq_plugins_beam_shaping/algorithms/ini_phase.py +102 -0
  16. pymodaq_plugins_beam_shaping/algorithms/ini_phases/__init__.py +0 -0
  17. pymodaq_plugins_beam_shaping/algorithms/ini_phases/quadratic_phase.py +133 -0
  18. pymodaq_plugins_beam_shaping/algorithms/ini_phases/random_phase.py +10 -0
  19. pymodaq_plugins_beam_shaping/algorithms/loss.py +217 -0
  20. pymodaq_plugins_beam_shaping/algorithms/stopping.py +113 -0
  21. pymodaq_plugins_beam_shaping/algorithms/stoppings/__init__.py +0 -0
  22. pymodaq_plugins_beam_shaping/algorithms/stoppings/stoppings_base.py +64 -0
  23. pymodaq_plugins_beam_shaping/algorithms/utils.py +51 -0
  24. pymodaq_plugins_beam_shaping/daq_move_plugins/__init__.py +14 -0
  25. pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_FocalLength.py +107 -0
  26. pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_MockAutoFocus.py +109 -0
  27. pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_MockShaper.py +135 -0
  28. pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_Zernike.py +117 -0
  29. pymodaq_plugins_beam_shaping/daq_viewer_plugins/__init__.py +3 -0
  30. pymodaq_plugins_beam_shaping/daq_viewer_plugins/plugins_2D/__init__.py +14 -0
  31. pymodaq_plugins_beam_shaping/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockAutoFocus.py +92 -0
  32. pymodaq_plugins_beam_shaping/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockShaperCamera.py +121 -0
  33. pymodaq_plugins_beam_shaping/extensions/__init__.py +6 -0
  34. pymodaq_plugins_beam_shaping/extensions/spatial_beam_shaping.py +688 -0
  35. pymodaq_plugins_beam_shaping/field/__init__.py +2 -0
  36. pymodaq_plugins_beam_shaping/field/factory.py +81 -0
  37. pymodaq_plugins_beam_shaping/field/field.py +273 -0
  38. pymodaq_plugins_beam_shaping/field/field_loader_app.py +561 -0
  39. pymodaq_plugins_beam_shaping/field/loaders/__init__.py +0 -0
  40. pymodaq_plugins_beam_shaping/field/loaders/dashboard_loader.py +49 -0
  41. pymodaq_plugins_beam_shaping/field/loaders/from_functions_loader.py +639 -0
  42. pymodaq_plugins_beam_shaping/field/loaders/image_loader.py +117 -0
  43. pymodaq_plugins_beam_shaping/field/loaders/save_field_loader.py +51 -0
  44. pymodaq_plugins_beam_shaping/field/utils.py +120 -0
  45. pymodaq_plugins_beam_shaping/hardware/__init__.py +0 -0
  46. pymodaq_plugins_beam_shaping/hardware/autofocus.py +189 -0
  47. pymodaq_plugins_beam_shaping/hardware/mock_shaper_camera.py +85 -0
  48. pymodaq_plugins_beam_shaping/models/OptimizerModelAutoFocus.py +102 -0
  49. pymodaq_plugins_beam_shaping/models/__init__.py +6 -0
  50. pymodaq_plugins_beam_shaping/models/datamixer_autofocus.py +62 -0
  51. pymodaq_plugins_beam_shaping/resources/Cemes - Logo - Sigle - Blanc.png +0 -0
  52. pymodaq_plugins_beam_shaping/resources/Papillon_flower_BW_resized.png +0 -0
  53. pymodaq_plugins_beam_shaping/resources/Pymodaq.png +0 -0
  54. pymodaq_plugins_beam_shaping/resources/__init__.py +0 -0
  55. pymodaq_plugins_beam_shaping/resources/cheshirecat_rect.png +0 -0
  56. pymodaq_plugins_beam_shaping/resources/config_template.toml +93 -0
  57. pymodaq_plugins_beam_shaping/resources/darumas_BW_resized.png +0 -0
  58. pymodaq_plugins_beam_shaping/resources/dollars.png +0 -0
  59. pymodaq_plugins_beam_shaping/resources/dollars.svg +139 -0
  60. pymodaq_plugins_beam_shaping/resources/face.png +0 -0
  61. pymodaq_plugins_beam_shaping/resources/holography.xml +1 -0
  62. pymodaq_plugins_beam_shaping/resources/holography_calibration.xml +1 -0
  63. pymodaq_plugins_beam_shaping/resources/mock_holo.xml +1 -0
  64. pymodaq_plugins_beam_shaping/resources/one_piece.png +0 -0
  65. pymodaq_plugins_beam_shaping/resources/osama.png +0 -0
  66. pymodaq_plugins_beam_shaping/resources/zernike.png +0 -0
  67. pymodaq_plugins_beam_shaping/scanners/__init__.py +0 -0
  68. pymodaq_plugins_beam_shaping/scanners/calibration.py +136 -0
  69. pymodaq_plugins_beam_shaping/scripts/__init__.py +0 -0
  70. pymodaq_plugins_beam_shaping/scripts/calibrate_slm.py +54 -0
  71. pymodaq_plugins_beam_shaping/setup/factory.py +151 -0
  72. pymodaq_plugins_beam_shaping/setup/setup.py +69 -0
  73. pymodaq_plugins_beam_shaping/slm/__init__.py +0 -0
  74. pymodaq_plugins_beam_shaping/slm/slm.py +13 -0
  75. pymodaq_plugins_beam_shaping/utilities/__init__.py +0 -0
  76. pymodaq_plugins_beam_shaping/utilities/calibrating.py +303 -0
  77. pymodaq_plugins_beam_shaping/utilities/corrections.py +310 -0
  78. pymodaq_plugins_beam_shaping/utilities/data.py +9 -0
  79. pymodaq_plugins_beam_shaping/utilities/h5saving.py +79 -0
  80. pymodaq_plugins_beam_shaping/utilities/masking.py +6 -0
  81. pymodaq_plugins_beam_shaping/utilities/shaper.py +35 -0
  82. pymodaq_plugins_beam_shaping/utilities/sizing.py +71 -0
  83. pymodaq_plugins_beam_shaping/utilities/zernike.py +93 -0
  84. pymodaq_plugins_beam_shaping/utils.py +19 -0
  85. pymodaq_plugins_beam_shaping-5.0.0.dist-info/METADATA +92 -0
  86. pymodaq_plugins_beam_shaping-5.0.0.dist-info/RECORD +89 -0
  87. pymodaq_plugins_beam_shaping-5.0.0.dist-info/WHEEL +4 -0
  88. pymodaq_plugins_beam_shaping-5.0.0.dist-info/entry_points.txt +14 -0
  89. pymodaq_plugins_beam_shaping-5.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,80 @@
1
+ from pathlib import Path
2
+ from pymodaq_utils.logger import set_logger, get_module_name # to be imported by other modules.
3
+ from .utils import Config
4
+
5
+
6
+ config = Config() # before field loader factory otherwise it will generate an error
7
+
8
+ from .field import LoaderFactory, register_loaders
9
+ from .algorithms.factory import AlgorithmFactory, register_algorithms
10
+ from .algorithms.ini_phase import register_phases
11
+ from .algorithms.stopping import register_stoppings
12
+
13
+ logger = set_logger(get_module_name(__file__))
14
+
15
+
16
+ logger.info('****************************************')
17
+ logger.info('Registering Optical Shaping algorithms')
18
+ register_algorithms()
19
+ logger.info('****************************************')
20
+
21
+ logger.info('****************************************')
22
+ logger.info('Registering Optical Shaping ini phases')
23
+ register_phases()
24
+ logger.info('****************************************')
25
+
26
+
27
+ logger.info('****************************************')
28
+ logger.info('Registering Optical Shaping stopping criteria')
29
+ register_stoppings()
30
+ logger.info('****************************************')
31
+
32
+
33
+ logger.info('****************************************')
34
+ logger.info('Registering Optical Shaping Field loaders')
35
+ register_loaders()
36
+ logger.info('****************************************')
37
+ logger.info('****************************************')
38
+
39
+ algo_factory = AlgorithmFactory()
40
+ loader_factory = LoaderFactory()
41
+
42
+ #check the presence of all registered algorithm in the config
43
+ registered_algorithms = algo_factory.algorithms
44
+ config_algorithms = config('algo', 'default_algo')
45
+
46
+ # add registered in config list
47
+ for algo in registered_algorithms:
48
+ if algo not in config_algorithms:
49
+ config_algorithms.append(algo)
50
+
51
+ #remove those that are not in registered
52
+ for algo in config_algorithms[:]:
53
+ if algo not in registered_algorithms:
54
+ config_algorithms.remove(algo)
55
+ config['algo', 'default_algo'] = config_algorithms
56
+
57
+ #check the presence of all registered field loader in the config
58
+ registered_field_loader = loader_factory.field_loaders
59
+ config_target_field_loader = config('target', 'default_loader')
60
+ config_input_field_loader = config('input', 'default_loader')
61
+
62
+ # add registered in config list
63
+ for loader in registered_field_loader:
64
+ if loader not in config_target_field_loader:
65
+ config_target_field_loader.append(loader)
66
+ if loader not in config_input_field_loader:
67
+ config_input_field_loader.append(loader)
68
+
69
+ #remove those that are not in registered
70
+ for loader in config_input_field_loader[:]:
71
+ if loader not in registered_field_loader:
72
+ config_input_field_loader.remove(loader)
73
+ # remove those that are not in registered
74
+ for loader in config_target_field_loader[:]:
75
+ if loader not in registered_field_loader:
76
+ config_target_field_loader.remove(loader)
77
+
78
+ config['target', 'default_loader'] = config_target_field_loader
79
+ config['input', 'default_loader'] = config_input_field_loader
80
+ config.save()
@@ -0,0 +1,2 @@
1
+ from .factory import AlgorithmFactory # noqa F403
2
+ from .algo_base import AlgoBase
@@ -0,0 +1,378 @@
1
+ from abc import ABCMeta
2
+ from typing import Optional, Union, Tuple, TYPE_CHECKING
3
+
4
+ import numpy as np
5
+ from scipy.ndimage.filters import gaussian_filter
6
+ from pyqtgraph.parametertree import Parameter
7
+ from qtpy import QtWidgets
8
+
9
+ from pymodaq_plugins_beam_shaping.utilities.shaper import get_shaper, ModulatorType
10
+ from pymodaq_utils.math_utils import gauss2D
11
+ from pymodaq_data import Q_, DataRaw, DataToExport
12
+ from pymodaq_gui.managers.parameter_manager import ParameterManager
13
+
14
+ from pymodaq_plugins_beam_shaping.algorithms.utils import (AlgoType, ApplyMaskTo,
15
+ LensSetup, CrossTalk, PhaseManipulation)
16
+ from pymodaq_plugins_beam_shaping.field import Field
17
+ from pymodaq_plugins_beam_shaping.utilities import sizing
18
+ from pymodaq_plugins_beam_shaping.utilities.masking import MaskType
19
+
20
+ from pymodaq_plugins_beam_shaping.utils import Config
21
+
22
+ if TYPE_CHECKING:
23
+ from pymodaq_plugins_beam_shaping.algorithms.algorithm_app import AlgoApp
24
+
25
+
26
+ plugin_config = Config()
27
+
28
+
29
+ class AlgoParameterManager(ParameterManager):
30
+ settings_name = 'algo_settings'
31
+
32
+ def __init__(self):
33
+ super().__init__()
34
+ self.settings_tree.header().setVisible(True)
35
+ self.settings_tree.header().setSectionResizeMode(
36
+ QtWidgets.QHeaderView.ResizeMode.Interactive)
37
+ self.settings_tree.header().setMinimumSectionSize(150)
38
+ self.settings_tree.setMinimumHeight(150)
39
+
40
+
41
+ class AlgoBase(AlgoParameterManager, metaclass=ABCMeta):
42
+ """
43
+ Here goes the abstract methods and shared properties/attributes of all algorithms used to
44
+ calculate amplitude/phase shaping
45
+ """
46
+
47
+ ALGO_NAME: str = None # to be reimplemented
48
+ SETUP_TYPE: LensSetup = None # to be reimplemented
49
+ ALGOTYPE = AlgoType.AMPLITUDE
50
+ INI_PHASE = True # if True, the algorithm will init an initial phase
51
+ ITERATIVE = False
52
+ MANUAL_LOOP = True
53
+ params = []
54
+
55
+
56
+ def __init__(self, parent: 'AlgoApp' = None,
57
+ crosstalk = CrossTalk(),
58
+ phase_wrap = PhaseManipulation(), ):
59
+
60
+ super().__init__()
61
+
62
+ self._running = False
63
+ self.fitness_name: str = ''
64
+
65
+ self._crosstalk: CrossTalk = crosstalk
66
+ self._phase_wrap: PhaseManipulation = phase_wrap
67
+
68
+ self.parent_app = parent
69
+ self._target_field = Field(amplitude=np.zeros(sizing.get_effective_needed_field_size()))
70
+ self._input_field = Field(amplitude=np.zeros(sizing.get_effective_needed_field_size()))
71
+ self.intermediate_field: Optional[Field] = None
72
+
73
+ self._modulator_field = Field(amplitude=np.zeros(sizing.get_effective_needed_field_size()))
74
+ self._modulator_field.calibrate_axes(self._input_field.pixels_sizes)
75
+ self._output_field = Field(amplitude=np.zeros(sizing.get_effective_needed_field_size()))
76
+
77
+ self.update_mask = True
78
+
79
+ @property
80
+ def crosstalk(self) -> CrossTalk:
81
+ return self._crosstalk
82
+
83
+ @crosstalk.setter
84
+ def crosstalk(self, crosstalk: CrossTalk):
85
+ self._crosstalk = crosstalk
86
+
87
+ @property
88
+ def phase_manipulation(self) -> PhaseManipulation:
89
+ return self._phase_wrap
90
+
91
+ @phase_manipulation.setter
92
+ def phase_manipulation(self, phase_wrap: PhaseManipulation):
93
+ self._phase_wrap = phase_wrap
94
+
95
+ def quit(self):
96
+ """ to reimplement if necessary"""
97
+ pass
98
+
99
+ def apply_mask(self, apply_to: Union[ApplyMaskTo, str]) -> bool:
100
+ return self.parent_app.apply_mask(apply_to)
101
+
102
+ def get_mask_slices(self, apply_to: Union[ApplyMaskTo, str]) -> tuple[slice, slice]:
103
+ """ Get the slices defined in the settings depending on the field it should apply to"""
104
+ return self.parent_app.get_mask_as_slices(apply_to)
105
+
106
+ def get_mask_field(self, apply_to: Union[ApplyMaskTo, str], inner_value=1, outer_value=0) -> Field:
107
+ """ Return a Field to be used to mask fields within the algorithm
108
+
109
+ To be reimplemented if needed
110
+ """
111
+ if self.apply_mask(apply_to):
112
+ mask = Field.init_from_field(self._target_field).amplitude * outer_value
113
+ slices = self.get_mask_slices(apply_to)
114
+
115
+ if self.get_mask_type(apply_to) == MaskType.SQUARE:
116
+ mask[*slices] = inner_value
117
+ else:
118
+ x = np.arange(0, self._target_field.shape[1], 1)
119
+ y = np.arange(0, self._target_field.shape[0], 1)
120
+ y0, x0 = tuple([(_slice.stop + _slice.start) / 2 for _slice in slices])
121
+ ry, rx = tuple([(_slice.stop - _slice.start) / 2 for _slice in slices])
122
+
123
+ xx, yy = np.meshgrid(x, y)
124
+ mask[
125
+ (xx - x0) ** 2 / rx ** 2 + (yy - y0) ** 2 / ry ** 2 <= 1] = inner_value
126
+ else:
127
+ mask = Field.init_from_field(self._target_field).amplitude
128
+ return Field(amplitude=mask)
129
+
130
+ def get_mask_type(self, apply_to: Union[ApplyMaskTo, str]) -> MaskType:
131
+ return self.parent_app.get_mask_type(apply_to)
132
+
133
+ @property
134
+ def output_field(self) -> Field:
135
+ return self._output_field
136
+
137
+ @property
138
+ def modulator_field(self) -> Field:
139
+ return self._modulator_field
140
+
141
+ @property
142
+ def target_field_pixels_sizes(self) -> tuple[Q_, Q_]:
143
+ return self._target_field.pixels_sizes
144
+
145
+ @property
146
+ def input_field_pixels_sizes(self) -> tuple[Q_, Q_]:
147
+ return self._input_field.pixels_sizes
148
+
149
+ @property
150
+ def shape(self):
151
+ return sizing.get_effective_needed_field_size()
152
+
153
+ def set_input_field(self, field: Field):
154
+ self._input_field = field
155
+ self.do_things_after_set_input()
156
+
157
+ def define_input_phase(self, phase):
158
+ phase = (phase + np.pi) % (2 * np.pi) - np.pi
159
+ self.set_phase_in_modulator_plane(phase)
160
+
161
+ def compute_forward_fft(self, update_plots = True):
162
+ """ Compute the forward fft usnig the "forward nomalization and the shape prefactor"""
163
+ self._output_field = self._modulator_field.fft2(norm='forward') * np.prod(self._modulator_field.shape)
164
+ self._output_field = self.scale_target_with_geometry(self._output_field)
165
+
166
+ if update_plots and self.parent_app is not None:
167
+ self.parent_app.fields_to_plot.emit(self.get_fields_to_plot())
168
+
169
+ def compute_backward_fft(self, field: Field) -> Field:
170
+ return field.ifft2(norm='forward')
171
+
172
+ @staticmethod
173
+ def normalize_wrt(field: Field, ref_field: Field) -> Field:
174
+ """ Make sure the intensities are normalized """
175
+ field.amplitude = field.amplitude * np.sqrt(np.sum(np.abs(ref_field.amplitude) ** 2) /
176
+ np.sum(np.abs(field.amplitude) ** 2))
177
+ return field
178
+
179
+ def stop(self):
180
+ self._running = False
181
+
182
+ def start(self):
183
+ self._running = True
184
+
185
+ def set_modulator_field(self, field: Field):
186
+ self._modulator_field = field
187
+ self.do_things_after_set_modulator()
188
+
189
+ def set_target_field(self, field: Field):
190
+ self._target_field = self.normalize_wrt(field, self._input_field)
191
+ self._output_field = Field.init_from_field(self._target_field)
192
+ self.do_things_after_set_target()
193
+
194
+ def set_target_intensity(self, intensity: np.ndarray):
195
+ self._target_field.amplitude = np.sqrt(intensity)
196
+
197
+ def set_field_in_modulator_plane(self, field: Field):
198
+ shaper = get_shaper()
199
+ if shaper.modulator_type == ModulatorType.PHASE:
200
+ self.set_phase_in_modulator_plane(field.phase)
201
+ elif shaper.modulator_type == ModulatorType.AMPLITUDE:
202
+ self.set_amplitude_in_modulator_plane(field.amplitude)
203
+ else:
204
+ self.set_phase_in_modulator_plane(field.phase)
205
+ self.set_amplitude_in_modulator_plane(field.amplitude)
206
+
207
+ def set_amplitude_in_modulator_plane(self, amplitude: np.ndarray):
208
+ self._modulator_field.amplitude = amplitude
209
+
210
+ def set_phase_in_modulator_plane(self, phase: np.ndarray, induced_amplitude: np.ndarray = None):
211
+ if phase.shape == self._modulator_field.shape:
212
+ phase = phase.copy()
213
+ if self.phase_manipulation.apply:
214
+ phase = phase % (self.phase_manipulation.wrap_value * np.pi)
215
+ phase = (np.round(
216
+ phase / (self.phase_manipulation.wrap_value * np.pi) * 2**self.phase_manipulation.dynamic_value) *
217
+ (self.phase_manipulation.wrap_value * np.pi) / 2**self.phase_manipulation.dynamic_value)
218
+ if self.crosstalk.apply:
219
+ phase = gaussian_filter(phase, self.crosstalk.value)
220
+
221
+ self._modulator_field.phase = phase
222
+ self._modulator_field.amplitude = (
223
+ self._input_field.amplitude.copy() *
224
+ (induced_amplitude if induced_amplitude is not None else 1))
225
+ else:
226
+ raise ValueError('The phase shape is incoherent with the parameters')
227
+
228
+ @property
229
+ def intensity_output(self):
230
+ return self._output_field.intensity
231
+
232
+ @property
233
+ def fitness(self) -> float:
234
+ """ Compute fitness with respect to the output_field and target_field
235
+
236
+ To be subclassed if the given implementation below is not correct for your algorithm"""
237
+ if self.ALGOTYPE == AlgoType.AMPLITUDE:
238
+ self.fitness_name = 'NRMSE'
239
+ return self.nrmse
240
+
241
+ elif self.ALGOTYPE == AlgoType.AMPLITUDE_PHASE:
242
+ self.fitness_name = 'Fidelity Error'
243
+ return 1 - self.fidelity_error
244
+ else:
245
+ raise TypeError('Algorithm type not supported')
246
+
247
+ @property
248
+ def fidelity_error(self) -> float:
249
+ if self.apply_mask(ApplyMaskTo.TARGET):
250
+ slices = self.get_mask_slices(ApplyMaskTo.TARGET)
251
+ else:
252
+ slices = (...,)
253
+ return 1- np.sqrt(np.abs(np.sum(np.conj(self._target_field.field[*slices]) * self._output_field.field[*slices])) ** 2 /
254
+ (np.sum(self._target_field.intensity[*slices] ** 2)))
255
+
256
+ @property
257
+ def nrmse(self) -> float:
258
+ if self.apply_mask(ApplyMaskTo.TARGET):
259
+ slices = self.get_mask_slices(ApplyMaskTo.TARGET)
260
+ else:
261
+ slices = (...,)
262
+ norm = 1
263
+
264
+ return np.sqrt(1 / norm * (
265
+ np.sum(
266
+ (self._target_field.intensity[*slices] - self._output_field.intensity[*slices]) ** 2) /
267
+ np.sum(self._target_field.intensity[*slices] ** 2)))
268
+
269
+ @property
270
+ def efficiency(self) -> float:
271
+ """ Compute efficiency as the ratio between output_field intensity within a given region
272
+ and total intensity
273
+
274
+ Meaningfully only for algorithm using a Target defined mask
275
+
276
+ To be subclassed if the given implementation below is not correct for your algorithm"""
277
+ if self.apply_mask(ApplyMaskTo.TARGET):
278
+ slices = self.get_mask_slices(ApplyMaskTo.TARGET)
279
+ else:
280
+ slices = (...,)
281
+
282
+ return (np.sum(self._output_field.intensity[*slices]) /
283
+ np.sum(self._output_field.intensity))
284
+
285
+ def fitness_as_dwa(self):
286
+ return DataRaw('fitness', data=[np.array([self.fitness])],
287
+ labels=[self.fitness_name])
288
+
289
+ def metrics_as_dwa(self):
290
+ return DataRaw('metrics', data=[np.array([self.fitness]),
291
+ np.array([self.efficiency])],
292
+ labels=[self.fitness_name, 'efficiency'])
293
+
294
+ def compute_phase(self, do_step=True, ini_phase: np.ndarray = None, **kwargs):
295
+ """ Compute the phase to apply to SLM given the target Field
296
+
297
+ To be subclassed in real implementation
298
+ """
299
+
300
+ raise NotImplementedError
301
+
302
+ def get_fields_to_plot(self) -> DataToExport:
303
+ dte = DataToExport('AlgoData', data=[
304
+ self.output_field.amplitude_as_dwa('output'),
305
+ self.output_field.phase_as_dwa('output'),
306
+ self.metrics_as_dwa(),
307
+ self.modulator_field.amplitude_as_dwa('modulator'),
308
+ self.modulator_field.phase_as_dwa('modulator'),
309
+ ])
310
+ if self.intermediate_field is not None:
311
+ dte.append(self.intermediate_field.intensity_as_dwa('intermediate'))
312
+ return dte
313
+
314
+ def do_things_after_init(self):
315
+ """ to reimplement if needed"""
316
+ pass
317
+
318
+ def do_things_after_set_input(self):
319
+ """ to reimplement if needed"""
320
+ pass
321
+
322
+ def do_things_after_set_target(self):
323
+ """ to reimplement if needed"""
324
+ pass
325
+
326
+ def do_things_after_set_modulator(self):
327
+ """ to reimplement if needed"""
328
+ pass
329
+
330
+
331
+ def scale_target_with_geometry(self, field: Field):
332
+ """ Apply an axis scaling to have the target and its axes in correct units with respect to
333
+ a given algorithm implementation and experimental setup
334
+ """
335
+ field.calibrate_axes(self.get_target_pixels_size())
336
+ return field
337
+
338
+ def get_target_pixels_size(self, input_size: Tuple[Q_, Q_] = None) -> list[Q_]:
339
+ """ Get the expected physical size of the pixels in the target plane given
340
+ the chosen algorithm and physical parameters: focal length, wavelength...
341
+
342
+ Here we use either a 4f setup or a 2f setup, so the output field has the same size as the SLM with a ratio given by the focal
343
+ length ratio
344
+
345
+ """
346
+ if input_size is None:
347
+ input_size = [self._input_field.shape[ind] * self._input_field.pixels_sizes[ind]
348
+ for ind in range(2)]
349
+ binning = plugin_config('sizing', 'binning')
350
+ if self.SETUP_TYPE == LensSetup.TwoF:
351
+ return [Q_(plugin_config('setup', 'wavelength_nm', ), 'nm') *
352
+ Q_(plugin_config('setup', self.SETUP_TYPE.value, 'focals')[0], 'mm') * binning /
353
+ size for size in input_size]
354
+
355
+ elif self.SETUP_TYPE == LensSetup.FourF:
356
+ pixels_size = self._input_field.pixels_sizes
357
+ focal_ratio = (plugin_config('setup', self.SETUP_TYPE.value, 'focals')[1] /
358
+ plugin_config('setup', self.SETUP_TYPE.value, 'focals')[0])
359
+ return [size * focal_ratio for size in pixels_size]
360
+
361
+ else:
362
+ raise NotImplementedError('The setup type is not recognized and cannot compute the pixels size')
363
+
364
+ @property
365
+ def intermediate_pixel_sizes(self):
366
+ if self.SETUP_TYPE == LensSetup.FourF:
367
+ slm_pixel_sizes = self._input_field.pixels_sizes
368
+ intermediate_pixel_sizes = [((Q_(plugin_config('setup', 'wavelength_nm',), 'nm') *
369
+ Q_(plugin_config('setup', self.SETUP_TYPE.value, 'focals')[0], 'mm')) /
370
+ (slm_pixel_sizes[ind] * self._target_field.shape[ind])
371
+ ).to('um') for ind in range(2)]
372
+
373
+ return intermediate_pixel_sizes
374
+ else:
375
+ raise ValueError('Intermediate pixel size can only be computed for 4f setups')
376
+
377
+ def value_changed(self, param: Parameter):
378
+ self.parent_app.algo_settings_changed()
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 19/11/2023
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+
8
+ from pathlib import Path
9
+ from pymodaq_utils.config import BaseConfig
10
+
11
+
12
+ class AlgoConfig(BaseConfig):
13
+ """ Config class to be used as a cache for latest configuration values of the algo"""
14
+ config_template_path = None
15
+ config_name = f"algo2D_settings"
16
+