ewoksid02 0.1.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 (54) hide show
  1. ewoksid02/__init__.py +0 -0
  2. ewoksid02/ocl/__init__.py +0 -0
  3. ewoksid02/resources/__init__.py +8 -0
  4. ewoksid02/resources/saxs_loop.json +96 -0
  5. ewoksid02/resources/template_saxs.yaml +37 -0
  6. ewoksid02/scripts/__init__.py +0 -0
  7. ewoksid02/scripts/__main__.py +70 -0
  8. ewoksid02/scripts/parsers.py +224 -0
  9. ewoksid02/scripts/saxs/__init__.py +0 -0
  10. ewoksid02/scripts/saxs/main.py +255 -0
  11. ewoksid02/scripts/saxs/slurm_python_post_script.py +3 -0
  12. ewoksid02/scripts/saxs/slurm_python_pre_script.py +5 -0
  13. ewoksid02/scripts/utils.py +21 -0
  14. ewoksid02/scripts/xpcs/__init__.py +0 -0
  15. ewoksid02/scripts/xpcs/__main__.py +3 -0
  16. ewoksid02/tasks/__init__.py +7 -0
  17. ewoksid02/tasks/averagetask.py +179 -0
  18. ewoksid02/tasks/azimuthaltask.py +272 -0
  19. ewoksid02/tasks/cavingtask.py +170 -0
  20. ewoksid02/tasks/dahuprocessingtask.py +71 -0
  21. ewoksid02/tasks/end.py +35 -0
  22. ewoksid02/tasks/id02processingtask.py +2582 -0
  23. ewoksid02/tasks/looptask.py +672 -0
  24. ewoksid02/tasks/metadatatask.py +879 -0
  25. ewoksid02/tasks/normalizationtask.py +204 -0
  26. ewoksid02/tasks/scalerstask.py +46 -0
  27. ewoksid02/tasks/secondaryscatteringtask.py +159 -0
  28. ewoksid02/tasks/sumtask.py +45 -0
  29. ewoksid02/tests/__init__.py +3 -0
  30. ewoksid02/tests/conftest.py +639 -0
  31. ewoksid02/tests/debug.py +64 -0
  32. ewoksid02/tests/test_2scat_node.py +119 -0
  33. ewoksid02/tests/test_ave_node.py +106 -0
  34. ewoksid02/tests/test_azim_node.py +89 -0
  35. ewoksid02/tests/test_cave_node.py +118 -0
  36. ewoksid02/tests/test_norm_node.py +190 -0
  37. ewoksid02/tests/test_saxs.py +69 -0
  38. ewoksid02/tests/test_sumtask.py +10 -0
  39. ewoksid02/tests/utils.py +514 -0
  40. ewoksid02/utils/__init__.py +22 -0
  41. ewoksid02/utils/average.py +158 -0
  42. ewoksid02/utils/blissdata.py +1157 -0
  43. ewoksid02/utils/caving.py +851 -0
  44. ewoksid02/utils/cupyutils.py +42 -0
  45. ewoksid02/utils/io.py +722 -0
  46. ewoksid02/utils/normalization.py +804 -0
  47. ewoksid02/utils/pyfai.py +424 -0
  48. ewoksid02/utils/secondaryscattering.py +597 -0
  49. ewoksid02-0.1.0.dist-info/METADATA +76 -0
  50. ewoksid02-0.1.0.dist-info/RECORD +54 -0
  51. ewoksid02-0.1.0.dist-info/WHEEL +5 -0
  52. ewoksid02-0.1.0.dist-info/entry_points.txt +5 -0
  53. ewoksid02-0.1.0.dist-info/licenses/LICENSE.md +20 -0
  54. ewoksid02-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,851 @@
