fakecbed 0.2.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.
- fakecbed/__init__.py +46 -0
- fakecbed/discretized.py +2554 -0
- fakecbed/shapes.py +3510 -0
- fakecbed/tds.py +268 -0
- fakecbed/version.py +16 -0
- fakecbed-0.2.0.dist-info/LICENSE +674 -0
- fakecbed-0.2.0.dist-info/METADATA +59 -0
- fakecbed-0.2.0.dist-info/RECORD +10 -0
- fakecbed-0.2.0.dist-info/WHEEL +5 -0
- fakecbed-0.2.0.dist-info/top_level.txt +1 -0
fakecbed/discretized.py
ADDED
|
@@ -0,0 +1,2554 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright 2024 Matthew Fitzpatrick.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
# the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
# Foundation, version 3.
|
|
7
|
+
#
|
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
10
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License along with
|
|
13
|
+
# this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
|
14
|
+
"""For creating discretized fake CBED patterns.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
#####################################
|
|
21
|
+
## Load libraries/packages/modules ##
|
|
22
|
+
#####################################
|
|
23
|
+
|
|
24
|
+
# For accessing attributes of functions.
|
|
25
|
+
import inspect
|
|
26
|
+
|
|
27
|
+
# For randomly selecting items in dictionaries.
|
|
28
|
+
import random
|
|
29
|
+
|
|
30
|
+
# For performing deep copies.
|
|
31
|
+
import copy
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# For general array handling.
|
|
36
|
+
import numpy as np
|
|
37
|
+
import torch
|
|
38
|
+
|
|
39
|
+
# For validating and converting objects.
|
|
40
|
+
import czekitout.check
|
|
41
|
+
import czekitout.convert
|
|
42
|
+
|
|
43
|
+
# For defining classes that support enforced validation, updatability,
|
|
44
|
+
# pre-serialization, and de-serialization.
|
|
45
|
+
import fancytypes
|
|
46
|
+
|
|
47
|
+
# For creating hyperspy signals and axes.
|
|
48
|
+
import hyperspy.signals
|
|
49
|
+
import hyperspy.axes
|
|
50
|
+
|
|
51
|
+
# For creating distortion models.
|
|
52
|
+
import distoptica
|
|
53
|
+
|
|
54
|
+
# For inpainting images.
|
|
55
|
+
import skimage.restoration
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# For creating undistorted geometric shapes.
|
|
60
|
+
import fakecbed.shapes
|
|
61
|
+
|
|
62
|
+
# For creating undistorted thermal diffuse models.
|
|
63
|
+
import fakecbed.tds
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
##################################
|
|
68
|
+
## Define classes and functions ##
|
|
69
|
+
##################################
|
|
70
|
+
|
|
71
|
+
# List of public objects in module.
|
|
72
|
+
__all__ = ["CBEDPattern"]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _check_and_convert_undistorted_tds_model(params):
|
|
77
|
+
current_func_name = inspect.stack()[0][3]
|
|
78
|
+
char_idx = 19
|
|
79
|
+
obj_name = current_func_name[char_idx:]
|
|
80
|
+
obj = params[obj_name]
|
|
81
|
+
|
|
82
|
+
accepted_types = (fakecbed.tds.Model, type(None))
|
|
83
|
+
|
|
84
|
+
if isinstance(obj, accepted_types[-1]):
|
|
85
|
+
undistorted_tds_model = accepted_types[0]()
|
|
86
|
+
else:
|
|
87
|
+
kwargs = {"obj": obj,
|
|
88
|
+
"obj_name": obj_name,
|
|
89
|
+
"accepted_types": accepted_types}
|
|
90
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
91
|
+
undistorted_tds_model = copy.deepcopy(obj)
|
|
92
|
+
|
|
93
|
+
return undistorted_tds_model
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _pre_serialize_undistorted_tds_model(undistorted_tds_model):
|
|
98
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
99
|
+
serializable_rep = obj_to_pre_serialize.pre_serialize()
|
|
100
|
+
|
|
101
|
+
return serializable_rep
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _de_pre_serialize_undistorted_tds_model(serializable_rep):
|
|
106
|
+
undistorted_tds_model = \
|
|
107
|
+
fakecbed.tds.Model.de_pre_serialize(serializable_rep)
|
|
108
|
+
|
|
109
|
+
return undistorted_tds_model
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _check_and_convert_undistorted_disks(params):
|
|
114
|
+
current_func_name = inspect.stack()[0][3]
|
|
115
|
+
char_idx = 19
|
|
116
|
+
obj_name = current_func_name[char_idx:]
|
|
117
|
+
obj = params[obj_name]
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
for undistorted_disk in obj:
|
|
121
|
+
accepted_types = (fakecbed.shapes.NonuniformBoundedShape,)
|
|
122
|
+
|
|
123
|
+
kwargs = {"obj": undistorted_disk,
|
|
124
|
+
"obj_name": "undistorted_disk",
|
|
125
|
+
"accepted_types": accepted_types}
|
|
126
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
127
|
+
|
|
128
|
+
accepted_types = (fakecbed.shapes.Circle, fakecbed.shapes.Ellipse)
|
|
129
|
+
|
|
130
|
+
undistorted_disk_core_attrs = \
|
|
131
|
+
undistorted_disk.get_core_attrs(deep_copy=False)
|
|
132
|
+
undistorted_disk_support = \
|
|
133
|
+
undistorted_disk_core_attrs["support"]
|
|
134
|
+
|
|
135
|
+
kwargs = {"obj": undistorted_disk_support,
|
|
136
|
+
"obj_name": "undistorted_disk_support",
|
|
137
|
+
"accepted_types": accepted_types}
|
|
138
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
139
|
+
except:
|
|
140
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
141
|
+
raise TypeError(err_msg)
|
|
142
|
+
|
|
143
|
+
undistorted_disks = copy.deepcopy(obj)
|
|
144
|
+
|
|
145
|
+
return undistorted_disks
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _pre_serialize_undistorted_disks(undistorted_disks):
|
|
150
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
151
|
+
serializable_rep = tuple()
|
|
152
|
+
for elem in obj_to_pre_serialize:
|
|
153
|
+
serializable_rep += (elem.pre_serialize(),)
|
|
154
|
+
|
|
155
|
+
return serializable_rep
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _de_pre_serialize_undistorted_disks(serializable_rep):
|
|
160
|
+
undistorted_disks = \
|
|
161
|
+
tuple()
|
|
162
|
+
for pre_serialized_undistorted_disk in serializable_rep:
|
|
163
|
+
cls_alias = \
|
|
164
|
+
fakecbed.shapes.NonuniformBoundedShape
|
|
165
|
+
undistorted_disk = \
|
|
166
|
+
cls_alias.de_pre_serialize(pre_serialized_undistorted_disk)
|
|
167
|
+
undistorted_disks += \
|
|
168
|
+
(undistorted_disk,)
|
|
169
|
+
|
|
170
|
+
return undistorted_disks
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _check_and_convert_undistorted_misc_shapes(params):
|
|
175
|
+
current_func_name = inspect.stack()[0][3]
|
|
176
|
+
char_idx = 19
|
|
177
|
+
obj_name = current_func_name[char_idx:]
|
|
178
|
+
obj = params[obj_name]
|
|
179
|
+
|
|
180
|
+
accepted_types = (fakecbed.shapes.Circle,
|
|
181
|
+
fakecbed.shapes.Ellipse,
|
|
182
|
+
fakecbed.shapes.Peak,
|
|
183
|
+
fakecbed.shapes.Band,
|
|
184
|
+
fakecbed.shapes.PlaneWave,
|
|
185
|
+
fakecbed.shapes.Arc,
|
|
186
|
+
fakecbed.shapes.GenericBlob,
|
|
187
|
+
fakecbed.shapes.Orbital,
|
|
188
|
+
fakecbed.shapes.Lune,
|
|
189
|
+
fakecbed.shapes.NonuniformBoundedShape)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
for undistorted_misc_shape in obj:
|
|
193
|
+
kwargs = {"obj": undistorted_misc_shape,
|
|
194
|
+
"obj_name": "undistorted_misc_shape",
|
|
195
|
+
"accepted_types": accepted_types}
|
|
196
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
197
|
+
except:
|
|
198
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
199
|
+
raise TypeError(err_msg)
|
|
200
|
+
|
|
201
|
+
undistorted_misc_shapes = copy.deepcopy(obj)
|
|
202
|
+
|
|
203
|
+
return undistorted_misc_shapes
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _pre_serialize_undistorted_misc_shapes(undistorted_misc_shapes):
|
|
208
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
209
|
+
serializable_rep = tuple()
|
|
210
|
+
for elem in obj_to_pre_serialize:
|
|
211
|
+
serializable_rep += (elem.pre_serialize(),)
|
|
212
|
+
|
|
213
|
+
return serializable_rep
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _de_pre_serialize_undistorted_misc_shapes(serializable_rep):
|
|
218
|
+
undistorted_misc_shapes = tuple()
|
|
219
|
+
|
|
220
|
+
for pre_serialized_undistorted_misc_shape in serializable_rep:
|
|
221
|
+
if "radius" in pre_serialized_undistorted_misc_shape:
|
|
222
|
+
cls_alias = fakecbed.shapes.Circle
|
|
223
|
+
elif "eccentricity" in pre_serialized_undistorted_misc_shape:
|
|
224
|
+
cls_alias = fakecbed.shapes.Ellipse
|
|
225
|
+
elif "functional_form" in pre_serialized_undistorted_misc_shape:
|
|
226
|
+
cls_alias = fakecbed.shapes.Peak
|
|
227
|
+
elif "end_pt_1" in pre_serialized_undistorted_misc_shape:
|
|
228
|
+
cls_alias = fakecbed.shapes.Band
|
|
229
|
+
elif "propagation_direction" in pre_serialized_undistorted_misc_shape:
|
|
230
|
+
cls_alias = fakecbed.shapes.PlaneWave
|
|
231
|
+
elif "subtending_angle" in pre_serialized_undistorted_misc_shape:
|
|
232
|
+
cls_alias = fakecbed.shapes.Arc
|
|
233
|
+
elif "radial_amplitudes" in pre_serialized_undistorted_misc_shape:
|
|
234
|
+
cls_alias = fakecbed.shapes.GenericBlob
|
|
235
|
+
elif "magnetic_quantum_number" in pre_serialized_undistorted_misc_shape:
|
|
236
|
+
cls_alias = fakecbed.shapes.Orbital
|
|
237
|
+
elif "bg_ellipse" in pre_serialized_undistorted_misc_shape:
|
|
238
|
+
cls_alias = fakecbed.shapes.Lune
|
|
239
|
+
else:
|
|
240
|
+
cls_alias = fakecbed.shapes.NonuniformBoundedShape
|
|
241
|
+
|
|
242
|
+
undistorted_misc_shape = \
|
|
243
|
+
cls_alias.de_pre_serialize(pre_serialized_undistorted_misc_shape)
|
|
244
|
+
undistorted_misc_shapes += \
|
|
245
|
+
(undistorted_misc_shape,)
|
|
246
|
+
|
|
247
|
+
return undistorted_misc_shapes
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _check_and_convert_undistorted_outer_illumination_shape(params):
|
|
252
|
+
current_func_name = inspect.stack()[0][3]
|
|
253
|
+
char_idx = 19
|
|
254
|
+
obj_name = current_func_name[char_idx:]
|
|
255
|
+
obj = params[obj_name]
|
|
256
|
+
|
|
257
|
+
accepted_types = (fakecbed.shapes.Circle,
|
|
258
|
+
fakecbed.shapes.Ellipse,
|
|
259
|
+
fakecbed.shapes.GenericBlob,
|
|
260
|
+
type(None))
|
|
261
|
+
|
|
262
|
+
if isinstance(obj, accepted_types[-1]):
|
|
263
|
+
kwargs = {"radius": np.inf}
|
|
264
|
+
undistorted_outer_illumination_shape = accepted_types[0](**kwargs)
|
|
265
|
+
else:
|
|
266
|
+
kwargs = {"obj": obj,
|
|
267
|
+
"obj_name": obj_name,
|
|
268
|
+
"accepted_types": accepted_types}
|
|
269
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
270
|
+
undistorted_outer_illumination_shape = copy.deepcopy(obj)
|
|
271
|
+
|
|
272
|
+
return undistorted_outer_illumination_shape
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _pre_serialize_undistorted_outer_illumination_shape(
|
|
277
|
+
undistorted_outer_illumination_shape):
|
|
278
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
279
|
+
serializable_rep = obj_to_pre_serialize.pre_serialize()
|
|
280
|
+
|
|
281
|
+
return serializable_rep
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _de_pre_serialize_undistorted_outer_illumination_shape(serializable_rep):
|
|
286
|
+
if "radius" in serializable_rep:
|
|
287
|
+
undistorted_outer_illumination_shape = \
|
|
288
|
+
fakecbed.shapes.Circle.de_pre_serialize(serializable_rep)
|
|
289
|
+
elif "eccentricity" in serializable_rep:
|
|
290
|
+
undistorted_outer_illumination_shape = \
|
|
291
|
+
fakecbed.shapes.Ellipse.de_pre_serialize(serializable_rep)
|
|
292
|
+
else:
|
|
293
|
+
undistorted_outer_illumination_shape = \
|
|
294
|
+
fakecbed.shapes.GenericBlob.de_pre_serialize(serializable_rep)
|
|
295
|
+
|
|
296
|
+
return undistorted_outer_illumination_shape
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _check_and_convert_gaussian_filter_std_dev(params):
|
|
301
|
+
current_func_name = inspect.stack()[0][3]
|
|
302
|
+
obj_name = current_func_name[19:]
|
|
303
|
+
func_alias = czekitout.convert.to_nonnegative_float
|
|
304
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
305
|
+
gaussian_filter_std_dev = func_alias(**kwargs)
|
|
306
|
+
|
|
307
|
+
return gaussian_filter_std_dev
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _pre_serialize_gaussian_filter_std_dev(
|
|
312
|
+
gaussian_filter_std_dev):
|
|
313
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
314
|
+
serializable_rep = obj_to_pre_serialize
|
|
315
|
+
|
|
316
|
+
return serializable_rep
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _de_pre_serialize_gaussian_filter_std_dev(serializable_rep):
|
|
321
|
+
gaussian_filter_std_dev = serializable_rep
|
|
322
|
+
|
|
323
|
+
return gaussian_filter_std_dev
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _check_and_convert_distortion_model(params):
|
|
328
|
+
current_func_name = inspect.stack()[0][3]
|
|
329
|
+
char_idx = 19
|
|
330
|
+
obj_name = current_func_name[char_idx:]
|
|
331
|
+
obj = params[obj_name]
|
|
332
|
+
|
|
333
|
+
num_pixels_across_pattern = \
|
|
334
|
+
_check_and_convert_num_pixels_across_pattern(params)
|
|
335
|
+
|
|
336
|
+
accepted_types = (distoptica.DistortionModel, type(None))
|
|
337
|
+
|
|
338
|
+
if isinstance(obj, accepted_types[-1]):
|
|
339
|
+
sampling_grid_dims_in_pixels = 2*(num_pixels_across_pattern,)
|
|
340
|
+
kwargs = {"sampling_grid_dims_in_pixels": sampling_grid_dims_in_pixels}
|
|
341
|
+
distortion_model = accepted_types[0](**kwargs)
|
|
342
|
+
else:
|
|
343
|
+
kwargs = {"obj": obj,
|
|
344
|
+
"obj_name": obj_name,
|
|
345
|
+
"accepted_types": accepted_types}
|
|
346
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
347
|
+
distortion_model = copy.deepcopy(obj)
|
|
348
|
+
|
|
349
|
+
distortion_model_core_attrs = \
|
|
350
|
+
distortion_model.get_core_attrs(deep_copy=False)
|
|
351
|
+
sampling_grid_dims_in_pixels = \
|
|
352
|
+
distortion_model_core_attrs["sampling_grid_dims_in_pixels"]
|
|
353
|
+
|
|
354
|
+
if ((sampling_grid_dims_in_pixels[0]%num_pixels_across_pattern != 0)
|
|
355
|
+
or (sampling_grid_dims_in_pixels[1]%num_pixels_across_pattern != 0)):
|
|
356
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
357
|
+
raise ValueError(err_msg)
|
|
358
|
+
|
|
359
|
+
return distortion_model
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _pre_serialize_distortion_model(distortion_model):
|
|
364
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
365
|
+
serializable_rep = obj_to_pre_serialize.pre_serialize()
|
|
366
|
+
|
|
367
|
+
return serializable_rep
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _de_pre_serialize_distortion_model(serializable_rep):
|
|
372
|
+
distortion_model = \
|
|
373
|
+
distoptica.DistortionModel.de_pre_serialize(serializable_rep)
|
|
374
|
+
|
|
375
|
+
return distortion_model
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _check_and_convert_num_pixels_across_pattern(params):
|
|
380
|
+
current_func_name = inspect.stack()[0][3]
|
|
381
|
+
obj_name = current_func_name[19:]
|
|
382
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
383
|
+
num_pixels_across_pattern = czekitout.convert.to_positive_int(**kwargs)
|
|
384
|
+
|
|
385
|
+
return num_pixels_across_pattern
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _pre_serialize_num_pixels_across_pattern(num_pixels_across_pattern):
|
|
390
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
391
|
+
serializable_rep = obj_to_pre_serialize
|
|
392
|
+
|
|
393
|
+
return serializable_rep
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _de_pre_serialize_num_pixels_across_pattern(serializable_rep):
|
|
398
|
+
num_pixels_across_pattern = serializable_rep
|
|
399
|
+
|
|
400
|
+
return num_pixels_across_pattern
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _check_and_convert_apply_shot_noise(params):
|
|
405
|
+
current_func_name = inspect.stack()[0][3]
|
|
406
|
+
obj_name = current_func_name[19:]
|
|
407
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
408
|
+
apply_shot_noise = czekitout.convert.to_bool(**kwargs)
|
|
409
|
+
|
|
410
|
+
return apply_shot_noise
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _pre_serialize_apply_shot_noise(apply_shot_noise):
|
|
415
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
416
|
+
serializable_rep = obj_to_pre_serialize
|
|
417
|
+
|
|
418
|
+
return serializable_rep
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _de_pre_serialize_apply_shot_noise(serializable_rep):
|
|
423
|
+
apply_shot_noise = serializable_rep
|
|
424
|
+
|
|
425
|
+
return apply_shot_noise
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _check_and_convert_detector_partition_width_in_pixels(params):
|
|
430
|
+
current_func_name = inspect.stack()[0][3]
|
|
431
|
+
obj_name = current_func_name[19:]
|
|
432
|
+
func_alias = czekitout.convert.to_nonnegative_int
|
|
433
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
434
|
+
detector_partition_width_in_pixels = func_alias(**kwargs)
|
|
435
|
+
|
|
436
|
+
return detector_partition_width_in_pixels
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _pre_serialize_detector_partition_width_in_pixels(
|
|
441
|
+
detector_partition_width_in_pixels):
|
|
442
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
443
|
+
serializable_rep = obj_to_pre_serialize
|
|
444
|
+
|
|
445
|
+
return serializable_rep
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def _de_pre_serialize_detector_partition_width_in_pixels(serializable_rep):
|
|
450
|
+
detector_partition_width_in_pixels = serializable_rep
|
|
451
|
+
|
|
452
|
+
return detector_partition_width_in_pixels
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _check_and_convert_cold_pixels(params):
|
|
457
|
+
current_func_name = inspect.stack()[0][3]
|
|
458
|
+
obj_name = current_func_name[19:]
|
|
459
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
460
|
+
cold_pixels = czekitout.convert.to_pairs_of_ints(**kwargs)
|
|
461
|
+
|
|
462
|
+
num_pixels_across_pattern = \
|
|
463
|
+
_check_and_convert_num_pixels_across_pattern(params)
|
|
464
|
+
|
|
465
|
+
coords_of_cold_pixels = cold_pixels
|
|
466
|
+
for coords_of_cold_pixel in coords_of_cold_pixels:
|
|
467
|
+
row, col = coords_of_cold_pixel
|
|
468
|
+
if ((row < -num_pixels_across_pattern)
|
|
469
|
+
or (num_pixels_across_pattern <= row)
|
|
470
|
+
or (col < -num_pixels_across_pattern)
|
|
471
|
+
or (num_pixels_across_pattern <= col)):
|
|
472
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
473
|
+
raise TypeError(err_msg)
|
|
474
|
+
|
|
475
|
+
return cold_pixels
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _pre_serialize_cold_pixels(cold_pixels):
|
|
480
|
+
serializable_rep = cold_pixels
|
|
481
|
+
|
|
482
|
+
return serializable_rep
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def _de_pre_serialize_cold_pixels(serializable_rep):
|
|
487
|
+
cold_pixels = serializable_rep
|
|
488
|
+
|
|
489
|
+
return cold_pixels
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def _check_and_convert_mask_frame(params):
|
|
494
|
+
current_func_name = inspect.stack()[0][3]
|
|
495
|
+
obj_name = current_func_name[19:]
|
|
496
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
497
|
+
mask_frame = czekitout.convert.to_quadruplet_of_nonnegative_ints(**kwargs)
|
|
498
|
+
|
|
499
|
+
return mask_frame
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _pre_serialize_mask_frame(mask_frame):
|
|
504
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
505
|
+
serializable_rep = obj_to_pre_serialize
|
|
506
|
+
|
|
507
|
+
return serializable_rep
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def _de_pre_serialize_mask_frame(serializable_rep):
|
|
512
|
+
mask_frame = serializable_rep
|
|
513
|
+
|
|
514
|
+
return mask_frame
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _check_and_convert_deep_copy(params):
|
|
519
|
+
current_func_name = inspect.stack()[0][3]
|
|
520
|
+
obj_name = current_func_name[19:]
|
|
521
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
522
|
+
deep_copy = czekitout.convert.to_bool(**kwargs)
|
|
523
|
+
|
|
524
|
+
return deep_copy
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def _check_and_convert_overriding_image(params):
|
|
529
|
+
current_func_name = inspect.stack()[0][3]
|
|
530
|
+
char_idx = 19
|
|
531
|
+
obj_name = current_func_name[char_idx:]
|
|
532
|
+
obj = params[obj_name]
|
|
533
|
+
|
|
534
|
+
func_alias = fakecbed.shapes._check_and_convert_real_torch_matrix
|
|
535
|
+
params["real_torch_matrix"] = obj
|
|
536
|
+
params["name_of_alias_of_real_torch_matrix"] = obj_name
|
|
537
|
+
overriding_image = func_alias(params)
|
|
538
|
+
|
|
539
|
+
del params["real_torch_matrix"]
|
|
540
|
+
del params["name_of_alias_of_real_torch_matrix"]
|
|
541
|
+
|
|
542
|
+
num_pixels_across_pattern = params["num_pixels_across_pattern"]
|
|
543
|
+
expected_image_dims_in_pixels = 2*(num_pixels_across_pattern,)
|
|
544
|
+
|
|
545
|
+
if overriding_image.shape != expected_image_dims_in_pixels:
|
|
546
|
+
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
547
|
+
args = expected_image_dims_in_pixels
|
|
548
|
+
err_msg = unformatted_err_msg.format(*args)
|
|
549
|
+
raise ValueError(err_msg)
|
|
550
|
+
|
|
551
|
+
return overriding_image
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def _check_and_convert_skip_validation_and_conversion(params):
|
|
556
|
+
func_alias = \
|
|
557
|
+
fakecbed.shapes._check_and_convert_skip_validation_and_conversion
|
|
558
|
+
skip_validation_and_conversion = \
|
|
559
|
+
func_alias(params)
|
|
560
|
+
|
|
561
|
+
return skip_validation_and_conversion
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
_default_undistorted_outer_illumination_shape = \
|
|
566
|
+
None
|
|
567
|
+
_default_undistorted_tds_model = \
|
|
568
|
+
None
|
|
569
|
+
_default_undistorted_disks = \
|
|
570
|
+
tuple()
|
|
571
|
+
_default_undistorted_misc_shapes = \
|
|
572
|
+
tuple()
|
|
573
|
+
_default_gaussian_filter_std_dev = \
|
|
574
|
+
0
|
|
575
|
+
_default_distortion_model = \
|
|
576
|
+
None
|
|
577
|
+
_default_num_pixels_across_pattern = \
|
|
578
|
+
512
|
|
579
|
+
_default_apply_shot_noise = \
|
|
580
|
+
False
|
|
581
|
+
_default_detector_partition_width_in_pixels = \
|
|
582
|
+
0
|
|
583
|
+
_default_cold_pixels = \
|
|
584
|
+
tuple()
|
|
585
|
+
_default_skip_validation_and_conversion = \
|
|
586
|
+
fakecbed.shapes._default_skip_validation_and_conversion
|
|
587
|
+
_default_deep_copy = \
|
|
588
|
+
True
|
|
589
|
+
_default_mask_frame = \
|
|
590
|
+
4*(0,)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
595
|
+
r"""The parameters of a discretized fake convergent beam electron
|
|
596
|
+
diffraction (CBED) pattern.
|
|
597
|
+
|
|
598
|
+
A series of parameters need to be specified in order to create an image of a
|
|
599
|
+
fake CBED pattern, with the most important parameters being: the set of
|
|
600
|
+
intensity patterns of undistorted shapes that determine the undistorted
|
|
601
|
+
noiseless non-blurred uncorrupted (UNNBU) fake CBED pattern; and a
|
|
602
|
+
distortion model which transforms the UNNBU fake CBED pattern into a
|
|
603
|
+
distorted noiseless non-blurred uncorrupted (DNNBU) fake CBED pattern. The
|
|
604
|
+
remaining parameters determine whether additional images effects are
|
|
605
|
+
applied, like e.g. shot noise or blur effects. Note that in the case of the
|
|
606
|
+
aforementioned shapes, we expand the notion of intensity patterns to mean a
|
|
607
|
+
2D real-valued function, i.e. it can be negative. To be clear, we do not
|
|
608
|
+
apply this generalized notion of intensity patterns to the fake CBED
|
|
609
|
+
patterns: in such cases intensity patterns mean 2D real-valued functions
|
|
610
|
+
that are strictly nonnegative.
|
|
611
|
+
|
|
612
|
+
Let :math:`u_{x}` and :math:`u_{y}` be the fractional horizontal and
|
|
613
|
+
vertical coordinates, respectively, of a point in an undistorted image,
|
|
614
|
+
where :math:`\left(u_{x},u_{y}\right)=\left(0,0\right)` is the bottom left
|
|
615
|
+
corner of the image. Secondly, let :math:`q_{x}` and :math:`q_{y}` be the
|
|
616
|
+
fractional horizontal and vertical coordinates, respectively, of a point in
|
|
617
|
+
a distorted image, where :math:`\left(q_{x},q_{y}\right)=\left(0,0\right)`
|
|
618
|
+
is the bottom left corner of the image. When users specify the distortion
|
|
619
|
+
model, represented by an instance of the class
|
|
620
|
+
:class:`distoptica.DistortionModel`, they also specify a coordinate
|
|
621
|
+
transformation, :math:`\left(T_{⌑;x}\left(u_{x},u_{y}\right),
|
|
622
|
+
T_{⌑;x}\left(u_{x},u_{y}\right)\right)`, which maps a given coordinate pair
|
|
623
|
+
:math:`\left(u_{x},u_{y}\right)` to a corresponding coordinate pair
|
|
624
|
+
:math:`\left(q_{x},q_{y}\right)`, and implicitly a right-inverse to said
|
|
625
|
+
coordinate transformation,
|
|
626
|
+
:math:`\left(T_{\square;x}\left(q_{x},q_{y}\right),
|
|
627
|
+
T_{\square;y}\left(q_{x},q_{y}\right)\right)`, that maps a coordinate pair
|
|
628
|
+
:math:`\left(q_{x},q_{y}\right)` to a corresponding coordinate pair
|
|
629
|
+
:math:`\left(u_{x},u_{y}\right)`, when such a relationship exists for
|
|
630
|
+
:math:`\left(q_{x},q_{y}\right)`.
|
|
631
|
+
|
|
632
|
+
The calculation of the image of the target fake CBED pattern involves
|
|
633
|
+
calculating various intermediate images which are subsequently combined to
|
|
634
|
+
yield the target image. These intermediate images share the same horizontal
|
|
635
|
+
and vertical dimensions in units of pixels, which may differ from those of
|
|
636
|
+
the image of the target fake CBED pattern. Let :math:`N_{\mathcal{I};x}` and
|
|
637
|
+
:math:`N_{\mathcal{I};y}` be the number of pixels in the image of the target
|
|
638
|
+
fake CBED pattern from left to right and top to bottom respectively, and let
|
|
639
|
+
:math:`N_{\mathring{\mathcal{I}};x}` and
|
|
640
|
+
:math:`N_{\mathring{\mathcal{I}};y}` be the number of pixels in each of the
|
|
641
|
+
aforementioned intermediate images from left to right and top to bottom
|
|
642
|
+
respectively. In :mod:`fakecbed`, we assume that
|
|
643
|
+
|
|
644
|
+
.. math ::
|
|
645
|
+
N_{\mathcal{I};x}=N_{\mathcal{I};y},
|
|
646
|
+
:label: N_I_x_eq_N_I_y__1
|
|
647
|
+
|
|
648
|
+
.. math ::
|
|
649
|
+
N_{\mathring{\mathcal{I}};x}\ge N_{\mathcal{I};x},
|
|
650
|
+
:label: N_ring_I_x_ge_N_I_x__1
|
|
651
|
+
|
|
652
|
+
and
|
|
653
|
+
|
|
654
|
+
.. math ::
|
|
655
|
+
N_{\mathring{\mathcal{I}};y}\ge N_{\mathcal{I};y}.
|
|
656
|
+
:label: N_ring_I_y_ge_N_I_y__1
|
|
657
|
+
|
|
658
|
+
The integer :math:`N_{\mathcal{I};x}` is specified by the parameter
|
|
659
|
+
``num_pixels_across_pattern``. The integers
|
|
660
|
+
:math:`N_{\mathring{\mathcal{I}};x}` and
|
|
661
|
+
:math:`N_{\mathring{\mathcal{I}};y}` are specified indirectly by the
|
|
662
|
+
parameter ``distortion_model``. The parameter ``distortion_model`` specifies
|
|
663
|
+
the distortion model, which as mentioned above is represented by an instance
|
|
664
|
+
of the class :class:`distoptica.DistortionModel`. One of the parameters of
|
|
665
|
+
said distortion model is the integer pair
|
|
666
|
+
``sampling_grid_dims_in_pixels``. In the current context,
|
|
667
|
+
``sampling_grid_dims_in_pixels[0]`` and ``sampling_grid_dims_in_pixels[1]``
|
|
668
|
+
are equal to :math:`N_{\mathring{\mathcal{I}};x}` and
|
|
669
|
+
:math:`N_{\mathring{\mathcal{I}};y}` respectively.
|
|
670
|
+
|
|
671
|
+
As mentioned above, a set of intensity patterns need to be specified in
|
|
672
|
+
order to create the target fake CBED pattern. The first of these is the
|
|
673
|
+
intensity pattern of an undistorted thermal diffuse scattering (TDS) model,
|
|
674
|
+
:math:`\mathcal{I}_{\text{TDS}}\left(u_{x},u_{y}\right)`, which is specified
|
|
675
|
+
by the parameter ``undistorted_tds_model``. The second of these intensity
|
|
676
|
+
patterns is that of the undistorted outer illumination shape,
|
|
677
|
+
:math:`\mathcal{I}_{\text{OI}}\left(u_{x},u_{y}\right)`, which is specified
|
|
678
|
+
by the parameter ``undistorted_outer_illumination_shape``.
|
|
679
|
+
:math:`\mathcal{I}_{\text{OI}}\left(u_{x},u_{y}\right)` is defined such that
|
|
680
|
+
for every coordinate pair :math:`\left(u_{x},u_{y}\right)`, if
|
|
681
|
+
:math:`\mathcal{I}_{\text{OI}}\left(u_{x},u_{y}\right)=0` then the value of
|
|
682
|
+
the UNNBU fake CBED pattern is also equal to 0. A separate subset of the
|
|
683
|
+
intensity patterns that need to be specified are :math:`N_{\text{D}}`
|
|
684
|
+
intensity patterns of undistorted nonuniform circles and/or ellipses,
|
|
685
|
+
:math:`\left\{ \mathcal{I}_{k;\text{D}}\left(u_{x},u_{y}\right)\right\}
|
|
686
|
+
_{k=0}^{N_{\text{D}}-1}`, which is specified by the parameter
|
|
687
|
+
``undistorted_disks``. Each intensity pattern
|
|
688
|
+
:math:`\mathcal{I}_{k;\text{D}}\left(u_{x},u_{y}\right)` is suppose to
|
|
689
|
+
depict one of the CBED disks in the fake CBED pattern in the absence of the
|
|
690
|
+
intensity background. Moreover, each intensity pattern
|
|
691
|
+
:math:`\mathcal{I}_{k;\text{D}}\left(u_{x},u_{y}\right)` has a corresponding
|
|
692
|
+
supporting intensity pattern
|
|
693
|
+
:math:`\mathcal{I}_{k;\text{DS}}\left(u_{x},u_{y}\right)`, which is defined
|
|
694
|
+
such that for every coordinate pair :math:`\left(u_{x},u_{y}\right)`, if
|
|
695
|
+
:math:`\mathcal{I}_{k;\text{DS}}\left(u_{x},u_{y}\right)=0` then
|
|
696
|
+
:math:`\mathcal{I}_{k;\text{D}}\left(u_{x},u_{y}\right)=0`. The remaining
|
|
697
|
+
intensity patterns that need to be specified, of which there are
|
|
698
|
+
:math:`N_{\text{M}}`, are intensity patterns of undistorted miscellaneous
|
|
699
|
+
shapes, :math:`\left\{
|
|
700
|
+
\mathcal{I}_{k;\text{M}}\left(u_{x},u_{y}\right)\right\}
|
|
701
|
+
_{k=0}^{N_{\text{M}}-1}`, which is specified by the parameter
|
|
702
|
+
``undistorted_misc_shapes``. These patterns, along with that of the TDS
|
|
703
|
+
model, contribute to the intensity background.
|
|
704
|
+
|
|
705
|
+
To add blur effects, users can specify a nonzero standard deviation
|
|
706
|
+
:math:`\sigma_{\text{blur}}` of the Gaussian filter used to yield such blur
|
|
707
|
+
effects on the target fake CBED pattern. The value of
|
|
708
|
+
:math:`\sigma_{\text{blur}}` is specified by the parameter
|
|
709
|
+
``gaussian_filter_std_dev``.
|
|
710
|
+
|
|
711
|
+
To add shot noise effects to the image of the target fake CBED pattern, the
|
|
712
|
+
parameter ``apply_shot_noise`` needs to be set to ``True``.
|
|
713
|
+
|
|
714
|
+
For some pixelated electron detectors, the pixels of a number
|
|
715
|
+
:math:`N_{\text{DPW}}` of contiguous rows and an equal number of contiguous
|
|
716
|
+
columns will not measure or readout incident electron counts. Instead, the
|
|
717
|
+
final intensity values measured are inpainted according to the final
|
|
718
|
+
intensity values of the other pixels in the detector. The intersection of
|
|
719
|
+
the aforementioned contiguous block of rows and the aforementioned
|
|
720
|
+
contiguous block of columns is located within one pixel of the center of the
|
|
721
|
+
detector. The integer :math:`N_{\text{DPW}}`, which we call the detector
|
|
722
|
+
partition width in units of pixels, is specified by the parameter
|
|
723
|
+
``detector_partition_width_in_pixels``.
|
|
724
|
+
|
|
725
|
+
Cold pixels, which are individual zero-valued pixels in the image of the
|
|
726
|
+
target fake CBED pattern, are specified by the parameter
|
|
727
|
+
``cold_pixels``. Let :math:`N_{\text{CP}}` be the number of cold pixels in
|
|
728
|
+
the image of the target fake CBED pattern. Furthermore, let :math:`\left\{
|
|
729
|
+
n_{k;\text{CP}}\right\} _{k=0}^{N_{\text{CP}}-1}` and :math:`\left\{
|
|
730
|
+
m_{k;\text{CP}}\right\} _{k=0}^{N_{\text{CP}}-1}` be integer sequences
|
|
731
|
+
respectively, where :math:`n_{k;\text{CP}}` and :math:`m_{k;\text{CP}}` are
|
|
732
|
+
the row and column indices respectively of the :math:`k^{\text{th}}` cold
|
|
733
|
+
pixel. For every nonnegative integer ``k`` less than :math:`N_{\text{CP}}`,
|
|
734
|
+
``cold_pixels[k][0]`` and ````cold_pixels[k][1]`` are
|
|
735
|
+
:math:`n_{k;\text{CP}}` and :math:`m_{k;\text{CP}}` respectively, with the
|
|
736
|
+
integer :math:`k` being equal to the value of ``k``.
|
|
737
|
+
|
|
738
|
+
The mask frame of the image of the target fake CBED pattern is specified by
|
|
739
|
+
the parameter ``mask_frame``, which is expected to be a 4-element tuple,
|
|
740
|
+
:math:`\left(L, R, B, T\right)`, of nonnegative integers. ``mask_frame[0]``,
|
|
741
|
+
``mask_frame[1]``, ``mask_frame[2]``, and ``mask_frame[3]`` are the widths,
|
|
742
|
+
in units of pixels, of the left, right, bottom, and top sides of the mask
|
|
743
|
+
frame respectively. If all elements of ``mask_frame`` are zero, then no
|
|
744
|
+
pixels in the image of the target fake CBED pattern are masked by the mask
|
|
745
|
+
frame.
|
|
746
|
+
|
|
747
|
+
Below we describe in more detail how various attributes of the current class
|
|
748
|
+
are effectively calculated. Before doing so, we need to introduce a few more
|
|
749
|
+
quantities:
|
|
750
|
+
|
|
751
|
+
.. math ::
|
|
752
|
+
j\in\left\{ j^{\prime}\right\}_{j^{\prime}=0}^{
|
|
753
|
+
N_{\mathcal{\mathring{I}};x}-1},
|
|
754
|
+
:label: j_range__1
|
|
755
|
+
|
|
756
|
+
.. math ::
|
|
757
|
+
i\in\left\{ i^{\prime}\right\} _{i^{\prime}=0}^{
|
|
758
|
+
N_{\mathcal{\mathring{I}};y}-1}
|
|
759
|
+
:label: i_range__1
|
|
760
|
+
|
|
761
|
+
.. math ::
|
|
762
|
+
q_{\mathcal{\mathring{I}};x;j}=\left(j+\frac{1}{2}\right)
|
|
763
|
+
\Delta q_{\mathcal{\mathring{I}};x},
|
|
764
|
+
:label: q_I_circ_x_j__1
|
|
765
|
+
|
|
766
|
+
.. math ::
|
|
767
|
+
q_{\mathcal{\mathring{I}};y;i}=1-\left(i+\frac{1}{2}\right)
|
|
768
|
+
\Delta q_{\mathcal{\mathring{I}};y},
|
|
769
|
+
:label: q_I_circ_y_i__1
|
|
770
|
+
|
|
771
|
+
.. math ::
|
|
772
|
+
\Delta q_{\mathcal{\mathring{I}};x}=
|
|
773
|
+
\frac{1}{N_{\mathcal{\mathring{I}};x}},
|
|
774
|
+
:label: Delta_q_I_circ_x__1
|
|
775
|
+
|
|
776
|
+
.. math ::
|
|
777
|
+
\Delta q_{\mathcal{\mathring{I}};y}=
|
|
778
|
+
\frac{1}{N_{\mathcal{\mathring{I}};y}}.
|
|
779
|
+
:label: Delta_q_I_circ_y__1
|
|
780
|
+
|
|
781
|
+
.. math ::
|
|
782
|
+
m\in\left\{ m^{\prime}\right\} _{m^{\prime}=0}^{N_{\mathcal{I};x}-1},
|
|
783
|
+
:label: m_range__1
|
|
784
|
+
|
|
785
|
+
.. math ::
|
|
786
|
+
n\in\left\{ n^{\prime}\right\} _{n^{\prime}=0}^{N_{\mathcal{I};y}-1},
|
|
787
|
+
:label: n_range__1
|
|
788
|
+
|
|
789
|
+
and
|
|
790
|
+
|
|
791
|
+
.. math ::
|
|
792
|
+
\mathbf{J}_{\square}\left(q_{x},q_{y}\right)=
|
|
793
|
+
\begin{pmatrix}\frac{\partial T_{\square;x}}{\partial q_{x}}
|
|
794
|
+
& \frac{\partial T_{\square;x}}{\partial q_{y}}\\
|
|
795
|
+
\frac{\partial T_{\square;y}}{\partial q_{x}}
|
|
796
|
+
& \frac{\partial T_{\square;y}}{\partial q_{y}}
|
|
797
|
+
\end{pmatrix},
|
|
798
|
+
:label: J_sq__1
|
|
799
|
+
|
|
800
|
+
where the derivatives in Eq. :eq:`J_sq__1` are calculated numerically using
|
|
801
|
+
the second-order accurate central differences method. The aforementioned
|
|
802
|
+
attributes of the current class are effectively calculated by executing the
|
|
803
|
+
following steps:
|
|
804
|
+
|
|
805
|
+
1. Calculate
|
|
806
|
+
|
|
807
|
+
.. math ::
|
|
808
|
+
\mathring{\mathcal{I}}_{\text{OI};⌑;i,j}\leftarrow
|
|
809
|
+
\mathcal{I}_{\text{OI}}\left(
|
|
810
|
+
T_{\square;x}\left(q_{\mathring{\mathcal{I}};x;j},
|
|
811
|
+
q_{\mathring{\mathcal{I}};y;i}\right),
|
|
812
|
+
T_{\square;y}\left(q_{\mathring{\mathcal{I}};x;j},
|
|
813
|
+
q_{\mathring{\mathcal{I}};y;i}\right)\right),
|
|
814
|
+
:label: HD_I_OI__1
|
|
815
|
+
|
|
816
|
+
.. math ::
|
|
817
|
+
\mathring{\mathcal{I}}_{\text{OI};⌑;i,j}\leftarrow\begin{cases}
|
|
818
|
+
\text{True}, & \text{if }\mathring{\mathcal{I}}_{\text{OI};⌑;i,j}
|
|
819
|
+
\neq0,\\
|
|
820
|
+
\text{False}, & \text{otherwise},
|
|
821
|
+
\end{cases}
|
|
822
|
+
:label: HD_I_OI__2
|
|
823
|
+
|
|
824
|
+
and then apply max pooling to
|
|
825
|
+
:math:`\mathring{\mathcal{I}}_{\text{OI};⌑;i,j}` with a kernel of dimensions
|
|
826
|
+
:math:`\left(N_{\mathring{\mathcal{I}};y}/N_{\mathcal{I};y},
|
|
827
|
+
N_{\mathring{\mathcal{I}};x}/N_{\mathcal{I};x}\right)`
|
|
828
|
+
and store the result in :math:`\mathcal{I}_{\text{OI};⌑;n,m}`.
|
|
829
|
+
|
|
830
|
+
2. Calculate
|
|
831
|
+
|
|
832
|
+
.. math ::
|
|
833
|
+
\mathcal{I}_{\text{DOM};⌑;n,m}\leftarrow0.
|
|
834
|
+
:label: LD_I_DOM__1
|
|
835
|
+
|
|
836
|
+
3. Calculate
|
|
837
|
+
|
|
838
|
+
.. math ::
|
|
839
|
+
\mathcal{I}_{\text{MF};⌑;n,m}\leftarrow\begin{cases}
|
|
840
|
+
\text{True}, & \text{if }L\le m<N_{\mathcal{I};x}-R
|
|
841
|
+
\text{ and }T\le n<N_{\mathcal{I};y}-B,\\
|
|
842
|
+
\text{False}, & \text{otherwise}.
|
|
843
|
+
\end{cases}
|
|
844
|
+
:label: LD_I_MF__1
|
|
845
|
+
|
|
846
|
+
4. For :math:`0\le k<N_{\text{D}}`, calculate
|
|
847
|
+
|
|
848
|
+
.. math ::
|
|
849
|
+
\mathring{\mathcal{I}}_{\text{CBED};⌑;i,j}&
|
|
850
|
+
\leftarrow\mathring{\mathcal{I}}_{\text{CBED};⌑;i,j}\\&
|
|
851
|
+
\quad\quad\mathop{+}\mathcal{I}_{k;\text{D}}\left(T_{\square;x}\left(
|
|
852
|
+
q_{\mathring{\mathcal{I}};x;j},q_{\mathring{\mathcal{I}};y;i}\right),
|
|
853
|
+
T_{\square;y}\left(q_{\mathring{\mathcal{I}};x;j},
|
|
854
|
+
q_{\mathring{\mathcal{I}};y;i}\right)\right),
|
|
855
|
+
:label: HD_I_CBED__1
|
|
856
|
+
|
|
857
|
+
.. math ::
|
|
858
|
+
\mathring{\mathcal{I}}_{k;\text{DS};⌑;i,j}\leftarrow
|
|
859
|
+
\mathcal{I}_{k;\text{DS}}\left(T_{\square;x}\left(
|
|
860
|
+
q_{\mathring{\mathcal{I}};x;j},q_{\mathring{\mathcal{I}};y;i}\right),
|
|
861
|
+
T_{\square;y}\left(q_{\mathring{\mathcal{I}};x;j},
|
|
862
|
+
q_{\mathring{\mathcal{I}};y;i}\right)\right),
|
|
863
|
+
:label: HD_I_k_DS__1
|
|
864
|
+
|
|
865
|
+
.. math ::
|
|
866
|
+
\mathring{\mathcal{I}}_{k;\text{DS};⌑;i,j}\leftarrow\begin{cases}
|
|
867
|
+
\text{True}, & \text{if }\mathring{\mathcal{I}}_{k;\text{DS};⌑;i,j}
|
|
868
|
+
\neq0,\\
|
|
869
|
+
\text{False}, & \text{otherwise},
|
|
870
|
+
\end{cases}
|
|
871
|
+
:label: HD_I_k_DS__2
|
|
872
|
+
|
|
873
|
+
then apply max pooling to :math:`\mathring{\mathcal{I}}_{k;\text{DS};⌑;i,j}`
|
|
874
|
+
with a kernel of dimensions
|
|
875
|
+
:math:`\left(N_{\mathring{\mathcal{I}};y}/N_{\mathcal{I};y},
|
|
876
|
+
N_{\mathring{\mathcal{I}};x}/N_{\mathcal{I};x}\right)` and store the result
|
|
877
|
+
in :math:`\mathcal{I}_{k;\text{DS};⌑;n,m}`, and calculate
|
|
878
|
+
|
|
879
|
+
.. math ::
|
|
880
|
+
\mathcal{I}_{\text{DOM};⌑;n,m}\leftarrow
|
|
881
|
+
\mathcal{I}_{\text{DOM};⌑;n,m}+\mathcal{I}_{k;\text{DS};⌑;n,m}.
|
|
882
|
+
:label: LD_I_DOM__2
|
|
883
|
+
|
|
884
|
+
5. Calculate
|
|
885
|
+
|
|
886
|
+
.. math ::
|
|
887
|
+
\mathcal{I}_{\text{DOM};⌑;n,m}\leftarrow
|
|
888
|
+
\mathcal{I}_{\text{MF};⌑;n,m}
|
|
889
|
+
\mathcal{I}_{\text{OI};⌑;n,m}
|
|
890
|
+
\mathcal{I}_{\text{DOM};⌑;n,m}.
|
|
891
|
+
:label: LD_I_DOM__3
|
|
892
|
+
|
|
893
|
+
6. For :math:`0\le k<N_{\text{M}}`, calculate
|
|
894
|
+
|
|
895
|
+
.. math ::
|
|
896
|
+
\mathring{\mathcal{I}}_{\text{CBED};⌑;i,j}&
|
|
897
|
+
\leftarrow\mathring{\mathcal{I}}_{\text{CBED};⌑;i,j}\\&
|
|
898
|
+
\quad\quad\mathop{+}\mathcal{I}_{k;\text{M}}\left(T_{\square;x}\left(
|
|
899
|
+
q_{\mathring{\mathcal{I}};x;j},q_{\mathring{\mathcal{I}};y;i}\right),
|
|
900
|
+
T_{\square;y}\left(q_{\mathring{\mathcal{I}};x;j},
|
|
901
|
+
q_{\mathring{\mathcal{I}};y;i}\right)\right).
|
|
902
|
+
:label: HD_I_CBED__2
|
|
903
|
+
|
|
904
|
+
7. Calculate
|
|
905
|
+
|
|
906
|
+
.. math ::
|
|
907
|
+
\mathring{\mathcal{I}}_{\text{CBED};⌑;i,j}\leftarrow
|
|
908
|
+
\text{det}\left(\mathbf{J}_{\square}\left(
|
|
909
|
+
q_{\mathring{\mathcal{I}};x;j},
|
|
910
|
+
q_{\mathring{\mathcal{I}};y;i}\right)\right)
|
|
911
|
+
\mathring{\mathcal{I}}_{\text{CBED};⌑;i,j}.
|
|
912
|
+
:label: HD_I_CBED__3
|
|
913
|
+
|
|
914
|
+
8. Apply average pooling to
|
|
915
|
+
:math:`\mathring{\mathcal{I}}_{\text{CBED};⌑;i,j}` with a kernel of
|
|
916
|
+
dimensions :math:`\left(N_{\mathring{\mathcal{I}};y}/N_{\mathcal{I};y},
|
|
917
|
+
N_{\mathring{\mathcal{I}};x}/N_{\mathcal{I};x}\right)`, and store the result
|
|
918
|
+
in :math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
919
|
+
|
|
920
|
+
9. Apply a Gaussian filter to :math:`\mathcal{I}_{\text{CBED};⌑;n,m}` that
|
|
921
|
+
is identical in outcome to that implemented by the function
|
|
922
|
+
:func:`scipy.ndimage.gaussian_filter`, with ``sigma`` set to
|
|
923
|
+
``gaussian_filter_std_dev`` and ``truncate`` set to ``4``, and store the
|
|
924
|
+
result in :math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
925
|
+
|
|
926
|
+
10. Calculate
|
|
927
|
+
|
|
928
|
+
.. math ::
|
|
929
|
+
k_{\text{I};1}\leftarrow
|
|
930
|
+
\left\lfloor \frac{N_{\mathcal{I};x}-1}{2}\right\rfloor
|
|
931
|
+
-\left\lfloor \frac{N_{\text{DPW}}}{2}\right\rfloor ,
|
|
932
|
+
:label: k_I_1__1
|
|
933
|
+
|
|
934
|
+
and
|
|
935
|
+
|
|
936
|
+
.. math ::
|
|
937
|
+
k_{\text{I};2}\leftarrow k_{\text{I};1}+N_{\text{DPW}}-1.
|
|
938
|
+
:label: k_I_2__1
|
|
939
|
+
|
|
940
|
+
11. If ``apply_shot_noise`` is set to ``True``, then apply shot/Poisson
|
|
941
|
+
noise to :math:`\mathcal{I}_{\text{CBED};⌑;n,m}`, and store the result in
|
|
942
|
+
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
943
|
+
|
|
944
|
+
12. If :math:`N_{\text{DPW}}>0`, then inpaint the pixels in the rows indexed
|
|
945
|
+
from :math:`k_{\text{I};1}` to :math:`k_{\text{I};2}` and the columns
|
|
946
|
+
indexed from :math:`k_{\text{I};1}` to :math:`k_{\text{I};2}` of the image
|
|
947
|
+
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}` using the function
|
|
948
|
+
:func:`skimage.restoration.inpaint_biharmonic`, and store the result in
|
|
949
|
+
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
950
|
+
|
|
951
|
+
13. Calculate
|
|
952
|
+
|
|
953
|
+
.. math ::
|
|
954
|
+
\mathcal{I}_{\text{CBED};⌑;n,m}\leftarrow
|
|
955
|
+
\mathcal{I}_{\text{MF};⌑;n,m}
|
|
956
|
+
\mathcal{I}_{\text{OI};⌑;n,m}
|
|
957
|
+
\mathcal{I}_{\text{CBED};⌑;n,m}.
|
|
958
|
+
:label: LD_I_CBED__1
|
|
959
|
+
|
|
960
|
+
14. Update pixels of :math:`\mathcal{I}_{\text{CBED};⌑;n,m}` at pixel
|
|
961
|
+
locations specified by ``cold_pixels`` to the value of zero.
|
|
962
|
+
|
|
963
|
+
15. Apply min-max normalization of :math:`\mathcal{I}_{\text{CBED};⌑;n,m}`,
|
|
964
|
+
and store result in :math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
965
|
+
|
|
966
|
+
16. Calculate
|
|
967
|
+
|
|
968
|
+
.. math ::
|
|
969
|
+
\mathcal{I}_{\text{CS};⌑;n,m}\leftarrow1
|
|
970
|
+
-\mathcal{I}_{\text{MF};⌑;n,m}\mathcal{I}_{\text{OI};⌑;n,m}.
|
|
971
|
+
:label: LD_I_CS__1
|
|
972
|
+
|
|
973
|
+
17. Convolve a :math:`3 \times 3` filter of ones over a symmetrically unity-padded
|
|
974
|
+
:math:`\mathcal{I}_{\text{CS};⌑;n,m}` to yield an output matrix with the
|
|
975
|
+
same dimensions of :math:`\mathcal{I}_{\text{CBED};⌑;n,m}`, and store said
|
|
976
|
+
output matrix in :math:`\mathcal{I}_{\text{CS};⌑;n,m}`.
|
|
977
|
+
|
|
978
|
+
18. For :math:`0\le k<N_{\text{D}}`, calculate
|
|
979
|
+
|
|
980
|
+
.. math ::
|
|
981
|
+
\mathcal{I}_{k;\text{DCM};⌑;n,m}\leftarrow
|
|
982
|
+
\mathcal{I}_{\text{CS};⌑;n,m}\mathcal{I}_{k;\text{DS};⌑;n,m},
|
|
983
|
+
:label: LD_I_DCM__1
|
|
984
|
+
|
|
985
|
+
.. math ::
|
|
986
|
+
\Omega_{k;\text{DCR};⌑}\leftarrow\begin{cases}
|
|
987
|
+
\text{True}, & \text{if }\sum_{n,m}\mathcal{I}_{k;\text{DCM};⌑;n,m}
|
|
988
|
+
\neq0,\\
|
|
989
|
+
\text{False}, & \text{otherwise},
|
|
990
|
+
\end{cases}
|
|
991
|
+
:label: Omega_k_DCR__1
|
|
992
|
+
|
|
993
|
+
and
|
|
994
|
+
|
|
995
|
+
.. math ::
|
|
996
|
+
\Omega_{k;\text{DAR};⌑}\leftarrow\begin{cases}
|
|
997
|
+
\text{True}, & \text{if }\sum_{n,m}\mathcal{I}_{k;\text{DS};⌑;n,m}=0,\\
|
|
998
|
+
\text{False}, & \text{otherwise}.
|
|
999
|
+
\end{cases}
|
|
1000
|
+
:label: Omega_k_DAR__1
|
|
1001
|
+
|
|
1002
|
+
We refer to :math:`\mathcal{I}_{\text{CBED};⌑;n,m}` as the image of the
|
|
1003
|
+
target fake CBED pattern, :math:`\mathcal{I}_{\text{OI};⌑;n,m}` as the image
|
|
1004
|
+
of the illumination support, :math:`\mathcal{I}_{\text{DOM};⌑;n,m}` as the
|
|
1005
|
+
image of the disk overlap map, :math:`\mathcal{I}_{k;\text{DS};⌑;n,m}` as
|
|
1006
|
+
the image of the support of the :math:`k^{\text{th}}` CBED disk,
|
|
1007
|
+
:math:`\left\{ \Omega_{k;\text{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}` as
|
|
1008
|
+
the disk clipping registry, and :math:`\left\{
|
|
1009
|
+
\Omega_{k;\text{DAR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}` as the disk absence
|
|
1010
|
+
registry.
|
|
1011
|
+
|
|
1012
|
+
Parameters
|
|
1013
|
+
----------
|
|
1014
|
+
undistorted_tds_model : :class:`fakecbed.tds.Model` | `None`, optional
|
|
1015
|
+
The intensity pattern of the undistorted TDS model,
|
|
1016
|
+
:math:`\mathcal{I}_{\text{TDS}}\left(u_{x},u_{y}\right)`. If
|
|
1017
|
+
``undistorted_tds_model`` is set to ``None``, then the parameter will be
|
|
1018
|
+
reassigned to the value ``fakecbed.tds.Model()``.
|
|
1019
|
+
undistorted_disks : `array_like` (:class:`fakecbed.shapes.NonuniformBoundedShape`, ndim=1), optional
|
|
1020
|
+
The intensity patterns of the undistorted fake CBED disks,
|
|
1021
|
+
:math:`\left\{ \mathcal{I}_{k;\text{D}}\left(u_{x},u_{y}\right)\right\}
|
|
1022
|
+
_{k=0}^{N_{\text{D}}-1}`. For every nonnegative integer ``k`` less than
|
|
1023
|
+
:math:`N_{\text{D}}`, ``undistorted_disks[k]`` is
|
|
1024
|
+
:math:`\mathcal{I}_{k;\text{D}}\left(u_{x},u_{y}\right)`, with the
|
|
1025
|
+
integer :math:`k` being equal to the value of ``k``
|
|
1026
|
+
undistorted_misc_shapes : `array_like` (`any_shape`, ndim=1), optional
|
|
1027
|
+
The intensity patterns of the undistorted miscellaneous shapes,
|
|
1028
|
+
:math:`\left\{ \mathcal{I}_{k;\text{M}}\left(u_{x},u_{y}\right)\right\}
|
|
1029
|
+
_{k=0}^{N_{\text{M}}-1}`. Note that `any_shape` means any public class
|
|
1030
|
+
defined in the module :mod:`fakecbed.shapes` that is a subclass of
|
|
1031
|
+
:class:`fakecbed.shapes.BaseShape`.
|
|
1032
|
+
undistorted_outer_illumination_shape : :class:`fakecbed.shapes.Circle` | :class:`fakecbed.shapes.Ellipse` | :class:`fakecbed.shapes.GenericBlob` | `None`, optional
|
|
1033
|
+
The intensity pattern of the undistorted outer illumination shape,
|
|
1034
|
+
:math:`\mathcal{I}_{\text{OI}}\left(u_{x},u_{y}\right)`. If
|
|
1035
|
+
``undistorted_outer_illumination_shape`` is set to ``None``, then
|
|
1036
|
+
:math:`\mathcal{I}_{\text{OI}}\left(u_{x},u_{y}\right)` will equal unity
|
|
1037
|
+
for all :math:`u_{x}` and :math:`u_{y}`.
|
|
1038
|
+
gaussian_filter_std_dev : `float`, optional
|
|
1039
|
+
The standard deviation :math:`\sigma_{\text{blur}}` of the Gaussian
|
|
1040
|
+
filter used to yield such blur effects on the target fake CBED
|
|
1041
|
+
pattern. Must be nonnegative.
|
|
1042
|
+
num_pixels_across_pattern : `int`, optional
|
|
1043
|
+
The number of pixels across the image of the fake CBED pattern,
|
|
1044
|
+
:math:`N_{\mathcal{I};x}`. Must be positive.
|
|
1045
|
+
distortion_model : :class:`distoptica.DistortionModel` | `None`, optional
|
|
1046
|
+
The distortion model. If ``distortion_model`` is set to ``None``, then
|
|
1047
|
+
the parameter will be reassigned to the value
|
|
1048
|
+
``distoptica.DistortionModel(sampling_grid_dims_in_pixels=(N_x, N_x))``,
|
|
1049
|
+
Where ``N_x`` is equal to ``num_pixels_across_pattern``.
|
|
1050
|
+
apply_shot_noise : `bool`, optional
|
|
1051
|
+
If ``apply_shot_noise`` is set to ``True``, then shot noise is applied
|
|
1052
|
+
to the image of the fake CBED pattern. Otherwise, no shot noise is
|
|
1053
|
+
applied.
|
|
1054
|
+
detector_partition_width_in_pixels : `int`, optional
|
|
1055
|
+
The detector partition width in units of pixels,
|
|
1056
|
+
:math:`N_{\text{DPW}}`. Must be nonnegative.
|
|
1057
|
+
cold_pixels : `array_like` (`int`, ndim=2), optional
|
|
1058
|
+
The pixel coordinates of the cold pixels.
|
|
1059
|
+
mask_frame : `array_like` (`int`, shape=(4,)), optional
|
|
1060
|
+
``mask_frame`` specifies the mask frame of the image of the target fake
|
|
1061
|
+
CBED pattern. ``mask_frame[0]``, ``mask_frame[1]``, ``mask_frame[2]``,
|
|
1062
|
+
and ``mask_frame[3]`` are the widths, in units of pixels, of the left,
|
|
1063
|
+
right, bottom, and top sides of the mask frame respectively. If all
|
|
1064
|
+
elements of ``mask_frame`` are zero, then no pixels in the image of the
|
|
1065
|
+
target fake CBED pattern are masked by the mask frame.
|
|
1066
|
+
|
|
1067
|
+
"""
|
|
1068
|
+
ctor_param_names = ("undistorted_tds_model",
|
|
1069
|
+
"undistorted_disks",
|
|
1070
|
+
"undistorted_misc_shapes",
|
|
1071
|
+
"undistorted_outer_illumination_shape",
|
|
1072
|
+
"gaussian_filter_std_dev",
|
|
1073
|
+
"num_pixels_across_pattern",
|
|
1074
|
+
"distortion_model",
|
|
1075
|
+
"apply_shot_noise",
|
|
1076
|
+
"detector_partition_width_in_pixels",
|
|
1077
|
+
"cold_pixels",
|
|
1078
|
+
"mask_frame")
|
|
1079
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
1080
|
+
"ctor_param_names": ctor_param_names}
|
|
1081
|
+
|
|
1082
|
+
_validation_and_conversion_funcs_ = \
|
|
1083
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
1084
|
+
_pre_serialization_funcs_ = \
|
|
1085
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
1086
|
+
_de_pre_serialization_funcs_ = \
|
|
1087
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
1088
|
+
|
|
1089
|
+
del ctor_param_names, kwargs
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
def __init__(self,
|
|
1094
|
+
undistorted_tds_model=\
|
|
1095
|
+
_default_undistorted_tds_model,
|
|
1096
|
+
undistorted_disks=\
|
|
1097
|
+
_default_undistorted_disks,
|
|
1098
|
+
undistorted_misc_shapes=\
|
|
1099
|
+
_default_undistorted_misc_shapes,
|
|
1100
|
+
undistorted_outer_illumination_shape=\
|
|
1101
|
+
_default_undistorted_outer_illumination_shape,
|
|
1102
|
+
gaussian_filter_std_dev=\
|
|
1103
|
+
_default_gaussian_filter_std_dev,
|
|
1104
|
+
num_pixels_across_pattern=\
|
|
1105
|
+
_default_num_pixels_across_pattern,
|
|
1106
|
+
distortion_model=\
|
|
1107
|
+
_default_distortion_model,
|
|
1108
|
+
apply_shot_noise=\
|
|
1109
|
+
_default_apply_shot_noise,
|
|
1110
|
+
detector_partition_width_in_pixels=\
|
|
1111
|
+
_default_detector_partition_width_in_pixels,
|
|
1112
|
+
cold_pixels=\
|
|
1113
|
+
_default_cold_pixels,
|
|
1114
|
+
mask_frame=\
|
|
1115
|
+
_default_mask_frame,
|
|
1116
|
+
skip_validation_and_conversion=\
|
|
1117
|
+
_default_skip_validation_and_conversion):
|
|
1118
|
+
ctor_params = {key: val
|
|
1119
|
+
for key, val in locals().items()
|
|
1120
|
+
if (key not in ("self", "__class__"))}
|
|
1121
|
+
kwargs = ctor_params
|
|
1122
|
+
kwargs["skip_cls_tests"] = True
|
|
1123
|
+
fancytypes.PreSerializableAndUpdatable.__init__(self, **kwargs)
|
|
1124
|
+
|
|
1125
|
+
self.execute_post_core_attrs_update_actions()
|
|
1126
|
+
|
|
1127
|
+
return None
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
@classmethod
|
|
1132
|
+
def get_validation_and_conversion_funcs(cls):
|
|
1133
|
+
validation_and_conversion_funcs = \
|
|
1134
|
+
cls._validation_and_conversion_funcs_.copy()
|
|
1135
|
+
|
|
1136
|
+
return validation_and_conversion_funcs
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
@classmethod
|
|
1141
|
+
def get_pre_serialization_funcs(cls):
|
|
1142
|
+
pre_serialization_funcs = \
|
|
1143
|
+
cls._pre_serialization_funcs_.copy()
|
|
1144
|
+
|
|
1145
|
+
return pre_serialization_funcs
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
@classmethod
|
|
1150
|
+
def get_de_pre_serialization_funcs(cls):
|
|
1151
|
+
de_pre_serialization_funcs = \
|
|
1152
|
+
cls._de_pre_serialization_funcs_.copy()
|
|
1153
|
+
|
|
1154
|
+
return de_pre_serialization_funcs
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
def execute_post_core_attrs_update_actions(self):
|
|
1159
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
1160
|
+
for self_core_attr_name in self_core_attrs:
|
|
1161
|
+
attr_name = "_"+self_core_attr_name
|
|
1162
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
1163
|
+
setattr(self, attr_name, attr)
|
|
1164
|
+
|
|
1165
|
+
self._num_disks = len(self._undistorted_disks)
|
|
1166
|
+
self._device = self._distortion_model.device
|
|
1167
|
+
|
|
1168
|
+
self._illumination_support = None
|
|
1169
|
+
self._image = None
|
|
1170
|
+
self._image_has_been_overridden = False
|
|
1171
|
+
self._signal = None
|
|
1172
|
+
self._disk_clipping_registry = None
|
|
1173
|
+
self._disk_supports = None
|
|
1174
|
+
self._disk_absence_registry = None
|
|
1175
|
+
self._disk_overlap_map = None
|
|
1176
|
+
|
|
1177
|
+
return None
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
def update(self,
|
|
1182
|
+
new_core_attr_subset_candidate,
|
|
1183
|
+
skip_validation_and_conversion=\
|
|
1184
|
+
_default_skip_validation_and_conversion):
|
|
1185
|
+
super().update(new_core_attr_subset_candidate,
|
|
1186
|
+
skip_validation_and_conversion)
|
|
1187
|
+
self.execute_post_core_attrs_update_actions()
|
|
1188
|
+
|
|
1189
|
+
return None
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
@property
|
|
1194
|
+
def num_disks(self):
|
|
1195
|
+
r"""`int`: The total number of CBED disks defined, :math:`N_{\text{D}}`.
|
|
1196
|
+
|
|
1197
|
+
See the summary documentation of the class
|
|
1198
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context.
|
|
1199
|
+
|
|
1200
|
+
Let ``core_attrs`` denote the attribute
|
|
1201
|
+
:attr:`~fancytypes.Checkable.core_attrs`. ``num_disks`` is equal to
|
|
1202
|
+
``len(core_attrs["undistorted_disks"])``.
|
|
1203
|
+
|
|
1204
|
+
Note that ``num_disks`` should be considered **read-only**.
|
|
1205
|
+
|
|
1206
|
+
"""
|
|
1207
|
+
result = self._num_disks
|
|
1208
|
+
|
|
1209
|
+
return result
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
@property
|
|
1214
|
+
def device(self):
|
|
1215
|
+
r"""`torch.device`: The device on which computationally intensive
|
|
1216
|
+
PyTorch operations are performed and attributes of the type
|
|
1217
|
+
:class:`torch.Tensor` are stored.
|
|
1218
|
+
|
|
1219
|
+
Note that ``device`` should be considered **read-only**.
|
|
1220
|
+
|
|
1221
|
+
"""
|
|
1222
|
+
result = copy.deepcopy(self._device)
|
|
1223
|
+
|
|
1224
|
+
return result
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
def override_image_then_reapply_mask(
|
|
1229
|
+
self,
|
|
1230
|
+
overriding_image,
|
|
1231
|
+
skip_validation_and_conversion=\
|
|
1232
|
+
_default_skip_validation_and_conversion):
|
|
1233
|
+
r"""Override the target fake CBED pattern image and reapply masking.
|
|
1234
|
+
|
|
1235
|
+
See the summary documentation of the class
|
|
1236
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context.
|
|
1237
|
+
|
|
1238
|
+
Let ``image``, ``illumination_support``, and ``core_attrs`` denote the
|
|
1239
|
+
attributes :attr:`fakecbed.discretized.CBEDPattern.image`,
|
|
1240
|
+
:attr:`fakecbed.discretized.CBEDPattern.illumination_support`, and
|
|
1241
|
+
:attr:`~fancytypes.Checkable.core_attrs`. ``overriding_image`` is the
|
|
1242
|
+
overriding image.
|
|
1243
|
+
|
|
1244
|
+
Upon calling the method ``override_image_then_reapply_mask``, the
|
|
1245
|
+
attribute ``image`` is updated effectively by:
|
|
1246
|
+
|
|
1247
|
+
.. code-block:: python
|
|
1248
|
+
|
|
1249
|
+
coords_of_cold_pixels = core_attrs["cold_pixels"]
|
|
1250
|
+
L, R, B, T = core_attrs["mask_frame"]
|
|
1251
|
+
N_I_x = core_attrs["num_pixels_across_pattern"]
|
|
1252
|
+
N_I_y = N_I_x
|
|
1253
|
+
|
|
1254
|
+
image = (overriding_image * illumination_support).clip(min=0)
|
|
1255
|
+
image[:T, :] = 0
|
|
1256
|
+
image[max(N_I_y-B, 0):, :] = 0
|
|
1257
|
+
image[:, :L] = 0
|
|
1258
|
+
image[:, max(N_I_x-R, 0):] = 0
|
|
1259
|
+
for coords_of_cold_pixel in coords_of_cold_pixels:
|
|
1260
|
+
image[coords_of_cold_pixel] = 0
|
|
1261
|
+
|
|
1262
|
+
and then finally min-max normalization is applied to ``image``.
|
|
1263
|
+
|
|
1264
|
+
Parameters
|
|
1265
|
+
----------
|
|
1266
|
+
overriding_image : `array_like` (`float`, shape=image.shape)
|
|
1267
|
+
The overriding image.
|
|
1268
|
+
skip_validation_and_conversion : `bool`, optional
|
|
1269
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then
|
|
1270
|
+
validations and conversions are performed on the above parameters.
|
|
1271
|
+
|
|
1272
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
1273
|
+
no validations and conversions are performed on the above
|
|
1274
|
+
parameters. This option is desired primarily when the user wants to
|
|
1275
|
+
avoid potentially expensive validation and/or conversion operations.
|
|
1276
|
+
|
|
1277
|
+
"""
|
|
1278
|
+
params = locals()
|
|
1279
|
+
|
|
1280
|
+
func_alias = _check_and_convert_skip_validation_and_conversion
|
|
1281
|
+
skip_validation_and_conversion = func_alias(params)
|
|
1282
|
+
|
|
1283
|
+
if (skip_validation_and_conversion == False):
|
|
1284
|
+
params = {"overriding_image": \
|
|
1285
|
+
overriding_image,
|
|
1286
|
+
"num_pixels_across_pattern": \
|
|
1287
|
+
self._num_pixels_across_pattern,
|
|
1288
|
+
"device": \
|
|
1289
|
+
self._device}
|
|
1290
|
+
overriding_image = _check_and_convert_overriding_image(params)
|
|
1291
|
+
|
|
1292
|
+
self._override_image_then_reapply_mask(overriding_image)
|
|
1293
|
+
|
|
1294
|
+
return None
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
def _override_image_then_reapply_mask(self, overriding_image):
|
|
1299
|
+
if self._illumination_support is None:
|
|
1300
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
1301
|
+
method_name = "_calc_illumination_support"
|
|
1302
|
+
method_alias = getattr(self, method_name)
|
|
1303
|
+
self._illumination_support = method_alias(u_x, u_y)
|
|
1304
|
+
illumination_support = self._illumination_support
|
|
1305
|
+
|
|
1306
|
+
coords_of_cold_pixels = self._cold_pixels
|
|
1307
|
+
L, R, B, T = self._mask_frame
|
|
1308
|
+
N_I_x = self._num_pixels_across_pattern
|
|
1309
|
+
N_I_y = N_I_x
|
|
1310
|
+
|
|
1311
|
+
image = overriding_image*illumination_support
|
|
1312
|
+
image[:T, :] = 0
|
|
1313
|
+
image[max(N_I_y-B, 0):, :] = 0
|
|
1314
|
+
image[:, :L] = 0
|
|
1315
|
+
image[:, max(N_I_x-R, 0):] = 0
|
|
1316
|
+
for coords_of_cold_pixel in coords_of_cold_pixels:
|
|
1317
|
+
image[coords_of_cold_pixel] = 0
|
|
1318
|
+
|
|
1319
|
+
kwargs = {"input_matrix": image}
|
|
1320
|
+
image = self._normalize_matrix(**kwargs)
|
|
1321
|
+
|
|
1322
|
+
self._image = image
|
|
1323
|
+
self._image_has_been_overridden = True
|
|
1324
|
+
|
|
1325
|
+
if self._signal is not None:
|
|
1326
|
+
self._signal.data[0] = image.numpy(force=True)
|
|
1327
|
+
|
|
1328
|
+
return None
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
def _calc_u_x_and_u_y(self):
|
|
1333
|
+
distortion_model = self._distortion_model
|
|
1334
|
+
|
|
1335
|
+
method_alias = distortion_model.get_sampling_grid
|
|
1336
|
+
sampling_grid = method_alias(deep_copy=False)
|
|
1337
|
+
|
|
1338
|
+
try:
|
|
1339
|
+
method_alias = \
|
|
1340
|
+
distortion_model.get_flow_field_of_coord_transform_right_inverse
|
|
1341
|
+
flow_field_of_coord_transform_right_inverse = \
|
|
1342
|
+
method_alias(deep_copy=False)
|
|
1343
|
+
except:
|
|
1344
|
+
err_msg = _cbed_pattern_err_msg_1
|
|
1345
|
+
raise RuntimeError(err_msg)
|
|
1346
|
+
|
|
1347
|
+
u_x = sampling_grid[0] + flow_field_of_coord_transform_right_inverse[0]
|
|
1348
|
+
u_y = sampling_grid[1] + flow_field_of_coord_transform_right_inverse[1]
|
|
1349
|
+
|
|
1350
|
+
return u_x, u_y
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
def _calc_illumination_support(self, u_x, u_y):
|
|
1355
|
+
shape = self._undistorted_outer_illumination_shape
|
|
1356
|
+
|
|
1357
|
+
pooler_kernel_size = self._calc_pooler_kernel_size()
|
|
1358
|
+
pooler = torch.nn.MaxPool2d(kernel_size=pooler_kernel_size)
|
|
1359
|
+
|
|
1360
|
+
illumination_support = (shape._eval(u_x, u_y) != 0)
|
|
1361
|
+
illumination_support = torch.unsqueeze(illumination_support, dim=0)
|
|
1362
|
+
illumination_support = torch.unsqueeze(illumination_support, dim=0)
|
|
1363
|
+
illumination_support = illumination_support.to(dtype=u_x.dtype)
|
|
1364
|
+
illumination_support = pooler(illumination_support)[0, 0]
|
|
1365
|
+
illumination_support = illumination_support.to(dtype=torch.bool)
|
|
1366
|
+
|
|
1367
|
+
return illumination_support
|
|
1368
|
+
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def _calc_pooler_kernel_size(self):
|
|
1372
|
+
distortion_model = self._distortion_model
|
|
1373
|
+
num_pixels_across_pattern = self._num_pixels_across_pattern
|
|
1374
|
+
|
|
1375
|
+
distortion_model_core_attrs = \
|
|
1376
|
+
distortion_model.get_core_attrs(deep_copy=False)
|
|
1377
|
+
sampling_grid_dims_in_pixels = \
|
|
1378
|
+
distortion_model_core_attrs["sampling_grid_dims_in_pixels"]
|
|
1379
|
+
|
|
1380
|
+
pooler_kernel_size = (sampling_grid_dims_in_pixels[1]
|
|
1381
|
+
// num_pixels_across_pattern,
|
|
1382
|
+
sampling_grid_dims_in_pixels[0]
|
|
1383
|
+
// num_pixels_across_pattern)
|
|
1384
|
+
|
|
1385
|
+
return pooler_kernel_size
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
def _normalize_matrix(self, input_matrix):
|
|
1390
|
+
if input_matrix.max()-input_matrix.min() > 0:
|
|
1391
|
+
normalization_weight = 1 / (input_matrix.max()-input_matrix.min())
|
|
1392
|
+
normalization_bias = -normalization_weight*input_matrix.min()
|
|
1393
|
+
output_matrix = (input_matrix*normalization_weight
|
|
1394
|
+
+ normalization_bias).clip(min=0, max=1)
|
|
1395
|
+
else:
|
|
1396
|
+
output_matrix = torch.zeros_like(input_matrix)
|
|
1397
|
+
|
|
1398
|
+
return output_matrix
|
|
1399
|
+
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
def get_signal(self, deep_copy=_default_deep_copy):
|
|
1403
|
+
r"""Return the hyperspy signal representation of the fake CBED pattern.
|
|
1404
|
+
|
|
1405
|
+
Parameters
|
|
1406
|
+
----------
|
|
1407
|
+
deep_copy : `bool`, optional
|
|
1408
|
+
Let ``signal`` denote the attribute
|
|
1409
|
+
:attr:`fakecbed.discretized.CBEDPattern.signal`.
|
|
1410
|
+
|
|
1411
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of ``signal``
|
|
1412
|
+
is returned. Otherwise, a reference to ``signal`` is returned.
|
|
1413
|
+
|
|
1414
|
+
Returns
|
|
1415
|
+
-------
|
|
1416
|
+
signal : :class:`hyperspy._signals.signal2d.Signal2D`
|
|
1417
|
+
The attribute :attr:`fakecbed.discretized.CBEDPattern.signal`.
|
|
1418
|
+
|
|
1419
|
+
"""
|
|
1420
|
+
params = {"deep_copy": deep_copy}
|
|
1421
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
1422
|
+
|
|
1423
|
+
if self._signal is None:
|
|
1424
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
1425
|
+
method_name = "_calc_signal_and_cache_select_intermediates"
|
|
1426
|
+
method_alias = getattr(self, method_name)
|
|
1427
|
+
self._signal = method_alias(u_x, u_y)
|
|
1428
|
+
|
|
1429
|
+
signal = (copy.deepcopy(self._signal)
|
|
1430
|
+
if (deep_copy == True)
|
|
1431
|
+
else self._signal)
|
|
1432
|
+
|
|
1433
|
+
return signal
|
|
1434
|
+
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
def _calc_signal_and_cache_select_intermediates(self, u_x, u_y):
|
|
1438
|
+
method_name = "_calc_signal_metadata_and_cache_select_intermediates"
|
|
1439
|
+
method_alias = getattr(self, method_name)
|
|
1440
|
+
signal_metadata = method_alias(u_x, u_y)
|
|
1441
|
+
|
|
1442
|
+
if self._image is None:
|
|
1443
|
+
method_name = "_calc_image_and_cache_select_intermediates"
|
|
1444
|
+
method_alias = getattr(self, method_name)
|
|
1445
|
+
self._image = method_alias(u_x, u_y)
|
|
1446
|
+
image = self._image.numpy(force=True)
|
|
1447
|
+
|
|
1448
|
+
if self._disk_overlap_map is None:
|
|
1449
|
+
method_name = ("_calc_disk_overlap_map"
|
|
1450
|
+
"_and_cache_select_intermediates")
|
|
1451
|
+
method_alias = getattr(self, method_name)
|
|
1452
|
+
self._disk_overlap_map = method_alias(u_x, u_y)
|
|
1453
|
+
disk_overlap_map = self._disk_overlap_map.numpy(force=True)
|
|
1454
|
+
|
|
1455
|
+
illumination_support = self._illumination_support.cpu().detach().clone()
|
|
1456
|
+
illumination_support = illumination_support.numpy(force=True)
|
|
1457
|
+
|
|
1458
|
+
disk_supports = self._disk_supports.numpy(force=True)
|
|
1459
|
+
|
|
1460
|
+
num_disks = self._num_disks
|
|
1461
|
+
L, R, B, T = self._mask_frame
|
|
1462
|
+
N_I_x = self._num_pixels_across_pattern
|
|
1463
|
+
N_I_y = N_I_x
|
|
1464
|
+
|
|
1465
|
+
signal_data_shape = (num_disks+3,) + image.shape
|
|
1466
|
+
|
|
1467
|
+
signal_data = np.zeros(signal_data_shape, dtype=image.dtype)
|
|
1468
|
+
signal_data[0] = image
|
|
1469
|
+
signal_data[1] = illumination_support
|
|
1470
|
+
signal_data[1, :T, :] = 0
|
|
1471
|
+
signal_data[1, max(N_I_y-B, 0):, :] = 0
|
|
1472
|
+
signal_data[1, :, :L] = 0
|
|
1473
|
+
signal_data[1, :, max(N_I_x-R, 0):] = 0
|
|
1474
|
+
signal_data[2] = disk_overlap_map
|
|
1475
|
+
signal_data[3:] = disk_supports
|
|
1476
|
+
|
|
1477
|
+
signal = hyperspy.signals.Signal2D(data=signal_data,
|
|
1478
|
+
metadata=signal_metadata)
|
|
1479
|
+
self._update_signal_axes(signal)
|
|
1480
|
+
|
|
1481
|
+
return signal
|
|
1482
|
+
|
|
1483
|
+
|
|
1484
|
+
|
|
1485
|
+
def _calc_signal_metadata_and_cache_select_intermediates(self, u_x, u_y):
|
|
1486
|
+
distortion_model = self._distortion_model
|
|
1487
|
+
|
|
1488
|
+
title = ("Fake Undistorted CBED Intensity Pattern"
|
|
1489
|
+
if distortion_model.is_trivial
|
|
1490
|
+
else "Fake Distorted CBED Intensity Pattern")
|
|
1491
|
+
|
|
1492
|
+
pre_serialized_core_attrs = self.pre_serialize()
|
|
1493
|
+
|
|
1494
|
+
if self._disk_clipping_registry is None:
|
|
1495
|
+
method_name = ("_calc_disk_clipping_registry"
|
|
1496
|
+
"_and_cache_select_intermediates")
|
|
1497
|
+
method_alias = getattr(self, method_name)
|
|
1498
|
+
self._disk_clipping_registry = method_alias(u_x, u_y)
|
|
1499
|
+
disk_clipping_registry = self._disk_clipping_registry.cpu()
|
|
1500
|
+
disk_clipping_registry = disk_clipping_registry.detach().clone()
|
|
1501
|
+
disk_clipping_registry = tuple(disk_clipping_registry.tolist())
|
|
1502
|
+
|
|
1503
|
+
if self._disk_absence_registry is None:
|
|
1504
|
+
method_name = ("_calc_disk_absence_registry"
|
|
1505
|
+
"_and_cache_select_intermediates")
|
|
1506
|
+
method_alias = getattr(self, method_name)
|
|
1507
|
+
self._disk_absence_registry = method_alias(u_x, u_y)
|
|
1508
|
+
disk_absence_registry = self._disk_absence_registry.cpu()
|
|
1509
|
+
disk_absence_registry = disk_absence_registry.detach().clone()
|
|
1510
|
+
disk_absence_registry = tuple(disk_absence_registry.tolist())
|
|
1511
|
+
|
|
1512
|
+
fakecbed_metadata = {"num_disks": \
|
|
1513
|
+
self._num_disks,
|
|
1514
|
+
"disk_clipping_registry": \
|
|
1515
|
+
disk_clipping_registry,
|
|
1516
|
+
"disk_absence_registry": \
|
|
1517
|
+
disk_absence_registry,
|
|
1518
|
+
"pre_serialized_core_attrs": \
|
|
1519
|
+
pre_serialized_core_attrs,
|
|
1520
|
+
"cbed_pattern_image_has_been_overridden": \
|
|
1521
|
+
self._image_has_been_overridden}
|
|
1522
|
+
|
|
1523
|
+
signal_metadata = {"General": {"title": title},
|
|
1524
|
+
"Signal": {"pixel value units": "dimensionless"},
|
|
1525
|
+
"FakeCBED": fakecbed_metadata}
|
|
1526
|
+
|
|
1527
|
+
return signal_metadata
|
|
1528
|
+
|
|
1529
|
+
|
|
1530
|
+
|
|
1531
|
+
def _calc_disk_clipping_registry_and_cache_select_intermediates(self,
|
|
1532
|
+
u_x,
|
|
1533
|
+
u_y):
|
|
1534
|
+
undistorted_disks = self._undistorted_disks
|
|
1535
|
+
num_disks = len(undistorted_disks)
|
|
1536
|
+
|
|
1537
|
+
if num_disks > 0:
|
|
1538
|
+
if self._illumination_support is None:
|
|
1539
|
+
method_name = "_calc_illumination_support"
|
|
1540
|
+
method_alias = getattr(self, method_name)
|
|
1541
|
+
self._illumination_support = method_alias(u_x, u_y)
|
|
1542
|
+
illumination_support = self._illumination_support
|
|
1543
|
+
|
|
1544
|
+
if self._disk_supports is None:
|
|
1545
|
+
method_name = ("_calc_disk_supports"
|
|
1546
|
+
"_and_cache_select_intermediates")
|
|
1547
|
+
method_alias = getattr(self, method_name)
|
|
1548
|
+
self._disk_supports = method_alias(u_x, u_y)
|
|
1549
|
+
disk_supports = self._disk_supports
|
|
1550
|
+
|
|
1551
|
+
clip_support = self._calc_clip_support(illumination_support)
|
|
1552
|
+
|
|
1553
|
+
disk_clipping_map = disk_supports*clip_support[None, :, :]
|
|
1554
|
+
|
|
1555
|
+
disk_clipping_registry = ((disk_clipping_map.sum(dim=(1, 2)) != 0)
|
|
1556
|
+
+ (disk_supports.sum(dim=(1, 2)) == 0))
|
|
1557
|
+
else:
|
|
1558
|
+
disk_clipping_registry = torch.zeros((num_disks,),
|
|
1559
|
+
device=u_x.device,
|
|
1560
|
+
dtype=torch.bool)
|
|
1561
|
+
|
|
1562
|
+
return disk_clipping_registry
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
def _calc_disk_supports_and_cache_select_intermediates(self, u_x, u_y):
|
|
1567
|
+
undistorted_disks = self._undistorted_disks
|
|
1568
|
+
num_disks = len(undistorted_disks)
|
|
1569
|
+
|
|
1570
|
+
if num_disks > 0:
|
|
1571
|
+
L, R, B, T = self._mask_frame
|
|
1572
|
+
N_I_x = self._num_pixels_across_pattern
|
|
1573
|
+
N_I_y = N_I_x
|
|
1574
|
+
|
|
1575
|
+
if self._illumination_support is None:
|
|
1576
|
+
method_name = "_calc_illumination_support"
|
|
1577
|
+
method_alias = getattr(self, method_name)
|
|
1578
|
+
self._illumination_support = method_alias(u_x, u_y)
|
|
1579
|
+
illumination_support = self._illumination_support
|
|
1580
|
+
|
|
1581
|
+
pooler_kernel_size = self._calc_pooler_kernel_size()
|
|
1582
|
+
pooler = torch.nn.MaxPool2d(kernel_size=pooler_kernel_size)
|
|
1583
|
+
|
|
1584
|
+
disk_supports_shape = (num_disks,)+u_x.shape
|
|
1585
|
+
|
|
1586
|
+
disk_supports = torch.zeros(disk_supports_shape,
|
|
1587
|
+
device=u_x.device)
|
|
1588
|
+
for disk_idx, undistorted_disk in enumerate(undistorted_disks):
|
|
1589
|
+
method_name = "_eval_without_intra_support_shapes"
|
|
1590
|
+
method_alias = getattr(undistorted_disk, method_name)
|
|
1591
|
+
disk_supports[disk_idx] = (method_alias(u_x, u_y) != 0)
|
|
1592
|
+
disk_supports = torch.unsqueeze(disk_supports, dim=0)
|
|
1593
|
+
disk_supports = disk_supports.to(dtype=u_x.dtype)
|
|
1594
|
+
disk_supports = pooler(disk_supports)[0]
|
|
1595
|
+
disk_supports = disk_supports.to(dtype=torch.bool)
|
|
1596
|
+
disk_supports[:, :, :] *= illumination_support[None, :, :]
|
|
1597
|
+
disk_supports[:, :T, :] = 0
|
|
1598
|
+
disk_supports[:, max(N_I_y-B, 0):, :] = 0
|
|
1599
|
+
disk_supports[:, :, :L] = 0
|
|
1600
|
+
disk_supports[:, :, max(N_I_x-R, 0):] = 0
|
|
1601
|
+
else:
|
|
1602
|
+
num_pixels_across_pattern = self._num_pixels_across_pattern
|
|
1603
|
+
|
|
1604
|
+
disk_supports_shape = (num_disks,
|
|
1605
|
+
num_pixels_across_pattern,
|
|
1606
|
+
num_pixels_across_pattern)
|
|
1607
|
+
|
|
1608
|
+
disk_supports = torch.zeros(disk_supports_shape,
|
|
1609
|
+
device=u_x.device,
|
|
1610
|
+
dtype=torch.bool)
|
|
1611
|
+
|
|
1612
|
+
return disk_supports
|
|
1613
|
+
|
|
1614
|
+
|
|
1615
|
+
|
|
1616
|
+
def _calc_clip_support(self, illumination_support):
|
|
1617
|
+
L, R, B, T = self._mask_frame
|
|
1618
|
+
N_I_x = self._num_pixels_across_pattern
|
|
1619
|
+
N_I_y = N_I_x
|
|
1620
|
+
|
|
1621
|
+
clip_support = ~illumination_support
|
|
1622
|
+
for _ in range(2):
|
|
1623
|
+
clip_support = torch.unsqueeze(clip_support, dim=0)
|
|
1624
|
+
clip_support = clip_support.to(dtype=torch.float)
|
|
1625
|
+
|
|
1626
|
+
conv_weights = torch.ones((1, 1, 5, 5),
|
|
1627
|
+
device=illumination_support.device)
|
|
1628
|
+
|
|
1629
|
+
kwargs = {"input": clip_support,
|
|
1630
|
+
"weight": conv_weights,
|
|
1631
|
+
"padding": "same"}
|
|
1632
|
+
clip_support = (torch.nn.functional.conv2d(**kwargs) != 0)
|
|
1633
|
+
|
|
1634
|
+
clip_support = clip_support.to(dtype=torch.bool)
|
|
1635
|
+
clip_support[0, 0, :T+2, :] = True
|
|
1636
|
+
clip_support[0, 0, max(N_I_y-B-2, 0):, :] = True
|
|
1637
|
+
clip_support[0, 0, :, :L+2] = True
|
|
1638
|
+
clip_support[0, 0, :, max(N_I_x-R-2, 0):] = True
|
|
1639
|
+
clip_support = clip_support[0, 0]
|
|
1640
|
+
|
|
1641
|
+
return clip_support
|
|
1642
|
+
|
|
1643
|
+
|
|
1644
|
+
|
|
1645
|
+
def _calc_disk_absence_registry_and_cache_select_intermediates(self,
|
|
1646
|
+
u_x,
|
|
1647
|
+
u_y):
|
|
1648
|
+
undistorted_disks = self._undistorted_disks
|
|
1649
|
+
num_disks = len(undistorted_disks)
|
|
1650
|
+
|
|
1651
|
+
if num_disks > 0:
|
|
1652
|
+
if self._disk_supports is None:
|
|
1653
|
+
method_name = ("_calc_disk_supports"
|
|
1654
|
+
"_and_cache_select_intermediates")
|
|
1655
|
+
method_alias = getattr(self, method_name)
|
|
1656
|
+
self._disk_supports = method_alias(u_x, u_y)
|
|
1657
|
+
disk_supports = self._disk_supports
|
|
1658
|
+
|
|
1659
|
+
disk_absence_registry = (disk_supports.sum(dim=(1, 2)) == 0)
|
|
1660
|
+
else:
|
|
1661
|
+
disk_absence_registry = torch.zeros((num_disks,),
|
|
1662
|
+
device=u_x.device,
|
|
1663
|
+
dtype=torch.bool)
|
|
1664
|
+
|
|
1665
|
+
return disk_absence_registry
|
|
1666
|
+
|
|
1667
|
+
|
|
1668
|
+
|
|
1669
|
+
def _calc_image_and_cache_select_intermediates(self, u_x, u_y):
|
|
1670
|
+
method_name = ("_calc_maskless_and_noiseless_image"
|
|
1671
|
+
"_and_cache_select_intermediates")
|
|
1672
|
+
method_alias = getattr(self, method_name)
|
|
1673
|
+
maskless_and_noiseless_image = method_alias(u_x, u_y)
|
|
1674
|
+
|
|
1675
|
+
if self._illumination_support is None:
|
|
1676
|
+
method_name = "_calc_illumination_support"
|
|
1677
|
+
method_alias = getattr(self, method_name)
|
|
1678
|
+
self._illumination_support = method_alias(u_x, u_y)
|
|
1679
|
+
illumination_support = self._illumination_support
|
|
1680
|
+
|
|
1681
|
+
noiseless_image = maskless_and_noiseless_image*illumination_support
|
|
1682
|
+
|
|
1683
|
+
apply_shot_noise = self._apply_shot_noise
|
|
1684
|
+
|
|
1685
|
+
image = (torch.poisson(noiseless_image)
|
|
1686
|
+
if (apply_shot_noise == True)
|
|
1687
|
+
else noiseless_image)
|
|
1688
|
+
|
|
1689
|
+
coords_of_cold_pixels = self._cold_pixels
|
|
1690
|
+
L, R, B, T = self._mask_frame
|
|
1691
|
+
N_I_x = self._num_pixels_across_pattern
|
|
1692
|
+
N_I_y = N_I_x
|
|
1693
|
+
|
|
1694
|
+
image[:T, :] = 0
|
|
1695
|
+
image[max(N_I_y-B, 0):, :] = 0
|
|
1696
|
+
image[:, :L] = 0
|
|
1697
|
+
image[:, max(N_I_x-R, 0):] = 0
|
|
1698
|
+
for coords_of_cold_pixel in coords_of_cold_pixels:
|
|
1699
|
+
image[coords_of_cold_pixel] = 0
|
|
1700
|
+
|
|
1701
|
+
image = self._apply_detector_partition_inpainting(input_image=image)
|
|
1702
|
+
|
|
1703
|
+
image = self._normalize_matrix(input_matrix=image)
|
|
1704
|
+
|
|
1705
|
+
image = torch.clip(image, min=0)
|
|
1706
|
+
|
|
1707
|
+
return image
|
|
1708
|
+
|
|
1709
|
+
|
|
1710
|
+
|
|
1711
|
+
def _calc_maskless_and_noiseless_image_and_cache_select_intermediates(self,
|
|
1712
|
+
u_x,
|
|
1713
|
+
u_y):
|
|
1714
|
+
jacobian_weights = self._calc_jacobian_weights(u_x, u_y)
|
|
1715
|
+
|
|
1716
|
+
bg = self._calc_bg(u_x, u_y, jacobian_weights)
|
|
1717
|
+
|
|
1718
|
+
if self._disk_supports is None:
|
|
1719
|
+
method_name = ("_calc_disk_supports"
|
|
1720
|
+
"_and_cache_select_intermediates")
|
|
1721
|
+
method_alias = getattr(self, method_name)
|
|
1722
|
+
self._disk_supports = method_alias(u_x, u_y)
|
|
1723
|
+
disk_supports = self._disk_supports
|
|
1724
|
+
|
|
1725
|
+
intra_disk_shapes = self._calc_intra_disk_shapes(u_x,
|
|
1726
|
+
u_y,
|
|
1727
|
+
jacobian_weights)
|
|
1728
|
+
|
|
1729
|
+
gaussian_filter_std_dev = self._gaussian_filter_std_dev
|
|
1730
|
+
|
|
1731
|
+
maskless_and_noiseless_image = (bg
|
|
1732
|
+
+ (disk_supports
|
|
1733
|
+
* intra_disk_shapes).sum(dim=0))
|
|
1734
|
+
|
|
1735
|
+
kwargs = {"input_matrix": maskless_and_noiseless_image,
|
|
1736
|
+
"truncate": 4}
|
|
1737
|
+
maskless_and_noiseless_image = self._apply_2d_guassian_filter(**kwargs)
|
|
1738
|
+
|
|
1739
|
+
maskless_and_noiseless_image = torch.clip(maskless_and_noiseless_image,
|
|
1740
|
+
min=0)
|
|
1741
|
+
|
|
1742
|
+
return maskless_and_noiseless_image
|
|
1743
|
+
|
|
1744
|
+
|
|
1745
|
+
|
|
1746
|
+
def _calc_jacobian_weights(self, u_x, u_y):
|
|
1747
|
+
distortion_model = self._distortion_model
|
|
1748
|
+
|
|
1749
|
+
sampling_grid = distortion_model.get_sampling_grid(deep_copy=False)
|
|
1750
|
+
|
|
1751
|
+
spacing = (sampling_grid[1][:, 0], sampling_grid[0][0, :])
|
|
1752
|
+
|
|
1753
|
+
kwargs = {"input": u_x,
|
|
1754
|
+
"spacing": spacing,
|
|
1755
|
+
"dim": None,
|
|
1756
|
+
"edge_order": 2}
|
|
1757
|
+
d_u_x_over_d_q_y, d_u_x_over_d_q_x = torch.gradient(**kwargs)
|
|
1758
|
+
|
|
1759
|
+
kwargs["input"] = u_y
|
|
1760
|
+
d_u_y_over_d_q_y, d_u_y_over_d_q_x = torch.gradient(**kwargs)
|
|
1761
|
+
|
|
1762
|
+
jacobian_weights = torch.abs(d_u_x_over_d_q_x*d_u_y_over_d_q_y
|
|
1763
|
+
- d_u_x_over_d_q_y*d_u_y_over_d_q_x)
|
|
1764
|
+
|
|
1765
|
+
return jacobian_weights
|
|
1766
|
+
|
|
1767
|
+
|
|
1768
|
+
|
|
1769
|
+
def _calc_bg(self, u_x, u_y, jacobian_weights):
|
|
1770
|
+
undistorted_tds_model = self._undistorted_tds_model
|
|
1771
|
+
undistorted_misc_shapes = self._undistorted_misc_shapes
|
|
1772
|
+
|
|
1773
|
+
pooler_kernel_size = self._calc_pooler_kernel_size()
|
|
1774
|
+
pooler = torch.nn.AvgPool2d(kernel_size=pooler_kernel_size)
|
|
1775
|
+
|
|
1776
|
+
bg = undistorted_tds_model._eval(u_x, u_y)
|
|
1777
|
+
for undistorted_misc_shape in undistorted_misc_shapes:
|
|
1778
|
+
bg[:, :] += undistorted_misc_shape._eval(u_x, u_y)[:, :]
|
|
1779
|
+
bg[:, :] *= jacobian_weights[:, :]
|
|
1780
|
+
bg = torch.unsqueeze(bg, dim=0)
|
|
1781
|
+
bg = torch.unsqueeze(bg, dim=0)
|
|
1782
|
+
bg = pooler(bg)[0, 0]
|
|
1783
|
+
|
|
1784
|
+
return bg
|
|
1785
|
+
|
|
1786
|
+
|
|
1787
|
+
|
|
1788
|
+
def _calc_intra_disk_shapes(self, u_x, u_y, jacobian_weights):
|
|
1789
|
+
undistorted_disks = self._undistorted_disks
|
|
1790
|
+
num_disks = len(undistorted_disks)
|
|
1791
|
+
|
|
1792
|
+
if num_disks > 0:
|
|
1793
|
+
pooler_kernel_size = self._calc_pooler_kernel_size()
|
|
1794
|
+
pooler = torch.nn.AvgPool2d(kernel_size=pooler_kernel_size)
|
|
1795
|
+
|
|
1796
|
+
intra_disk_shapes_shape = (num_disks,)+u_x.shape
|
|
1797
|
+
|
|
1798
|
+
intra_disk_shapes = torch.zeros(intra_disk_shapes_shape,
|
|
1799
|
+
device=u_x.device)
|
|
1800
|
+
for disk_idx, undistorted_disk in enumerate(undistorted_disks):
|
|
1801
|
+
method_alias = undistorted_disk._eval_without_support
|
|
1802
|
+
intra_disk_shapes[disk_idx] = method_alias(u_x, u_y)
|
|
1803
|
+
intra_disk_shapes[:, :, :] *= jacobian_weights[None, :, :]
|
|
1804
|
+
intra_disk_shapes = torch.unsqueeze(intra_disk_shapes, dim=0)
|
|
1805
|
+
intra_disk_shapes = pooler(intra_disk_shapes)[0]
|
|
1806
|
+
else:
|
|
1807
|
+
num_pixels_across_pattern = self._num_pixels_across_pattern
|
|
1808
|
+
|
|
1809
|
+
intra_disk_shapes_shape = (num_disks,
|
|
1810
|
+
num_pixels_across_pattern,
|
|
1811
|
+
num_pixels_across_pattern)
|
|
1812
|
+
|
|
1813
|
+
intra_disk_shapes = torch.zeros(intra_disk_shapes_shape,
|
|
1814
|
+
device=u_x.device)
|
|
1815
|
+
|
|
1816
|
+
return intra_disk_shapes
|
|
1817
|
+
|
|
1818
|
+
|
|
1819
|
+
|
|
1820
|
+
def _apply_2d_guassian_filter(self, input_matrix, truncate):
|
|
1821
|
+
intermediate_tensor = input_matrix
|
|
1822
|
+
for axis_idx in range(2):
|
|
1823
|
+
kwargs = {"input_matrix": intermediate_tensor,
|
|
1824
|
+
"truncate": truncate,
|
|
1825
|
+
"axis_idx": axis_idx}
|
|
1826
|
+
intermediate_tensor = self._apply_1d_guassian_filter(**kwargs)
|
|
1827
|
+
output_matrix = intermediate_tensor
|
|
1828
|
+
|
|
1829
|
+
return output_matrix
|
|
1830
|
+
|
|
1831
|
+
|
|
1832
|
+
|
|
1833
|
+
def _apply_1d_guassian_filter(self, input_matrix, truncate, axis_idx):
|
|
1834
|
+
intermediate_tensor = torch.unsqueeze(input_matrix, dim=0)
|
|
1835
|
+
intermediate_tensor = torch.unsqueeze(intermediate_tensor, dim=0)
|
|
1836
|
+
|
|
1837
|
+
sigma = self._gaussian_filter_std_dev
|
|
1838
|
+
|
|
1839
|
+
if sigma > 0:
|
|
1840
|
+
radius = int(truncate*sigma + 0.5)
|
|
1841
|
+
coords = torch.arange(-radius, radius+1, device=input_matrix.device)
|
|
1842
|
+
|
|
1843
|
+
weights = torch.exp(-(coords/sigma)*(coords/sigma)/2)
|
|
1844
|
+
weights /= torch.sum(weights)
|
|
1845
|
+
weights = torch.unsqueeze(weights, dim=axis_idx)
|
|
1846
|
+
weights = torch.unsqueeze(weights, dim=0)
|
|
1847
|
+
weights = torch.unsqueeze(weights, dim=0)
|
|
1848
|
+
|
|
1849
|
+
kwargs = {"input": intermediate_tensor,
|
|
1850
|
+
"weight": weights,
|
|
1851
|
+
"padding": "same"}
|
|
1852
|
+
output_matrix = torch.nn.functional.conv2d(**kwargs)[0, 0]
|
|
1853
|
+
else:
|
|
1854
|
+
output_matrix = input_matrix
|
|
1855
|
+
|
|
1856
|
+
return output_matrix
|
|
1857
|
+
|
|
1858
|
+
|
|
1859
|
+
|
|
1860
|
+
def _apply_detector_partition_inpainting(self, input_image):
|
|
1861
|
+
N_DPW = self._detector_partition_width_in_pixels
|
|
1862
|
+
|
|
1863
|
+
k_I_1 = ((input_image.shape[1]-1)//2) - (N_DPW//2)
|
|
1864
|
+
k_I_2 = k_I_1 + N_DPW - 1
|
|
1865
|
+
|
|
1866
|
+
inpainting_mask = np.zeros(input_image.shape, dtype=bool)
|
|
1867
|
+
inpainting_mask[k_I_1:k_I_2+1, :] = True
|
|
1868
|
+
inpainting_mask[:, k_I_1:k_I_2+1] = True
|
|
1869
|
+
|
|
1870
|
+
kwargs = {"image": input_image.numpy(force=True),
|
|
1871
|
+
"mask": inpainting_mask}
|
|
1872
|
+
output_image = skimage.restoration.inpaint_biharmonic(**kwargs)
|
|
1873
|
+
output_image = torch.from_numpy(output_image)
|
|
1874
|
+
output_image = output_image.to(device=input_image.device,
|
|
1875
|
+
dtype=input_image.dtype)
|
|
1876
|
+
|
|
1877
|
+
return output_image
|
|
1878
|
+
|
|
1879
|
+
|
|
1880
|
+
|
|
1881
|
+
def _calc_disk_overlap_map_and_cache_select_intermediates(self, u_x, u_y):
|
|
1882
|
+
undistorted_disks = self._undistorted_disks
|
|
1883
|
+
num_disks = len(undistorted_disks)
|
|
1884
|
+
|
|
1885
|
+
if num_disks > 0:
|
|
1886
|
+
if self._illumination_support is None:
|
|
1887
|
+
method_name = "_calc_illumination_support"
|
|
1888
|
+
method_alias = getattr(self, method_name)
|
|
1889
|
+
self._illumination_support = method_alias(u_x, u_y)
|
|
1890
|
+
illumination_support = self._illumination_support
|
|
1891
|
+
|
|
1892
|
+
if self._disk_supports is None:
|
|
1893
|
+
method_name = ("_calc_disk_supports"
|
|
1894
|
+
"_and_cache_select_intermediates")
|
|
1895
|
+
method_alias = getattr(self, method_name)
|
|
1896
|
+
self._disk_supports = method_alias(u_x, u_y)
|
|
1897
|
+
disk_supports = self._disk_supports
|
|
1898
|
+
|
|
1899
|
+
L, R, B, T = self._mask_frame
|
|
1900
|
+
N_I_x = self._num_pixels_across_pattern
|
|
1901
|
+
N_I_y = N_I_x
|
|
1902
|
+
|
|
1903
|
+
disk_overlap_map = (illumination_support
|
|
1904
|
+
* torch.sum(disk_supports, dim=0))
|
|
1905
|
+
disk_overlap_map[:T, :] = 0
|
|
1906
|
+
disk_overlap_map[max(N_I_y-B, 0):, :] = 0
|
|
1907
|
+
disk_overlap_map[:, :L] = 0
|
|
1908
|
+
disk_overlap_map[:, max(N_I_x-R, 0):] = 0
|
|
1909
|
+
else:
|
|
1910
|
+
num_pixels_across_pattern = self._num_pixels_across_pattern
|
|
1911
|
+
|
|
1912
|
+
disk_overlap_map_shape = 2*(num_pixels_across_pattern,)
|
|
1913
|
+
|
|
1914
|
+
disk_overlap_map = torch.zeros(disk_overlap_map_shape,
|
|
1915
|
+
device=u_x.device,
|
|
1916
|
+
dtype=torch.int)
|
|
1917
|
+
|
|
1918
|
+
return disk_overlap_map
|
|
1919
|
+
|
|
1920
|
+
|
|
1921
|
+
|
|
1922
|
+
def _update_signal_axes(self, signal):
|
|
1923
|
+
num_pixels_across_pattern = signal.axes_manager.signal_shape[0]
|
|
1924
|
+
|
|
1925
|
+
sizes = (signal.axes_manager.navigation_shape
|
|
1926
|
+
+ signal.axes_manager.signal_shape)
|
|
1927
|
+
scales = (1,
|
|
1928
|
+
1/num_pixels_across_pattern,
|
|
1929
|
+
-1/num_pixels_across_pattern)
|
|
1930
|
+
offsets = (0,
|
|
1931
|
+
0.5/num_pixels_across_pattern,
|
|
1932
|
+
1-(1-0.5)/num_pixels_across_pattern)
|
|
1933
|
+
axes_labels = (r"fake CBED pattern attribute",
|
|
1934
|
+
r"fractional horizontal coordinate",
|
|
1935
|
+
r"fractional vertical coordinate")
|
|
1936
|
+
units = ("dimensionless",)*3
|
|
1937
|
+
|
|
1938
|
+
num_axes = len(units)
|
|
1939
|
+
|
|
1940
|
+
for axis_idx in range(num_axes):
|
|
1941
|
+
axis = hyperspy.axes.UniformDataAxis(size=sizes[axis_idx],
|
|
1942
|
+
scale=scales[axis_idx],
|
|
1943
|
+
offset=offsets[axis_idx],
|
|
1944
|
+
units=units[axis_idx],
|
|
1945
|
+
name=axes_labels[axis_idx])
|
|
1946
|
+
signal.axes_manager[axis_idx].update_from(axis)
|
|
1947
|
+
signal.axes_manager[axis_idx].name = axis.name
|
|
1948
|
+
|
|
1949
|
+
return None
|
|
1950
|
+
|
|
1951
|
+
|
|
1952
|
+
|
|
1953
|
+
@property
|
|
1954
|
+
def signal(self):
|
|
1955
|
+
r"""`hyperspy._signals.signal2d.Signal2D`: The hyperspy signal
|
|
1956
|
+
representation of the fake CBED pattern.
|
|
1957
|
+
|
|
1958
|
+
See the summary documentation of the class
|
|
1959
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context.
|
|
1960
|
+
|
|
1961
|
+
Let ``image``, ``illumination_support``, ``disk_overlap_map``,
|
|
1962
|
+
``disk_supports``, ``disk_clipping_registry``,
|
|
1963
|
+
``image_has_been_overridden``, ``num_disks``, ``disk_absence_registry``,
|
|
1964
|
+
and ``core_attrs`` denote the attributes
|
|
1965
|
+
:attr:`fakecbed.discretized.CBEDPattern.image`,
|
|
1966
|
+
:attr:`fakecbed.discretized.CBEDPattern.illumination_support`,
|
|
1967
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_overlap_map`,
|
|
1968
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_supports`,
|
|
1969
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_clipping_registry`,
|
|
1970
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_absence_registry`,
|
|
1971
|
+
:attr:`fakecbed.discretized.CBEDPattern.image_has_been_overridden`,
|
|
1972
|
+
:attr:`fakecbed.discretized.CBEDPattern.num_disks`, and
|
|
1973
|
+
:attr:`~fancytypes.Checkable.core_attrs`. Furthermore, let
|
|
1974
|
+
``pre_serialize`` denote the method
|
|
1975
|
+
:meth:`~fancytypes.PreSerializable.pre_serialize`.
|
|
1976
|
+
|
|
1977
|
+
The signal data, ``signal.data``, is a NumPy array having a shape equal
|
|
1978
|
+
to ``(num_disks+3,)+2*(core_attrs["num_pixels_across_pattern"],)``. The
|
|
1979
|
+
elements of ``signal.data`` are set effectively by:
|
|
1980
|
+
|
|
1981
|
+
.. code-block:: python
|
|
1982
|
+
|
|
1983
|
+
L, R, B, T = core_attrs["mask_frame"]
|
|
1984
|
+
N_I_x = core_attrs["num_pixels_across_pattern"]
|
|
1985
|
+
N_I_y = N_I_x
|
|
1986
|
+
|
|
1987
|
+
signal.data[0] = image.numpy(force=True)
|
|
1988
|
+
signal.data[1] = illumination_support.numpy(force=True)
|
|
1989
|
+
signal.data[1, :T, :] = 0
|
|
1990
|
+
signal.data[1, max(N_I_y-B, 0):, :] = 0
|
|
1991
|
+
signal.data[1, :, :L] = 0
|
|
1992
|
+
signal.data[1, :, max(N_I_x-R, 0):] = 0
|
|
1993
|
+
signal.data[2] = disk_overlap_map.numpy(force=True)
|
|
1994
|
+
signal.data[3:] = disk_supports.numpy(force=True)
|
|
1995
|
+
|
|
1996
|
+
The signal metadata, ``signal.metadata``, stores serializable forms of
|
|
1997
|
+
several instance attributes, in addition to other items of
|
|
1998
|
+
metadata. ``signal.metadata.as_dictionary()`` yields a dictionary
|
|
1999
|
+
``signal_metadata`` that is calculated effectively by:
|
|
2000
|
+
|
|
2001
|
+
.. code-block:: python
|
|
2002
|
+
|
|
2003
|
+
distortion_model = core_attrs["distortion_model"]
|
|
2004
|
+
|
|
2005
|
+
title = ("Fake Undistorted CBED Intensity Pattern"
|
|
2006
|
+
if distortion_model.is_trivial
|
|
2007
|
+
else "Fake Distorted CBED Intensity Pattern")
|
|
2008
|
+
|
|
2009
|
+
pre_serialized_core_attrs = pre_serialize()
|
|
2010
|
+
|
|
2011
|
+
fakecbed_metadata = {"num_disks": \
|
|
2012
|
+
num_disks,
|
|
2013
|
+
"disk_clipping_registry": \
|
|
2014
|
+
disk_clipping_registry.numpy(force=True),
|
|
2015
|
+
"disk_absence_registry": \
|
|
2016
|
+
disk_absence_registry.numpy(force=True),
|
|
2017
|
+
"pre_serialized_core_attrs": \
|
|
2018
|
+
pre_serialized_core_attrs,
|
|
2019
|
+
"cbed_pattern_image_has_been_overridden": \
|
|
2020
|
+
image_has_been_overridden}
|
|
2021
|
+
|
|
2022
|
+
signal_metadata = {"General": {"title": title},
|
|
2023
|
+
"Signal": {"pixel value units": "dimensionless"},
|
|
2024
|
+
"FakeCBED": fakecbed_metadata}
|
|
2025
|
+
|
|
2026
|
+
Note that ``signal`` should be considered **read-only**.
|
|
2027
|
+
|
|
2028
|
+
"""
|
|
2029
|
+
result = self.get_signal(deep_copy=True)
|
|
2030
|
+
|
|
2031
|
+
return result
|
|
2032
|
+
|
|
2033
|
+
|
|
2034
|
+
|
|
2035
|
+
def get_image(self, deep_copy=_default_deep_copy):
|
|
2036
|
+
r"""Return the image of the target fake CBED pattern,
|
|
2037
|
+
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
2038
|
+
|
|
2039
|
+
Parameters
|
|
2040
|
+
----------
|
|
2041
|
+
deep_copy : `bool`, optional
|
|
2042
|
+
Let ``image`` denote the attribute
|
|
2043
|
+
:attr:`fakecbed.discretized.CBEDPattern.image`.
|
|
2044
|
+
|
|
2045
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of ``image``
|
|
2046
|
+
is returned. Otherwise, a reference to ``image`` is returned.
|
|
2047
|
+
|
|
2048
|
+
Returns
|
|
2049
|
+
-------
|
|
2050
|
+
image : `torch.Tensor` (`float`, ndim=2)
|
|
2051
|
+
The attribute :attr:`fakecbed.discretized.CBEDPattern.image`.
|
|
2052
|
+
|
|
2053
|
+
"""
|
|
2054
|
+
params = {"deep_copy": deep_copy}
|
|
2055
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
2056
|
+
|
|
2057
|
+
if self._image is None:
|
|
2058
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
2059
|
+
method_name = "_calc_image_and_cache_select_intermediates"
|
|
2060
|
+
method_alias = getattr(self, method_name)
|
|
2061
|
+
self._image = method_alias(u_x, u_y)
|
|
2062
|
+
|
|
2063
|
+
image = (self._image.detach().clone()
|
|
2064
|
+
if (deep_copy == True)
|
|
2065
|
+
else self._image)
|
|
2066
|
+
|
|
2067
|
+
return image
|
|
2068
|
+
|
|
2069
|
+
|
|
2070
|
+
|
|
2071
|
+
@property
|
|
2072
|
+
def image(self):
|
|
2073
|
+
r"""`torch.Tensor`: The image of the target fake CBED pattern,
|
|
2074
|
+
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
2075
|
+
|
|
2076
|
+
See the summary documentation of the class
|
|
2077
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2078
|
+
particular a description of the calculation of
|
|
2079
|
+
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
2080
|
+
|
|
2081
|
+
Let ``core_attrs`` denote the attribute
|
|
2082
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
2083
|
+
|
|
2084
|
+
``image`` is a PyTorch tensor having a shape equal to
|
|
2085
|
+
``2*(core_attrs["num_pixels_across_pattern"],)``.
|
|
2086
|
+
|
|
2087
|
+
For every pair of nonnegative integers ``(n, m)`` that does not raise an
|
|
2088
|
+
``IndexError`` exception upon calling ``image[n, m]``, ``image[n, m]``
|
|
2089
|
+
is equal to :math:`\mathcal{I}_{\text{CBED};⌑;n,m}`, with the integers
|
|
2090
|
+
:math:`n` and :math:`m` being equal to the values of ``n`` and ``m``
|
|
2091
|
+
respectively.
|
|
2092
|
+
|
|
2093
|
+
Note that ``image`` should be considered **read-only**.
|
|
2094
|
+
|
|
2095
|
+
"""
|
|
2096
|
+
result = self.get_image(deep_copy=True)
|
|
2097
|
+
|
|
2098
|
+
return result
|
|
2099
|
+
|
|
2100
|
+
|
|
2101
|
+
|
|
2102
|
+
def get_illumination_support(self, deep_copy=_default_deep_copy):
|
|
2103
|
+
r"""Return the image of the illumination support,
|
|
2104
|
+
:math:`\mathcal{I}_{\text{OI};⌑;n,m}`.
|
|
2105
|
+
|
|
2106
|
+
Parameters
|
|
2107
|
+
----------
|
|
2108
|
+
deep_copy : `bool`, optional
|
|
2109
|
+
Let ``illumination_support`` denote the attribute
|
|
2110
|
+
:attr:`fakecbed.discretized.CBEDPattern.illumination_support`.
|
|
2111
|
+
|
|
2112
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
2113
|
+
``illumination_support`` is returned. Otherwise, a reference to
|
|
2114
|
+
``illumination_support`` is returned.
|
|
2115
|
+
|
|
2116
|
+
Returns
|
|
2117
|
+
-------
|
|
2118
|
+
illumination_support : `torch.Tensor` (`bool`, ndim=2)
|
|
2119
|
+
The attribute
|
|
2120
|
+
:attr:`fakecbed.discretized.CBEDPattern.illumination_support`.
|
|
2121
|
+
|
|
2122
|
+
"""
|
|
2123
|
+
params = {"deep_copy": deep_copy}
|
|
2124
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
2125
|
+
|
|
2126
|
+
if self._illumination_support is None:
|
|
2127
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
2128
|
+
method_name = "_calc_illumination_support"
|
|
2129
|
+
method_alias = getattr(self, method_name)
|
|
2130
|
+
self._illumination_support = method_alias(u_x, u_y)
|
|
2131
|
+
|
|
2132
|
+
illumination_support = (self._illumination_support.detach().clone()
|
|
2133
|
+
if (deep_copy == True)
|
|
2134
|
+
else self._illumination_support)
|
|
2135
|
+
|
|
2136
|
+
return illumination_support
|
|
2137
|
+
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
@property
|
|
2141
|
+
def illumination_support(self):
|
|
2142
|
+
r"""`torch.Tensor`: The image of the illumination support,
|
|
2143
|
+
:math:`\mathcal{I}_{\text{OI};⌑;n,m}`.
|
|
2144
|
+
|
|
2145
|
+
See the summary documentation of the class
|
|
2146
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2147
|
+
particular a description of the calculation of
|
|
2148
|
+
:math:`\mathcal{I}_{\text{OI};⌑;n,m}`.
|
|
2149
|
+
|
|
2150
|
+
Note that :math:`\mathcal{I}_{\text{CBED};⌑;n,m}` is the image of the
|
|
2151
|
+
target fake CBED pattern, which is stored in the attribute
|
|
2152
|
+
:attr:`fakecbed.discretized.CBEDPattern.image`
|
|
2153
|
+
|
|
2154
|
+
Let ``core_attrs`` denote the attribute
|
|
2155
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
2156
|
+
|
|
2157
|
+
``illumination_support`` is a PyTorch tensor having a shape equal to
|
|
2158
|
+
``2*(core_attrs["num_pixels_across_pattern"],)``.
|
|
2159
|
+
|
|
2160
|
+
For every pair of nonnegative integers ``(n, m)`` that does not raise an
|
|
2161
|
+
``IndexError`` exception upon calling ``illumination_support[n, m]``,
|
|
2162
|
+
``illumination_support[n, m]`` is equal to
|
|
2163
|
+
:math:`\mathcal{I}_{\text{OI};⌑;n,m}`, with the integers :math:`n` and
|
|
2164
|
+
:math:`m` being equal to the values of ``n`` and ``m``
|
|
2165
|
+
respectively. Furthermore, for each such pair of integers ``(n, m)``, if
|
|
2166
|
+
``illumination_support[n, m]`` equals zero, then the corresponding pixel
|
|
2167
|
+
of the image of the target fake CBED pattern is also zero.
|
|
2168
|
+
|
|
2169
|
+
Note that ``illumination_support`` should be considered **read-only**.
|
|
2170
|
+
|
|
2171
|
+
"""
|
|
2172
|
+
result = self.get_illumination_support(deep_copy=True)
|
|
2173
|
+
|
|
2174
|
+
return result
|
|
2175
|
+
|
|
2176
|
+
|
|
2177
|
+
|
|
2178
|
+
def get_disk_supports(self, deep_copy=_default_deep_copy):
|
|
2179
|
+
r"""Return the image stack of the disk supports,
|
|
2180
|
+
:math:`\left\{\mathcal{I}_{k;
|
|
2181
|
+
\text{DS};⌑;n,m}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2182
|
+
|
|
2183
|
+
Parameters
|
|
2184
|
+
----------
|
|
2185
|
+
deep_copy : `bool`, optional
|
|
2186
|
+
Let ``disk_supports`` denote the attribute
|
|
2187
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_supports`.
|
|
2188
|
+
|
|
2189
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
2190
|
+
``disk_supports`` is returned. Otherwise, a reference to
|
|
2191
|
+
``disk_supports`` is returned.
|
|
2192
|
+
|
|
2193
|
+
Returns
|
|
2194
|
+
-------
|
|
2195
|
+
disk_supports : `torch.Tensor` (`bool`, ndim=3)
|
|
2196
|
+
The attribute
|
|
2197
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_supports`.
|
|
2198
|
+
|
|
2199
|
+
"""
|
|
2200
|
+
params = {"deep_copy": deep_copy}
|
|
2201
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
2202
|
+
|
|
2203
|
+
if self._disk_supports is None:
|
|
2204
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
2205
|
+
method_name = "_calc_disk_supports_and_cache_select_intermediates"
|
|
2206
|
+
method_alias = getattr(self, method_name)
|
|
2207
|
+
self._disk_supports = method_alias(u_x, u_y)
|
|
2208
|
+
|
|
2209
|
+
disk_supports = (self._disk_supports.detach().clone()
|
|
2210
|
+
if (deep_copy == True)
|
|
2211
|
+
else self._disk_supports)
|
|
2212
|
+
|
|
2213
|
+
return disk_supports
|
|
2214
|
+
|
|
2215
|
+
|
|
2216
|
+
|
|
2217
|
+
@property
|
|
2218
|
+
def disk_supports(self):
|
|
2219
|
+
r"""`torch.Tensor`: The image stack of the disk supports,
|
|
2220
|
+
:math:`\left\{\mathcal{I}_{k;
|
|
2221
|
+
\text{DS};⌑;n,m}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2222
|
+
|
|
2223
|
+
See the summary documentation of the class
|
|
2224
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2225
|
+
particular a description of the calculation of
|
|
2226
|
+
:math:`\left\{\mathcal{I}_{k;
|
|
2227
|
+
\text{DS};⌑;n,m}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2228
|
+
|
|
2229
|
+
Let ``core_attrs`` and ``num_disks`` denote the attributes
|
|
2230
|
+
:attr:`~fancytypes.Checkable.core_attrs` and
|
|
2231
|
+
:attr:`fakecbed.discretized.CBEDPattern.num_disks` respectively.
|
|
2232
|
+
|
|
2233
|
+
``disk_supports`` is a PyTorch tensor having a shape equal to
|
|
2234
|
+
``(num_disks,) + 2*(core_attrs["num_pixels_across_pattern"],)``.
|
|
2235
|
+
|
|
2236
|
+
For every pair of nonnegative integers ``(k, n, m)`` that does not raise
|
|
2237
|
+
an ``IndexError`` exception upon calling ``disk_supports[k, n, m]``,
|
|
2238
|
+
``disk_supports[k, n, m]`` is equal to
|
|
2239
|
+
:math:`\mathcal{I}_{k;\text{DS};⌑;n,m}`, with the integers :math:`k`,
|
|
2240
|
+
:math:`n`, and :math:`m` being equal to the values of ``k``, ``n``, and
|
|
2241
|
+
``m`` respectively. Furthermore, for each such triplet of integers ``(k,
|
|
2242
|
+
n, m)``, if ``disk_supports[k, n, m]`` equals zero, then the
|
|
2243
|
+
:math:`k^{\text{th}}` distorted CBED disk is not supported at the pixel
|
|
2244
|
+
of the image of the target fake CBED pattern specified by ``(n, m)``.
|
|
2245
|
+
|
|
2246
|
+
Note that ``disk_supports`` should be considered **read-only**.
|
|
2247
|
+
|
|
2248
|
+
"""
|
|
2249
|
+
result = self.get_disk_supports(deep_copy=True)
|
|
2250
|
+
|
|
2251
|
+
return result
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
def get_disk_overlap_map(self, deep_copy=_default_deep_copy):
|
|
2256
|
+
r"""Return the image of the disk overlap map,
|
|
2257
|
+
:math:`\mathcal{I}_{\text{DOM};⌑;n,m}`.
|
|
2258
|
+
|
|
2259
|
+
Parameters
|
|
2260
|
+
----------
|
|
2261
|
+
deep_copy : `bool`, optional
|
|
2262
|
+
Let ``disk_overlap_map`` denote the attribute
|
|
2263
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_overlap_map`.
|
|
2264
|
+
|
|
2265
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
2266
|
+
``disk_overlap_map`` is returned. Otherwise, a reference to
|
|
2267
|
+
``disk_overlap_map`` is returned.
|
|
2268
|
+
|
|
2269
|
+
Returns
|
|
2270
|
+
-------
|
|
2271
|
+
disk_overlap_map : `torch.Tensor` (`int`, ndim=2)
|
|
2272
|
+
The attribute
|
|
2273
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_overlap_map`.
|
|
2274
|
+
|
|
2275
|
+
"""
|
|
2276
|
+
params = {"deep_copy": deep_copy}
|
|
2277
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
2278
|
+
|
|
2279
|
+
if self._disk_overlap_map is None:
|
|
2280
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
2281
|
+
method_name = ("_calc_disk_overlap_map"
|
|
2282
|
+
"_and_cache_select_intermediates")
|
|
2283
|
+
method_alias = getattr(self, method_name)
|
|
2284
|
+
self._disk_overlap_map = method_alias(u_x, u_y)
|
|
2285
|
+
|
|
2286
|
+
disk_overlap_map = (self._disk_overlap_map.detach().clone()
|
|
2287
|
+
if (deep_copy == True)
|
|
2288
|
+
else self._disk_overlap_map)
|
|
2289
|
+
|
|
2290
|
+
return disk_overlap_map
|
|
2291
|
+
|
|
2292
|
+
|
|
2293
|
+
|
|
2294
|
+
@property
|
|
2295
|
+
def disk_overlap_map(self):
|
|
2296
|
+
r"""`torch.Tensor`: The image of the disk overlap map,
|
|
2297
|
+
:math:`\mathcal{I}_{\text{DOM};⌑;n,m}`.
|
|
2298
|
+
|
|
2299
|
+
See the summary documentation of the class
|
|
2300
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2301
|
+
particular a description of the calculation of
|
|
2302
|
+
:math:`\mathcal{I}_{\text{DOM};⌑;n,m}`.
|
|
2303
|
+
|
|
2304
|
+
Note that :math:`\mathcal{I}_{\text{CBED};⌑;n,m}` is the image of the
|
|
2305
|
+
target fake CBED pattern, which is stored in the attribute
|
|
2306
|
+
:attr:`fakecbed.discretized.CBEDPattern.image`
|
|
2307
|
+
|
|
2308
|
+
Let ``core_attrs`` denote the attribute
|
|
2309
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
2310
|
+
|
|
2311
|
+
``disk_overlap_map`` is a PyTorch tensor having a shape equal to
|
|
2312
|
+
``2*(core_attrs["num_pixels_across_pattern"],)``.
|
|
2313
|
+
|
|
2314
|
+
For every pair of nonnegative integers ``(n, m)`` that does not raise an
|
|
2315
|
+
``IndexError`` exception upon calling ``disk_overlap_map[n, m]``,
|
|
2316
|
+
``disk_overlap_map[n, m]`` is equal to
|
|
2317
|
+
:math:`\mathcal{I}_{\text{DOM};⌑;n,m}`, with the integers :math:`n` and
|
|
2318
|
+
:math:`m` being equal to the values of ``n`` and ``m`` respectively. In
|
|
2319
|
+
other words, for each such pair of integers ``(n, m)``,
|
|
2320
|
+
``disk_overlap_map[n, m]`` is equal to the number of imaged CBED disks
|
|
2321
|
+
that overlap at the corresponding pixel of the image of the target fake
|
|
2322
|
+
CBED pattern.
|
|
2323
|
+
|
|
2324
|
+
Note that ``disk_overlap_map`` should be considered **read-only**.
|
|
2325
|
+
|
|
2326
|
+
"""
|
|
2327
|
+
result = self.get_disk_overlap_map(deep_copy=True)
|
|
2328
|
+
|
|
2329
|
+
return result
|
|
2330
|
+
|
|
2331
|
+
|
|
2332
|
+
|
|
2333
|
+
def get_disk_clipping_registry(self, deep_copy=_default_deep_copy):
|
|
2334
|
+
r"""Return the disk clipping registry,
|
|
2335
|
+
:math:`\left\{\Omega_{k;\text{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2336
|
+
|
|
2337
|
+
Parameters
|
|
2338
|
+
----------
|
|
2339
|
+
deep_copy : `bool`, optional
|
|
2340
|
+
Let ``disk_clipping_registry`` denote the attribute
|
|
2341
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_clipping_registry`.
|
|
2342
|
+
|
|
2343
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
2344
|
+
``disk_clipping_registry`` is returned. Otherwise, a reference to
|
|
2345
|
+
``disk_clipping_registry`` is returned.
|
|
2346
|
+
|
|
2347
|
+
Returns
|
|
2348
|
+
-------
|
|
2349
|
+
disk_clipping_registry : `torch.Tensor` (`bool`, ndim=1)
|
|
2350
|
+
The attribute
|
|
2351
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_clipping_registry`.
|
|
2352
|
+
|
|
2353
|
+
"""
|
|
2354
|
+
params = {"deep_copy": deep_copy}
|
|
2355
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
2356
|
+
|
|
2357
|
+
if self._disk_clipping_registry is None:
|
|
2358
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
2359
|
+
method_name = ("_calc_disk_clipping_registry"
|
|
2360
|
+
"_and_cache_select_intermediates")
|
|
2361
|
+
method_alias = getattr(self, method_name)
|
|
2362
|
+
self._disk_clipping_registry = method_alias(u_x, u_y)
|
|
2363
|
+
|
|
2364
|
+
disk_clipping_registry = (self._disk_clipping_registry.detach().clone()
|
|
2365
|
+
if (deep_copy == True)
|
|
2366
|
+
else self._disk_clipping_registry)
|
|
2367
|
+
|
|
2368
|
+
return disk_clipping_registry
|
|
2369
|
+
|
|
2370
|
+
|
|
2371
|
+
|
|
2372
|
+
@property
|
|
2373
|
+
def disk_clipping_registry(self):
|
|
2374
|
+
r"""`torch.Tensor`: The disk clipping registry,
|
|
2375
|
+
:math:`\left\{\Omega_{k;\text{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2376
|
+
|
|
2377
|
+
See the summary documentation of the class
|
|
2378
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2379
|
+
particular a description of the calculation of
|
|
2380
|
+
:math:`\left\{\Omega_{k;\text{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2381
|
+
|
|
2382
|
+
Note that :math:`N_{\text{D}}` is equal to the value of the attribute
|
|
2383
|
+
:attr:`fakecbed.discretized.CBEDPattern.num_disks`,
|
|
2384
|
+
:math:`\mathcal{I}_{\text{OI};⌑;n,m}` is the image of the illumination
|
|
2385
|
+
support, and :math:`\mathcal{I}_{k;\text{DS};⌑;n,m}` is the image of the
|
|
2386
|
+
support of the :math:`k^{\text{th}}` distorted CBED disk.
|
|
2387
|
+
|
|
2388
|
+
``disk_clipping_registry`` is a one-dimensional PyTorch tensor of length
|
|
2389
|
+
equal to :math:`N_{\text{D}}`. For every nonnegative integer ``k`` less
|
|
2390
|
+
than :math:`N_{\text{D}}`, ``disk_clipping_registry[k]`` is
|
|
2391
|
+
:math:`\Omega_{k;\text{DCR};⌑}`, with the integer :math:`k` being equal
|
|
2392
|
+
to the value of ``k``. If ``disk_clipping_registry[k]`` is equal to
|
|
2393
|
+
``False``, then every nonzero pixel of the image of the support of the
|
|
2394
|
+
:math:`k^{\text{th}}` distorted CBED disk is at least two pixels away
|
|
2395
|
+
from (i.e. at least next-nearest neighbours to) every zero-valued pixel
|
|
2396
|
+
of the image of the illumination support and is at least one pixel away
|
|
2397
|
+
from every pixel bordering the image of the illumination support, and
|
|
2398
|
+
that the image of the support of the :math:`k^{\text{th}}` distorted
|
|
2399
|
+
CBED disk has at least one nonzero pixel. Otherwise, if
|
|
2400
|
+
``disk_clipping_registry[k]`` is equal to ``True``, then the opposite of
|
|
2401
|
+
the above scenario is true.
|
|
2402
|
+
|
|
2403
|
+
Note that ``disk_clipping_registry`` should be considered **read-only**.
|
|
2404
|
+
|
|
2405
|
+
"""
|
|
2406
|
+
result = self.get_disk_clipping_registry(deep_copy=True)
|
|
2407
|
+
|
|
2408
|
+
return result
|
|
2409
|
+
|
|
2410
|
+
|
|
2411
|
+
|
|
2412
|
+
def get_disk_absence_registry(self, deep_copy=_default_deep_copy):
|
|
2413
|
+
r"""Return the disk clipping registry,
|
|
2414
|
+
:math:`\left\{\Omega_{k;\text{DAR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2415
|
+
|
|
2416
|
+
Parameters
|
|
2417
|
+
----------
|
|
2418
|
+
deep_copy : `bool`, optional
|
|
2419
|
+
Let ``disk_absence_registry`` denote the attribute
|
|
2420
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_absence_registry`.
|
|
2421
|
+
|
|
2422
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
2423
|
+
``disk_absence_registry`` is returned. Otherwise, a reference to
|
|
2424
|
+
``disk_absence_registry`` is returned.
|
|
2425
|
+
|
|
2426
|
+
Returns
|
|
2427
|
+
-------
|
|
2428
|
+
disk_absence_registry : `torch.Tensor` (`bool`, ndim=1)
|
|
2429
|
+
The attribute
|
|
2430
|
+
:attr:`fakecbed.discretized.CBEDPattern.disk_absence_registry`.
|
|
2431
|
+
|
|
2432
|
+
"""
|
|
2433
|
+
params = {"deep_copy": deep_copy}
|
|
2434
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
2435
|
+
|
|
2436
|
+
if self._disk_absence_registry is None:
|
|
2437
|
+
u_x, u_y = self._calc_u_x_and_u_y()
|
|
2438
|
+
method_name = ("_calc_disk_absence_registry"
|
|
2439
|
+
"_and_cache_select_intermediates")
|
|
2440
|
+
method_alias = getattr(self, method_name)
|
|
2441
|
+
self._disk_absence_registry = method_alias(u_x, u_y)
|
|
2442
|
+
|
|
2443
|
+
disk_absence_registry = (self._disk_absence_registry.detach().clone()
|
|
2444
|
+
if (deep_copy == True)
|
|
2445
|
+
else self._disk_absence_registry)
|
|
2446
|
+
|
|
2447
|
+
return disk_absence_registry
|
|
2448
|
+
|
|
2449
|
+
|
|
2450
|
+
|
|
2451
|
+
@property
|
|
2452
|
+
def disk_absence_registry(self):
|
|
2453
|
+
r"""`torch.Tensor`: The disk clipping registry,
|
|
2454
|
+
:math:`\left\{\Omega_{k;\text{DAR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2455
|
+
|
|
2456
|
+
See the summary documentation of the class
|
|
2457
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2458
|
+
particular a description of the calculation of
|
|
2459
|
+
:math:`\left\{\Omega_{k;\text{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2460
|
+
|
|
2461
|
+
Note that :math:`N_{\text{D}}` is equal to the value of the attribute
|
|
2462
|
+
:attr:`fakecbed.discretized.CBEDPattern.num_disks`,
|
|
2463
|
+
:math:`\mathcal{I}_{\text{OI};⌑;n,m}` is the image of the illumination
|
|
2464
|
+
support, and :math:`\mathcal{I}_{k;\text{DS};⌑;n,m}` is the image of the
|
|
2465
|
+
support of the :math:`k^{\text{th}}` distorted CBED disk.
|
|
2466
|
+
|
|
2467
|
+
``disk_absence_registry`` is a one-dimensional PyTorch tensor of length
|
|
2468
|
+
equal to :math:`N_{\text{D}}`. For every nonnegative integer ``k`` less
|
|
2469
|
+
than :math:`N_{\text{D}}`, ``disk_absence_registry[k]`` is
|
|
2470
|
+
:math:`\Omega_{k;\text{DAR};⌑}`, with the integer :math:`k` being equal
|
|
2471
|
+
to the value of ``k``. If ``disk_absence_registry[k]`` is equal to
|
|
2472
|
+
``False``, then the image of the support of the :math:`k^{\text{th}}`
|
|
2473
|
+
distorted CBED disk has at least one nonzero pixel. Otherwise, if
|
|
2474
|
+
``disk_absence_registry[k]`` is equal to ``True``, then the opposite of
|
|
2475
|
+
the above scenario is true.
|
|
2476
|
+
|
|
2477
|
+
Note that ``disk_absence_registry`` should be considered **read-only**.
|
|
2478
|
+
|
|
2479
|
+
"""
|
|
2480
|
+
result = self.get_disk_absence_registry(deep_copy=True)
|
|
2481
|
+
|
|
2482
|
+
return result
|
|
2483
|
+
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
@property
|
|
2487
|
+
def image_has_been_overridden(self):
|
|
2488
|
+
r"""`bool`: Equals ``True`` if the image of the target fake CBED pattern
|
|
2489
|
+
has been overridden.
|
|
2490
|
+
|
|
2491
|
+
Let ``override_image_then_reapply_mask``, and ``update`` denote the
|
|
2492
|
+
methods
|
|
2493
|
+
:meth:`fakecbed.discretized.CBEDPattern.override_image_then_reapply_mask`,
|
|
2494
|
+
and :meth:`~fancytypes.Updatable.update`
|
|
2495
|
+
respectively. ``image_has_been_overridden`` equals ``True`` if the
|
|
2496
|
+
method ``override_image_then_reapply_mask`` has been called without
|
|
2497
|
+
raising an exception after either instance update via the method
|
|
2498
|
+
``update``, or instance construction. Otherwise,
|
|
2499
|
+
``image_has_been_overridden`` equals ``False``.
|
|
2500
|
+
|
|
2501
|
+
Note that ``image_has_been_overridden`` should be considered
|
|
2502
|
+
**read-only**.
|
|
2503
|
+
|
|
2504
|
+
"""
|
|
2505
|
+
result = self._image_has_been_overridden
|
|
2506
|
+
|
|
2507
|
+
return result
|
|
2508
|
+
|
|
2509
|
+
|
|
2510
|
+
|
|
2511
|
+
###########################
|
|
2512
|
+
## Define error messages ##
|
|
2513
|
+
###########################
|
|
2514
|
+
|
|
2515
|
+
_check_and_convert_undistorted_disks_err_msg_1 = \
|
|
2516
|
+
("The object ``undistorted_disks`` must be a sequence of "
|
|
2517
|
+
"`fakecbed.shapes.NonuniformBoundedShape` objects, where for each element "
|
|
2518
|
+
"``elem`` in ``undistorted_disks``, "
|
|
2519
|
+
"``isinstance(elem.core_attrs['support'], "
|
|
2520
|
+
"(fakecbed.shapes.Circle, fakecbed.shapes.Ellipse))`` evaluates to "
|
|
2521
|
+
"``True``.")
|
|
2522
|
+
|
|
2523
|
+
_check_and_convert_undistorted_misc_shapes_err_msg_1 = \
|
|
2524
|
+
("The object ``undistorted_misc_shapes`` must be a sequence of objects of "
|
|
2525
|
+
"any of the following types: ("
|
|
2526
|
+
"`fakecbed.shapes.Circle`, "
|
|
2527
|
+
"`fakecbed.shapes.Ellipse`, "
|
|
2528
|
+
"`fakecbed.shapes.Peak`, "
|
|
2529
|
+
"`fakecbed.shapes.Band`, "
|
|
2530
|
+
"`fakecbed.shapes.PlaneWave`, "
|
|
2531
|
+
"`fakecbed.shapes.Arc`, "
|
|
2532
|
+
"`fakecbed.shapes.GenericBlob`, "
|
|
2533
|
+
"`fakecbed.shapes.Orbital`, "
|
|
2534
|
+
"`fakecbed.shapes.Lune`, "
|
|
2535
|
+
"`fakecbed.shapes.NonuniformBoundedShape`).")
|
|
2536
|
+
|
|
2537
|
+
_check_and_convert_distortion_model_err_msg_1 = \
|
|
2538
|
+
("The dimensions, in units of pixels, of the distortion model sampling "
|
|
2539
|
+
"grid, specified by the object ``distortion_model``, must be divisible "
|
|
2540
|
+
"by the object ``num_pixels_across_pattern``.")
|
|
2541
|
+
|
|
2542
|
+
_check_and_convert_cold_pixels_err_msg_1 = \
|
|
2543
|
+
("The object ``cold_pixels`` must be a sequence of integer pairs, where "
|
|
2544
|
+
"each integer pair specifies valid pixel coordinates (i.e. row and column "
|
|
2545
|
+
"indices) of a pixel in the discretized fake CBED pattern.")
|
|
2546
|
+
|
|
2547
|
+
_check_and_convert_overriding_image_err_msg_1 = \
|
|
2548
|
+
("The object ``overriding_image`` must have dimensions, in units of "
|
|
2549
|
+
"pixels, equal to those of the original CBED pattern intensity image "
|
|
2550
|
+
"being overridden, which in this case are ``({}, {})``.")
|
|
2551
|
+
|
|
2552
|
+
_cbed_pattern_err_msg_1 = \
|
|
2553
|
+
("Failed to generate discretized fake CBED pattern. See traceback for "
|
|
2554
|
+
"details.")
|