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.
- pymodaq_plugins_beam_shaping/__init__.py +80 -0
- pymodaq_plugins_beam_shaping/algorithms/__init__.py +2 -0
- pymodaq_plugins_beam_shaping/algorithms/algo_base.py +378 -0
- pymodaq_plugins_beam_shaping/algorithms/algo_config.py +16 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithm_app.py +721 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/checker_board.py +100 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/diffractive.py +252 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/direct.py +65 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/minimizer.py +124 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/optim.py +103 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/projections.py +149 -0
- pymodaq_plugins_beam_shaping/algorithms/algorithms/torch_base.py +175 -0
- pymodaq_plugins_beam_shaping/algorithms/factory.py +88 -0
- pymodaq_plugins_beam_shaping/algorithms/ini_phase.py +102 -0
- pymodaq_plugins_beam_shaping/algorithms/ini_phases/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/algorithms/ini_phases/quadratic_phase.py +133 -0
- pymodaq_plugins_beam_shaping/algorithms/ini_phases/random_phase.py +10 -0
- pymodaq_plugins_beam_shaping/algorithms/loss.py +217 -0
- pymodaq_plugins_beam_shaping/algorithms/stopping.py +113 -0
- pymodaq_plugins_beam_shaping/algorithms/stoppings/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/algorithms/stoppings/stoppings_base.py +64 -0
- pymodaq_plugins_beam_shaping/algorithms/utils.py +51 -0
- pymodaq_plugins_beam_shaping/daq_move_plugins/__init__.py +14 -0
- pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_FocalLength.py +107 -0
- pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_MockAutoFocus.py +109 -0
- pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_MockShaper.py +135 -0
- pymodaq_plugins_beam_shaping/daq_move_plugins/daq_move_Zernike.py +117 -0
- pymodaq_plugins_beam_shaping/daq_viewer_plugins/__init__.py +3 -0
- pymodaq_plugins_beam_shaping/daq_viewer_plugins/plugins_2D/__init__.py +14 -0
- pymodaq_plugins_beam_shaping/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockAutoFocus.py +92 -0
- pymodaq_plugins_beam_shaping/daq_viewer_plugins/plugins_2D/daq_2Dviewer_MockShaperCamera.py +121 -0
- pymodaq_plugins_beam_shaping/extensions/__init__.py +6 -0
- pymodaq_plugins_beam_shaping/extensions/spatial_beam_shaping.py +688 -0
- pymodaq_plugins_beam_shaping/field/__init__.py +2 -0
- pymodaq_plugins_beam_shaping/field/factory.py +81 -0
- pymodaq_plugins_beam_shaping/field/field.py +273 -0
- pymodaq_plugins_beam_shaping/field/field_loader_app.py +561 -0
- pymodaq_plugins_beam_shaping/field/loaders/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/field/loaders/dashboard_loader.py +49 -0
- pymodaq_plugins_beam_shaping/field/loaders/from_functions_loader.py +639 -0
- pymodaq_plugins_beam_shaping/field/loaders/image_loader.py +117 -0
- pymodaq_plugins_beam_shaping/field/loaders/save_field_loader.py +51 -0
- pymodaq_plugins_beam_shaping/field/utils.py +120 -0
- pymodaq_plugins_beam_shaping/hardware/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/hardware/autofocus.py +189 -0
- pymodaq_plugins_beam_shaping/hardware/mock_shaper_camera.py +85 -0
- pymodaq_plugins_beam_shaping/models/OptimizerModelAutoFocus.py +102 -0
- pymodaq_plugins_beam_shaping/models/__init__.py +6 -0
- pymodaq_plugins_beam_shaping/models/datamixer_autofocus.py +62 -0
- pymodaq_plugins_beam_shaping/resources/Cemes - Logo - Sigle - Blanc.png +0 -0
- pymodaq_plugins_beam_shaping/resources/Papillon_flower_BW_resized.png +0 -0
- pymodaq_plugins_beam_shaping/resources/Pymodaq.png +0 -0
- pymodaq_plugins_beam_shaping/resources/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/resources/cheshirecat_rect.png +0 -0
- pymodaq_plugins_beam_shaping/resources/config_template.toml +93 -0
- pymodaq_plugins_beam_shaping/resources/darumas_BW_resized.png +0 -0
- pymodaq_plugins_beam_shaping/resources/dollars.png +0 -0
- pymodaq_plugins_beam_shaping/resources/dollars.svg +139 -0
- pymodaq_plugins_beam_shaping/resources/face.png +0 -0
- pymodaq_plugins_beam_shaping/resources/holography.xml +1 -0
- pymodaq_plugins_beam_shaping/resources/holography_calibration.xml +1 -0
- pymodaq_plugins_beam_shaping/resources/mock_holo.xml +1 -0
- pymodaq_plugins_beam_shaping/resources/one_piece.png +0 -0
- pymodaq_plugins_beam_shaping/resources/osama.png +0 -0
- pymodaq_plugins_beam_shaping/resources/zernike.png +0 -0
- pymodaq_plugins_beam_shaping/scanners/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/scanners/calibration.py +136 -0
- pymodaq_plugins_beam_shaping/scripts/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/scripts/calibrate_slm.py +54 -0
- pymodaq_plugins_beam_shaping/setup/factory.py +151 -0
- pymodaq_plugins_beam_shaping/setup/setup.py +69 -0
- pymodaq_plugins_beam_shaping/slm/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/slm/slm.py +13 -0
- pymodaq_plugins_beam_shaping/utilities/__init__.py +0 -0
- pymodaq_plugins_beam_shaping/utilities/calibrating.py +303 -0
- pymodaq_plugins_beam_shaping/utilities/corrections.py +310 -0
- pymodaq_plugins_beam_shaping/utilities/data.py +9 -0
- pymodaq_plugins_beam_shaping/utilities/h5saving.py +79 -0
- pymodaq_plugins_beam_shaping/utilities/masking.py +6 -0
- pymodaq_plugins_beam_shaping/utilities/shaper.py +35 -0
- pymodaq_plugins_beam_shaping/utilities/sizing.py +71 -0
- pymodaq_plugins_beam_shaping/utilities/zernike.py +93 -0
- pymodaq_plugins_beam_shaping/utils.py +19 -0
- pymodaq_plugins_beam_shaping-5.0.0.dist-info/METADATA +92 -0
- pymodaq_plugins_beam_shaping-5.0.0.dist-info/RECORD +89 -0
- pymodaq_plugins_beam_shaping-5.0.0.dist-info/WHEEL +4 -0
- pymodaq_plugins_beam_shaping-5.0.0.dist-info/entry_points.txt +14 -0
- 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,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
|
+
|