1
+ import numpy
2
+
3
+ from .io import get_persistent_array_mask
4
+
5
+ try:
6
+ import cupy
7
+ from .cupyutils import log_allocated_gpu_memory
8
+ except ImportError:
9
+ CUPY_AVAILABLE = False
10
+ CUPY_MEM_POOL = None
11
+ cupy = numpy
12
+ else:
13
+ CUPY_AVAILABLE = True
14
+ CUPY_MEM_POOL = cupy.get_default_memory_pool()
15
+ import logging
16
+
17
+ MASK_PIXELS_AVAILABLE = None
18
+
19
+ logger = logging.getLogger(__name__)
20
+ logger.setLevel(logging.INFO)
21
+
22
+
23
+ def get_index_shifted(
24
+ data_shape: tuple,
25
+ center_x: int,
26
+ center_y: int,
27
+ ):
28
+ """Calculates the x,y index vectors, shifted to recreate the centro-symmetric pixels
29
+
30
+ Inputs:
31
+ - data_shape (tuple): shape of the data array (2 dimensional)
32
+ - center_x (int): beam center in the first data dimension
33
+ - center_y (int): beam center in the second data dimension
34
+ Outputs:
35
+ Tuple: pair of shifted x,y index vectors
36
+ """
37
+ y, x = numpy.meshgrid(
38
+ numpy.arange(data_shape[0]),
39
+ numpy.arange(data_shape[1]),
40
+ indexing="ij",
41
+ sparse=True,
42
+ )
43
+
44
+ x_ = 2 * int(center_x) - x + 1
45
+ y_ = 2 * int(center_y) - y + 1
46
+
47
+ # Think it's correct
48
+ return x_, y_
49
+
50
+
51
+ def get_position_vectors(
52
+ data_shape: tuple,
53
+ use_cupy: bool = False,
54
+ ):
55
+ """Creates the x,y index vector of a 2-dimensional data array
56
+
57
+ Inputs:
58
+ - data_shape (tuple): shape of the data array (2 dimensional)
59
+ - use_cupy (bool): transfer the vectors to the GPU, ready to process using cupy
60
+ Outputs:
61
+ - Tuple: pair of x-y index vector
62
+ """
63
+ y_vector, x_vector = numpy.meshgrid(
64
+ numpy.arange(data_shape[0]),
65
+ numpy.arange(data_shape[1]),
66
+ indexing="ij",
67
+ sparse=True,
68
+ )
69
+ if use_cupy:
70
+ return cupy.asarray(x_vector), cupy.asarray(y_vector)
71
+ return x_vector, y_vector
72
+
73
+
74
+ def shift_position_vectors(
75
+ x_vector: numpy.ndarray,
76
+ y_vector: numpy.ndarray,
77
+ center_x: int,
78
+ center_y: int,
79
+ use_cupy: bool = False,
80
+ ):
81
+ """Shift a pair of index vectors, to point to centro-symmetric brothers
82
+
83
+ Inputs:
84
+ - x_vector (numpy.ndarray) : index vector along the first dimension of array
85
+ - y_vector (numpy.ndarray) : index vector along the second dimension of array
86
+ - center_x (int): beam center in the first data dimension
87
+ - center_y (int): beam center in the second data dimension
88
+ - use_cupy (bool): the return result is transfered to the GPU using cupy
89
+ Outputs:
90
+ - Tuple: pair of shifted x,y index vectors
91
+ """
92
+ center_x = int(center_x)
93
+ center_y = int(center_y)
94
+ x_shifted = 2 * center_x - x_vector + 1
95
+ y_shifted = 2 * center_y - y_vector + 1
96
+ if use_cupy:
97
+ return cupy.asarray(x_shifted), cupy.asarray(y_shifted)
98
+ return x_shifted, y_shifted
99
+
100
+
101
+ def get_mask_limits(
102
+ data_shape: tuple = None,
103
+ center_x: int = None,
104
+ center_y: int = None,
105
+ x_shifted: numpy.ndarray = None,
106
+ y_shifted: numpy.ndarray = None,
107
+ only_x_dim: bool = False,
108
+ only_y_dim: bool = False,
109
+ use_cupy: bool = False,
110
+ ):
111
+ """
112
+ Mask limits is the primitive mask, it defines the area where the cave can be applied.
113
+ It's the area of the pixels whose centro-symmetric brother falls in the detector surface.
114
+ If the center of the beam matches with the center of the detector, the whole detector area can be caved.
115
+ If the center falls into the edge of the detector, no pixel can be caved.
116
+
117
+ Inputs:
118
+ - data_shape (tuple): shape of the data array (2 dimensional)
119
+ - center_x (int): beam center in the first data dimension
120
+ - center_y (int): beam center in the second data dimension
121
+ - x_shifted (numpy.ndarray) : shifted index vector along the first data dimension
122
+ - y_shifted (numpy.ndarray) : shifted index vector along the second data dimension
123
+ - only_x_dim (bool): if True, returns the mask of the first dimension limits
124
+ - only_y_dim (bool): if True, returns the mask of the second dimension limits
125
+ - use_cupy (bool): the return result is transfered to the GPU using cupy
126
+ Outputs:
127
+ - numpy.ndarray / cupy.ndarray: array to mask the pixels whose centrosymmetric brother falls out of the detector area.
128
+ """
129
+ if x_shifted is None or y_shifted is None:
130
+ x_vector, y_vector = get_position_vectors(
131
+ data_shape=data_shape,
132
+ use_cupy=use_cupy,
133
+ )
134
+ x_shifted, y_shifted = shift_position_vectors(
135
+ x_vector=x_vector,
136
+ y_vector=y_vector,
137
+ center_x=center_x,
138
+ center_y=center_y,
139
+ use_cupy=use_cupy,
140
+ )
141
+ x_max = x_shifted.shape[-1]
142
+ y_max = y_shifted.shape[0]
143
+ if use_cupy:
144
+ mask_limits_x_cupy = cupy.logical_and(x_shifted > 0, x_shifted < x_max)
145
+ if only_x_dim:
146
+ return mask_limits_x_cupy
147
+ mask_limits_y_cupy = cupy.logical_and(y_shifted > 0, y_shifted < y_max)
148
+ if only_y_dim:
149
+ return mask_limits_y_cupy
150
+ mask_limits_cupy = mask_limits_x_cupy & mask_limits_y_cupy # boolean
151
+ return mask_limits_cupy
152
+ mask_limits_x = numpy.logical_and(x_shifted > 0, x_shifted < x_max)
153
+ if only_x_dim:
154
+ return mask_limits_x
155
+ mask_limits_y = numpy.logical_and(y_shifted > 0, y_shifted < y_max)
156
+ if only_y_dim:
157
+ return mask_limits_y
158
+ mask_limits = mask_limits_x & mask_limits_y # boolean
159
+ return mask_limits
160
+
161
+
162
+ def _mask_caving(
163
+ data_shape: tuple,
164
+ Center_1: int,
165
+ Center_2: int,
166
+ mask_reference: numpy.ndarray = None,
167
+ vertical_symmetry: bool = True,
168
+ horizontal_symmetry: bool = True,
169
+ use_cupy=False,
170
+ ) -> numpy.ndarray:
171
+ """Data-free method, generates the mask with the pixels available. Those pixels whose intensity can be substitued by its centrosymmetric, horz-symmetric or vert-symmetric brother.
172
+ It's determined by the position of the beam center, (the closer to the array center, the more pixels available), and the mask_reference, that determines which pixels will not be caved.
173
+
174
+ Inputs:
175
+ - data_shape (tuple): shape of the data array (2 dimensional)
176
+ - Center_1 (int): beam center in the first data dimension
177
+ - Center_2 (int): beam center in the second data dimension
178
+ - mask_reference (numpy.ndarray): mask with the pixels whose intensity should not be used, that means that their symmetric brother should not be caved
179
+ - vertical_symmetry (bool): the caving performs a vertical flip around the beam center
180
+ - horizontal_symmetry (bool) : the caving performs a horizontal flip around the beam center
181
+ - use_cupy (bool): returns a cupy array
182
+ Outputs:
183
+ - numpy.ndarray, or cupy.ndarray with the available pixels, taken into account the detector limits and the mask reference
184
+ """
185
+ # Dataset-free method
186
+ # With a reference, this method takes ~20 ms for all operations, < 1ms without reference
187
+ y_vector, x_vector = numpy.meshgrid(
188
+ numpy.arange(data_shape[0]),
189
+ numpy.arange(data_shape[1]),
190
+ indexing="ij",
191
+ sparse=True,
192
+ )
193
+ x_shifted = 2 * int(Center_1) - x_vector + 1
194
+ y_shifted = 2 * int(Center_2) - y_vector + 1
195
+ x_max = x_shifted.shape[-1]
196
+ y_max = y_shifted.shape[0]
197
+
198
+ if vertical_symmetry and horizontal_symmetry:
199
+ # Centrosymmetric caving (standard)
200
+ mask_limits = numpy.logical_and(
201
+ x_shifted > 0, x_shifted < x_max
202
+ ) & numpy.logical_and(y_shifted > 0, y_shifted < y_max)
203
+ if mask_reference is None:
204
+ mask_caving = mask_limits
205
+ else:
206
+ mask_caving = mask_limits ^ (
207
+ mask_limits & mask_reference[y_shifted, x_shifted]
208
+ )
209
+ elif horizontal_symmetry:
210
+ # Horizontal flipping around the beam
211
+ mask_limits = numpy.logical_and(x_shifted > 0, x_shifted < x_max)
212
+ if mask_reference is None:
213
+ mask_caving = mask_limits
214
+ else:
215
+ mask_caving = mask_limits ^ (
216
+ mask_limits & mask_reference[y_vector, x_shifted]
217
+ )
218
+ elif vertical_symmetry:
219
+ # Vertical flippinf around the beam
220
+ mask_limits = numpy.logical_and(y_shifted > 0, y_shifted < y_max)
221
+ if mask_reference is None:
222
+ mask_caving = mask_limits
223
+ else:
224
+ mask_caving = mask_limits ^ (
225
+ mask_limits & mask_reference[y_shifted, x_vector]
226
+ )
227
+ else:
228
+ mask_limits = numpy.logical_and(
229
+ x_vector > 0, x_vector < x_max
230
+ ) & numpy.logical_and(y_vector > 0, y_vector < y_max)
231
+ if mask_reference is None:
232
+ mask_caving = mask_limits
233
+ else:
234
+ mask_caving = mask_limits ^ (
235
+ mask_limits & mask_reference[y_vector, x_vector]
236
+ )
237
+ if use_cupy:
238
+ return cupy.asarray(mask_caving).astype(bool)
239
+ return mask_caving.astype(bool)
240
+
241
+
242
+ def _mask_to_cave(
243
+ data: numpy.ndarray, Dummy: int = None, mask_static: numpy.ndarray = None
244
+ ) -> numpy.ndarray:
245
+ """
246
+ Generates the mask with the pixels that we want to substitute. It's determined by a dummy value and/or by a mask.
247
+
248
+ Inputs:
249
+ - data (numpy.ndarray): data signal, 2 or 3 dimensional
250
+ - Dummy (int): pixels in data with dummy value will be replaced
251
+ - mask_static (numpy.ndarray): pixels that will be replaced
252
+ Outputs:
253
+ - numpy.ndarray: mask whose pixels we want to replace
254
+ """
255
+ # ~ 1 ms per frame if only Dummy
256
+ # ~ 2 ms per frame if both dummy and mask_static
257
+ # ~0 if only mask_static
258
+ if Dummy and mask_static is not None:
259
+ mask_to_cave = (data == Dummy) + mask_static
260
+ elif Dummy:
261
+ mask_to_cave = data == Dummy
262
+ elif mask_static is not None:
263
+ mask_to_cave = mask_static
264
+ else:
265
+ raise ValueError(
266
+ "Caving has to be determined by a dummy value and/or a static mask"
267
+ )
268
+ return mask_to_cave
269
+
270
+
271
+ def _process_data_caving(
272
+ data: numpy.ndarray,
273
+ pixels_to_cave: numpy.ndarray,
274
+ pixels_to_substitute: numpy.ndarray,
275
+ x_vector: numpy.ndarray,
276
+ y_vector: numpy.ndarray,
277
+ Dummy: int = None,
278
+ mask_dummy_pixels: bool = True,
279
+ use_cupy: bool = False,
280
+ ):
281
+ """Replace the pixels in data according to a mask. The replaced data correspond to a specific index-transformation, determined by input vectors.
282
+
283
+ Inputs:
284
+ - data (numpy.ndarray): data signal, 2 or 3 dimensional
285
+ - mask_to_cave (numpy.ndarray): pixels that will be masked with Dummy or caved (substituted by a symmetric brother)
286
+
287
+ - x_vector (numpy.ndarray): index vector used to shift the data along the first dimension of data array
288
+ - y_vector (numpy.ndarray): index vector used to shift the data along the second dimension of data array
289
+ - Dummy (int): in this method, the dummy value is used to replace the pixels whose replacement falls also into the mask_caving
290
+ - use_cupy (bool): to use cupy, all the inputs must be already in the GPU
291
+ Outputs:
292
+ - numpy.ndarray: data with replaced pixels (caved)
293
+ """
294
+ # Works for both numpy, cupy, 2 or 3 dimensional, for all cavings
295
+ mask_complete = pixels_to_cave & pixels_to_substitute
296
+ if use_cupy:
297
+ data_caved = cupy.copy(data)
298
+ if mask_dummy_pixels and Dummy:
299
+ cupy.copyto(data_caved, Dummy, where=pixels_to_cave)
300
+ if data.ndim == 3:
301
+ cupy.copyto(
302
+ data_caved, data_caved[:, y_vector, x_vector], where=mask_complete
303
+ )
304
+ elif data.ndim == 2:
305
+ cupy.copyto(data_caved, data_caved[y_vector, x_vector], where=mask_complete)
306
+ else:
307
+ data_caved = numpy.copy(data)
308
+ if mask_dummy_pixels and Dummy:
309
+ numpy.copyto(data_caved, Dummy, where=pixels_to_cave)
310
+ if data.ndim == 3:
311
+ numpy.copyto(
312
+ data_caved, data_caved[:, y_vector, x_vector], where=mask_complete
313
+ )
314
+ elif data.ndim == 2:
315
+ numpy.copyto(
316
+ data_caved, data_caved[y_vector, x_vector], where=mask_complete
317
+ )
318
+ return data_caved
319
+
320
+
321
+ def _process_dataset_caving_numpy(
322
+ dataset: numpy.ndarray,
323
+ Center_1: int,
324
+ Center_2: int,
325
+ Dummy: int = None,
326
+ mask_static: numpy.ndarray = None,
327
+ mask_reference: numpy.ndarray = None,
328
+ flip_caving: bool = False,
329
+ flip_horizontally_preference: bool = True,
330
+ **kwargs,
331
+ ) -> numpy.ndarray:
332
+ """Performs the caving on a 3-dimensional dataset using numpy methods
333
+
334
+ Inputs:
335
+ - dataset (numpy.ndarray): data signal, 3-dimensional
336
+ - Center_1 (int): beam center in the first data dimension
337
+ - Center_2 (int): beam center in the second data dimension
338
+ - Dummy (int): pixels in data with dummy value will be replaced
339
+ - mask_static (numpy.ndarray): pixels that will be replaced
340
+ - mask_reference (numpy.ndarray): mask with the pixels whose intensity should not be used, that means that their symmetric brother should not be caved
341
+ - flip_caving (bool): if True, adds horizontal-symmetric and vertical-symmetric caving (by default, centro-symmetric caving is always performed)
342
+ - flip_horizontally_preference (bool): if flip_caving is activated, horizontal caving is done before vertical caving
343
+ - **kwargs
344
+ Outputs:
345
+ - numpy.ndarray: data with replaced pixels (caved) using numpy methods
346
+ """
347
+ if dataset.ndim == 2:
348
+ data_shape = dataset.shape
349
+ dataset = dataset[numpy.newaxis, ...]
350
+ elif dataset.ndim == 3:
351
+ data_shape = dataset.shape[1:]
352
+
353
+ y_vector, x_vector = numpy.meshgrid(
354
+ numpy.arange(data_shape[0]),
355
+ numpy.arange(data_shape[1]),
356
+ indexing="ij",
357
+ sparse=True,
358
+ )
359
+ x_shifted = 2 * int(Center_1) - x_vector + 1
360
+ y_shifted = 2 * int(Center_2) - y_vector + 1
361
+
362
+ mask_pixels_available_centrosymmetric = kwargs.get(
363
+ "mask_pixels_available_centrosymmetric"
364
+ )
365
+ mask_pixels_available_horizontal = kwargs.get("mask_pixels_available_horizontal")
366
+ mask_pixels_available_vertical = kwargs.get("mask_pixels_available_vertical")
367
+ if mask_pixels_available_centrosymmetric is None:
368
+ mask_pixels_available_centrosymmetric = _mask_caving(
369
+ data_shape,
370
+ Center_1,
371
+ Center_2,
372
+ mask_reference,
373
+ vertical_symmetry=True,
374
+ horizontal_symmetry=True,
375
+ )
376
+ if flip_caving and mask_pixels_available_horizontal is None:
377
+ mask_pixels_available_horizontal = _mask_caving(
378
+ data_shape,
379
+ Center_1,
380
+ Center_2,
381
+ mask_reference,
382
+ vertical_symmetry=False,
383
+ horizontal_symmetry=True,
384
+ )
385
+ if flip_caving and mask_pixels_available_vertical is None:
386
+ mask_pixels_available_vertical = _mask_caving(
387
+ data_shape,
388
+ Center_1,
389
+ Center_2,
390
+ mask_reference,
391
+ vertical_symmetry=True,
392
+ horizontal_symmetry=False,
393
+ )
394
+
395
+ # This is the mask with the pixels that will be, either masked with Dummy or caved
396
+ mask_to_cave = _mask_to_cave(data=dataset, Dummy=Dummy, mask_static=mask_static)
397
+
398
+ data_caved = _process_data_caving(
399
+ data=dataset,
400
+ Dummy=Dummy,
401
+ mask_dummy_pixels=True,
402
+ pixels_to_cave=mask_to_cave,
403
+ pixels_to_substitute=mask_pixels_available_centrosymmetric,
404
+ x_vector=x_shifted,
405
+ y_vector=y_shifted,
406
+ use_cupy=False,
407
+ )
408
+ if flip_caving and Dummy:
409
+ if flip_horizontally_preference:
410
+ # First horizontal caving
411
+ mask_to_cave = data_caved == Dummy
412
+ data_caved = _process_data_caving(
413
+ data=data_caved,
414
+ Dummy=Dummy,
415
+ mask_dummy_pixels=False,
416
+ pixels_to_cave=mask_to_cave,
417
+ pixels_to_substitute=mask_pixels_available_horizontal,
418
+ x_vector=x_shifted,
419
+ y_vector=y_vector,
420
+ use_cupy=False,
421
+ )
422
+ # Second vertical caving
423
+ mask_to_cave = data_caved == Dummy
424
+ data_caved = _process_data_caving(
425
+ data=data_caved,
426
+ Dummy=Dummy,
427
+ mask_dummy_pixels=False,
428
+ pixels_to_cave=mask_to_cave,
429
+ pixels_to_substitute=mask_pixels_available_vertical,
430
+ x_vector=x_vector,
431
+ y_vector=y_shifted,
432
+ use_cupy=False,
433
+ )
434
+ else:
435
+ # First vertical caving
436
+ mask_to_cave = data_caved == Dummy
437
+ data_caved = _process_data_caving(
438
+ data=data_caved,
439
+ Dummy=Dummy,
440
+ mask_dummy_pixels=False,
441
+ pixels_to_cave=mask_to_cave,
442
+ pixels_to_substitute=mask_pixels_available_vertical,
443
+ x_vector=x_vector,
444
+ y_vector=y_shifted,
445
+ use_cupy=False,
446
+ )
447
+ # Second horizontal caving
448
+ mask_to_cave = data_caved == Dummy
449
+ data_caved = _process_data_caving(
450
+ data=data_caved,
451
+ Dummy=Dummy,
452
+ mask_dummy_pixels=False,
453
+ pixels_to_cave=mask_to_cave,
454
+ pixels_to_substitute=mask_pixels_available_horizontal,
455
+ x_vector=x_shifted,
456
+ y_vector=y_vector,
457
+ use_cupy=False,
458
+ )
459
+ return data_caved
460
+
461
+
462
+ def _process_dataset_caving_cupy(
463
+ dataset: numpy.ndarray,
464
+ Center_1: int,
465
+ Center_2: int,
466
+ Dummy: int = None,
467
+ mask_static: numpy.ndarray = None,
468
+ mask_reference: numpy.ndarray = None,
469
+ flip_caving: bool = False,
470
+ flip_horizontally_preference: bool = True,
471
+ **kwargs,
472
+ ) -> numpy.ndarray:
473
+ """Performs the caving on a 3-dimensional dataset using cupy methods (frame by frame)
474
+
475
+ Inputs:
476
+ - dataset (numpy.ndarray): data signal, 3-dimensional
477
+ - Center_1 (int): beam center in the first data dimension
478
+ - Center_2 (int): beam center in the second data dimension
479
+ - Dummy (int): pixels in data with dummy value will be replaced
480
+ - mask_static (numpy.ndarray): pixels that will be replaced
481
+ - mask_reference (numpy.ndarray): mask with the pixels whose intensity should not be used, that means that their symmetric brother should not be caved
482
+ - flip_caving (bool): if True, adds horizontal-symmetric and vertical-symmetric caving (by default, centro-symmetric caving is always performed)
483
+ - flip_horizontally_preference (bool): if flip_caving is activated, horizontal caving is done before vertical caving
484
+ - **kwargs
485
+ Outputs:
486
+ - numpy.ndarray: data with replaced pixels (caved) using numpy methods
487
+ """
488
+ log_allocated_gpu_memory()
489
+ if dataset.ndim == 2:
490
+ data_shape = dataset.shape
491
+ dataset = dataset[numpy.newaxis, ...]
492
+ elif dataset.ndim == 3:
493
+ data_shape = dataset.shape[1:]
494
+
495
+ y_vector, x_vector = numpy.meshgrid(
496
+ numpy.arange(data_shape[0]),
497
+ numpy.arange(data_shape[1]),
498
+ indexing="ij",
499
+ sparse=True,
500
+ )
501
+ x_shifted = 2 * int(Center_1) - x_vector + 1
502
+ y_shifted = 2 * int(Center_2) - y_vector + 1
503
+
504
+ x_shifted_cupy = cupy.asarray(x_shifted)
505
+ y_shifted_cupy = cupy.asarray(y_shifted)
506
+ x_vector_cupy = cupy.asarray(x_vector)
507
+ y_vector_cupy = cupy.asarray(y_vector)
508
+
509
+ mask_pixels_available_centrosymmetric = kwargs.get(
510
+ "mask_pixels_available_centrosymmetric"
511
+ )
512
+ mask_pixels_available_horizontal = kwargs.get("mask_pixels_available_horizontal")
513
+ mask_pixels_available_vertical = kwargs.get("mask_pixels_available_vertical")
514
+ if mask_pixels_available_centrosymmetric is None:
515
+ mask_pixels_available_centrosymmetric = _mask_caving(
516
+ data_shape,
517
+ Center_1,
518
+ Center_2,
519
+ mask_reference,
520
+ vertical_symmetry=True,
521
+ horizontal_symmetry=True,
522
+ )
523
+ if flip_caving and mask_pixels_available_horizontal is None:
524
+ mask_pixels_available_horizontal = _mask_caving(
525
+ data_shape,
526
+ Center_1,
527
+ Center_2,
528
+ mask_reference,
529
+ vertical_symmetry=False,
530
+ horizontal_symmetry=True,
531
+ )
532
+ if flip_caving and mask_pixels_available_vertical is None:
533
+ mask_pixels_available_vertical = _mask_caving(
534
+ data_shape,
535
+ Center_1,
536
+ Center_2,
537
+ mask_reference,
538
+ vertical_symmetry=True,
539
+ horizontal_symmetry=False,
540
+ )
541
+
542
+ if mask_static is not None:
543
+ mask_static_cupy = cupy.asarray(mask_static)
544
+ else:
545
+ mask_static_cupy = None
546
+
547
+ mask_pixels_available_centrosymmetric_cupy = cupy.asarray(
548
+ mask_pixels_available_centrosymmetric
549
+ )
550
+ if flip_caving:
551
+ mask_pixels_available_horizontal_cupy = cupy.asarray(
552
+ mask_pixels_available_horizontal
553
+ )
554
+ mask_pixels_available_vertical_cupy = cupy.asarray(
555
+ mask_pixels_available_vertical
556
+ )
557
+ else:
558
+ mask_pixels_available_horizontal_cupy = None
559
+ mask_pixels_available_vertical_cupy = None
560
+
561
+ dataset_caved = numpy.zeros_like(dataset)
562
+ for index_frame, data in enumerate(dataset):
563
+ data_cupy = cupy.asarray(data)
564
+ mask_to_cave = _mask_to_cave(data_cupy, Dummy, mask_static_cupy)
565
+ # mask_complete = mask_to_cave & mask_pixels_available_centrosymmetric_cupy
566
+ data_cupy = _process_data_caving(
567
+ data=data_cupy,
568
+ Dummy=Dummy,
569
+ mask_dummy_pixels=True,
570
+ pixels_to_cave=mask_to_cave,
571
+ pixels_to_substitute=mask_pixels_available_centrosymmetric_cupy,
572
+ x_vector=x_shifted_cupy,
573
+ y_vector=y_shifted_cupy,
574
+ use_cupy=True,
575
+ )
576
+ if flip_caving and Dummy:
577
+ if flip_horizontally_preference:
578
+ # First horizontal caving
579
+ mask_to_cave = data_cupy == Dummy
580
+ data_cupy = _process_data_caving(
581
+ data=data_cupy,
582
+ Dummy=Dummy,
583
+ mask_dummy_pixels=False,
584
+ pixels_to_cave=mask_to_cave,
585
+ pixels_to_substitute=mask_pixels_available_horizontal_cupy,
586
+ x_vector=x_shifted_cupy,
587
+ y_vector=y_vector_cupy,
588
+ use_cupy=True,
589
+ )
590
+ # Second vertical caving
591
+ mask_to_cave = data_cupy == Dummy
592
+ data_cupy = _process_data_caving(
593
+ data=data_cupy,
594
+ Dummy=Dummy,
595
+ mask_dummy_pixels=False,
596
+ pixels_to_cave=mask_to_cave,
597
+ pixels_to_substitute=mask_pixels_available_vertical_cupy,
598
+ x_vector=x_vector_cupy,
599
+ y_vector=y_shifted_cupy,
600
+ use_cupy=True,
601
+ )
602
+ else:
603
+ # First vertical caving
604
+ mask_to_cave = data_cupy == Dummy
605
+ data_cupy = _process_data_caving(
606
+ data=data_cupy,
607
+ Dummy=Dummy,
608
+ mask_dummy_pixels=False,
609
+ pixels_to_cave=mask_to_cave,
610
+ pixels_to_substitute=mask_pixels_available_vertical_cupy,
611
+ x_vector=x_vector_cupy,
612
+ y_vector=y_shifted_cupy,
613
+ use_cupy=True,
614
+ )
615
+ # Second horizontal caving
616
+ mask_to_cave = data_cupy == Dummy
617
+ data_cupy = _process_data_caving(
618
+ data=data_cupy,
619
+ Dummy=Dummy,
620
+ mask_dummy_pixels=False,
621
+ pixels_to_cave=mask_to_cave,
622
+ pixels_to_substitute=mask_pixels_available_horizontal_cupy,
623
+ x_vector=x_shifted_cupy,
624
+ y_vector=y_vector_cupy,
625
+ use_cupy=True,
626
+ )
627
+ dataset_caved[index_frame] = data_cupy.get()
628
+ return dataset_caved
629
+
630
+
631
+ def _process_data_caving_cupy(
632
+ data_cupy: cupy.ndarray,
633
+ mask_pixels_available_centrosymmetric_cupy: cupy.ndarray,
634
+ x_shifted_cupy: cupy.ndarray,
635
+ y_shifted_cupy: cupy.ndarray,
636
+ Dummy: int = None,
637
+ mask_static_cupy: cupy.ndarray = None,
638
+ flip_caving: bool = False,
639
+ flip_horizontally_preference: bool = True,
640
+ mask_pixels_available_horizontal_cupy: cupy.ndarray = None,
641
+ mask_pixels_available_vertical_cupy: cupy.ndarray = None,
642
+ x_vector_cupy: cupy.ndarray = None,
643
+ y_vector_cupy: cupy.ndarray = None,
644
+ ) -> cupy.ndarray:
645
+ """Performs the caving on a 3-dimensional dataset using cupy methods (frame by frame)
646
+
647
+ Inputs:
648
+ - data_cupy (cupy.ndarray): data signal, 2-dimensional, already on GPU
649
+ - mask_pixels_available_centrosymmetric_cupy (cupy.ndarray): mask with the available pixels to do centrosymmetric caving (already on GPU)
650
+ - x_shifted_cupy (cupy.ndarray): shifted vector of index along the first dimension of data array, already on GPU
651
+ - y_shifted_cupy (cupy.ndarray): shifted vector of index along the second dimension of data array, already on GPU
652
+ - Dummy (int): pixels in data with dummy value will be replaced
653
+ - mask_static_cupy (cupy.ndarray): mask with the pixels that will be replaced, already on GPU
654
+ - flip_caving (bool): if True, adds horizontal-symmetric and vertical-symmetric caving (by default, centro-symmetric caving is always performed)
655
+ - flip_horizontally_preference (bool): if flip_caving is activated, horizontal caving is done before vertical caving
656
+ - mask_pixels_available_horizontal_cupy (cupy.ndarray): mask with the available pixels to do horizontal-symmetric caving (already on GPU)
657
+ - mask_pixels_available_vertical_cupy (cupy.ndarray): mask with the available pixels to do vertical-symmetric caving (already on GPU)
658
+ - x_vector_cupy (cupy.ndarray): vector of index along the first dimension of data array, already on GPU
659
+ - y_vector_cupy (cupy.ndarray): vector of index along the second dimension of data array, already on GPU
660
+ Outputs:
661
+ - cupy.ndarray: data with replaced pixels (caved), still on GPU
662
+ """
663
+ mask_to_cave = _mask_to_cave(data_cupy, Dummy, mask_static_cupy)
664
+ data_cupy = _process_data_caving(
665
+ data=data_cupy,
666
+ Dummy=Dummy,
667
+ mask_dummy_pixels=True,
668
+ pixels_to_cave=mask_to_cave,
669
+ pixels_to_substitute=mask_pixels_available_centrosymmetric_cupy,
670
+ x_vector=x_shifted_cupy,
671
+ y_vector=y_shifted_cupy,
672
+ use_cupy=True,
673
+ )
674
+ if flip_caving and Dummy:
675
+ if flip_horizontally_preference:
676
+ # First horizontal caving
677
+ mask_to_cave = data_cupy == Dummy
678
+ data_cupy = _process_data_caving(
679
+ data=data_cupy,
680
+ Dummy=Dummy,
681
+ mask_dummy_pixels=False,
682
+ pixels_to_cave=mask_to_cave,
683
+ pixels_to_substitute=mask_pixels_available_horizontal_cupy,
684
+ x_vector=x_shifted_cupy,
685
+ y_vector=y_vector_cupy,
686
+ use_cupy=True,
687
+ )
688
+ # Second vertical caving
689
+ mask_to_cave = data_cupy == Dummy
690
+ data_cupy = _process_data_caving(
691
+ data=data_cupy,
692
+ Dummy=Dummy,
693
+ mask_dummy_pixels=False,
694
+ pixels_to_cave=mask_to_cave,
695
+ pixels_to_substitute=mask_pixels_available_vertical_cupy,
696
+ x_vector=x_vector_cupy,
697
+ y_vector=y_shifted_cupy,
698
+ use_cupy=True,
699
+ )
700
+ else:
701
+ # First vertical caving
702
+ mask_to_cave = data_cupy == Dummy
703
+ data_cupy = _process_data_caving(
704
+ data=data_cupy,
705
+ Dummy=Dummy,
706
+ mask_dummy_pixels=False,
707
+ pixels_to_cave=mask_to_cave,
708
+ pixels_to_substitute=mask_pixels_available_vertical_cupy,
709
+ x_vector=x_vector_cupy,
710
+ y_vector=y_shifted_cupy,
711
+ use_cupy=True,
712
+ )
713
+ # Second horizontal caving
714
+ mask_to_cave = data_cupy == Dummy
715
+ data_cupy = _process_data_caving(
716
+ data=data_cupy,
717
+ Dummy=Dummy,
718
+ mask_dummy_pixels=False,
719
+ pixels_to_cave=mask_to_cave,
720
+ pixels_to_substitute=mask_pixels_available_horizontal_cupy,
721
+ x_vector=x_shifted_cupy,
722
+ y_vector=y_vector_cupy,
723
+ use_cupy=True,
724
+ )
725
+ return data_cupy
726
+
727
+
728
+ def process_data_caving(
729
+ data: numpy.ndarray,
730
+ Center_1: int,
731
+ Center_2: int,
732
+ Dummy: int = None,
733
+ filename_mask_static: str = None,
734
+ filename_mask_reference: str = None,
735
+ algorithm: str = "numpy",
736
+ flip_caving: bool = False,
737
+ flip_horizontally_preference: bool = True,
738
+ **kwargs,
739
+ ) -> numpy.ndarray:
740
+ """Performs the caving on a 3-dimensional dataset using numpy methods
741
+
742
+ Inputs:
743
+ - data (numpy.ndarray): data signal, 2 or 3-dimensional
744
+ - Center_1 (int): beam center in the first data dimension
745
+ - Center_2 (int): beam center in the second data dimension
746
+ - Dummy (int): pixels in data with dummy value will be replaced
747
+ - filename_mask_static (str): path to the file with the mask whose pixels we want to replace
748
+ - filename_mask_reference (str): path to the file with the mask whose pixels should not be used, that means that their symmetric brother should not be caved
749
+ - algorithm (str): implementation to perform the caving: numpy or cupy
750
+ - flip_caving (bool): if True, adds horizontal-symmetric and vertical-symmetric caving (by default, centro-symmetric caving is always performed)
751
+ - flip_horizontally_preference (bool): if flip_caving is activated, horizontal caving is done before vertical caving
752
+ - **kwargs
753
+ Outputs:
754
+ - numpy.ndarray: data with replaced pixels (caved) using numpy methods
755
+ """
756
+ if data.ndim == 2:
757
+ data_signal_shape = data.shape
758
+ data = data[numpy.newaxis, ...]
759
+ elif data.ndim == 3:
760
+ data_signal_shape = data.shape[1:]
761
+
762
+ if algorithm not in ALGORITHMS_AVAILABLE:
763
+ logger.warning(
764
+ f"Algorithm '{algorithm}' is not available. Using '{DEFAULT_ALGORITHM}' instead."
765
+ )
766
+ algorithm = DEFAULT_ALGORITHM
767
+ elif algorithm == "cupy" and not CUPY_AVAILABLE:
768
+ logger.warning(f"CuPy is not available. Using {DEFAULT_ALGORITHM} instead.")
769
+ algorithm = DEFAULT_ALGORITHM
770
+ binning = kwargs.get("binning")
771
+ use_cupy = True if algorithm == "cupy" else False
772
+
773
+ # Load static masks
774
+ mask_static = None
775
+ if filename_mask_static:
776
+ mask_static = get_persistent_array_mask(
777
+ filename_mask=filename_mask_static,
778
+ data_signal_shape=data_signal_shape,
779
+ binning=binning,
780
+ use_cupy=use_cupy,
781
+ )
782
+ if mask_static is not None:
783
+ mask_static = mask_static.astype(bool)
784
+ mask_reference = None
785
+ if filename_mask_reference:
786
+ mask_reference = get_persistent_array_mask(
787
+ filename_mask=filename_mask_reference,
788
+ data_signal_shape=data_signal_shape,
789
+ binning=binning,
790
+ use_cupy=False, # This reference mask won't be cupy
791
+ )
792
+ if mask_reference is not None:
793
+ mask_reference = mask_reference.astype(bool)
794
+
795
+ # This is the mask that contains the only pixels which can be substituted, limited by the detector limits and the reference mask (it's data independent)
796
+ mask_pixels_available_centrosymmetric = _mask_caving(
797
+ data_signal_shape,
798
+ Center_1,
799
+ Center_2,
800
+ mask_reference,
801
+ vertical_symmetry=True,
802
+ horizontal_symmetry=True,
803
+ use_cupy=use_cupy,
804
+ )
805
+ if flip_caving:
806
+ mask_pixels_available_horizontal = _mask_caving(
807
+ data_signal_shape,
808
+ Center_1,
809
+ Center_2,
810
+ mask_reference,
811
+ vertical_symmetry=False,
812
+ horizontal_symmetry=True,
813
+ use_cupy=use_cupy,
814
+ )
815
+ mask_pixels_available_vertical = _mask_caving(
816
+ data_signal_shape,
817
+ Center_1,
818
+ Center_2,
819
+ mask_reference,
820
+ vertical_symmetry=True,
821
+ horizontal_symmetry=False,
822
+ use_cupy=use_cupy,
823
+ )
824
+ else:
825
+ mask_pixels_available_horizontal = None
826
+ mask_pixels_available_vertical = None
827
+
828
+ params_caving = {
829
+ "dataset": data,
830
+ "Center_1": Center_1,
831
+ "Center_2": Center_2,
832
+ "Dummy": Dummy,
833
+ "mask_static": mask_static,
834
+ "mask_reference": mask_reference,
835
+ "flip_caving": flip_caving,
836
+ "flip_horizontally_preference": flip_horizontally_preference,
837
+ "mask_pixels_available_centrosymmetric": mask_pixels_available_centrosymmetric,
838
+ "mask_pixels_available_horizontal": mask_pixels_available_horizontal,
839
+ "mask_pixels_available_vertical": mask_pixels_available_vertical,
840
+ }
841
+ result = ALGORITHMS_AVAILABLE[algorithm]["algorithm"](
842
+ **params_caving,
843
+ )
844
+ return result
845
+
846
+
847
+ ALGORITHMS_AVAILABLE = {
848
+ "numpy": {"algorithm": _process_dataset_caving_numpy, "use_cupy": False},
849
+ "cupy": {"algorithm": _process_dataset_caving_cupy, "use_cupy": True},
850
+ }
851
+ DEFAULT_ALGORITHM = "numpy"