fakecbed 0.3.6__py3-none-any.whl → 0.5.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/discretized.py +2801 -81
- fakecbed/shapes.py +1 -1
- fakecbed/version.py +16 -3
- {fakecbed-0.3.6.dist-info → fakecbed-0.5.0.dist-info}/METADATA +3 -2
- fakecbed-0.5.0.dist-info/RECORD +10 -0
- fakecbed-0.3.6.dist-info/RECORD +0 -10
- {fakecbed-0.3.6.dist-info → fakecbed-0.5.0.dist-info}/WHEEL +0 -0
- {fakecbed-0.3.6.dist-info → fakecbed-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {fakecbed-0.3.6.dist-info → fakecbed-0.5.0.dist-info}/top_level.txt +0 -0
fakecbed/discretized.py
CHANGED
|
@@ -48,6 +48,9 @@ import distoptica
|
|
|
48
48
|
# For inpainting images.
|
|
49
49
|
import skimage.restoration
|
|
50
50
|
|
|
51
|
+
# For cropping hyperspy signals.
|
|
52
|
+
import empix
|
|
53
|
+
|
|
51
54
|
|
|
52
55
|
|
|
53
56
|
# For creating undistorted geometric shapes.
|
|
@@ -63,7 +66,8 @@ import fakecbed.tds
|
|
|
63
66
|
##################################
|
|
64
67
|
|
|
65
68
|
# List of public objects in module.
|
|
66
|
-
__all__ = ["CBEDPattern"
|
|
69
|
+
__all__ = ["CBEDPattern",
|
|
70
|
+
"CroppedCBEDPattern"]
|
|
67
71
|
|
|
68
72
|
|
|
69
73
|
|
|
@@ -613,8 +617,7 @@ _default_mask_frame = \
|
|
|
613
617
|
|
|
614
618
|
|
|
615
619
|
class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
616
|
-
r"""
|
|
617
|
-
diffraction (CBED) pattern.
|
|
620
|
+
r"""A discretized fake convergent beam electron diffraction (CBED) pattern.
|
|
618
621
|
|
|
619
622
|
A series of parameters need to be specified in order to create an image of a
|
|
620
623
|
fake CBED pattern, with the most important parameters being: the set of
|
|
@@ -640,7 +643,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
640
643
|
model, represented by an instance of the class
|
|
641
644
|
:class:`distoptica.DistortionModel`, they also specify a coordinate
|
|
642
645
|
transformation, :math:`\left(T_{⌑;x}\left(u_{x},u_{y}\right),
|
|
643
|
-
T_{⌑;
|
|
646
|
+
T_{⌑;y}\left(u_{x},u_{y}\right)\right)`, which maps a given coordinate pair
|
|
644
647
|
:math:`\left(u_{x},u_{y}\right)` to a corresponding coordinate pair
|
|
645
648
|
:math:`\left(q_{x},q_{y}\right)`, and implicitly a right-inverse to said
|
|
646
649
|
coordinate transformation,
|
|
@@ -1005,8 +1008,9 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
1005
1008
|
|
|
1006
1009
|
.. math ::
|
|
1007
1010
|
\Omega_{k;\text{DCR};⌑}\leftarrow\begin{cases}
|
|
1008
|
-
\text{True}, & \text{if }
|
|
1009
|
-
\neq0
|
|
1011
|
+
\text{True}, & \text{if }
|
|
1012
|
+
\left(\sum_{n,m}\mathcal{I}_{k;\text{DCM};⌑;n,m}\neq0\right)\bigparallel
|
|
1013
|
+
\left(\sum_{n,m}\mathcal{I}_{k;\text{DS};⌑;n,m}=0\right),\\
|
|
1010
1014
|
\text{False}, & \text{otherwise},
|
|
1011
1015
|
\end{cases}
|
|
1012
1016
|
:label: Omega_k_DCR__1
|
|
@@ -1087,6 +1091,32 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
1087
1091
|
right, bottom, and top sides of the mask frame respectively. If all
|
|
1088
1092
|
elements of ``mask_frame`` are zero, then no pixels in the image of the
|
|
1089
1093
|
target fake CBED pattern are masked by the mask frame.
|
|
1094
|
+
skip_validation_and_conversion : `bool`, optional
|
|
1095
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
1096
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
1097
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
1098
|
+
being `dict` objects.
|
|
1099
|
+
|
|
1100
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
1101
|
+
representation of the constructor parameters excluding the parameter
|
|
1102
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
1103
|
+
different constructor parameter name, excluding the name
|
|
1104
|
+
``"skip_validation_and_conversion"``, and
|
|
1105
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
1106
|
+
constructor parameter with the name given by ``key``.
|
|
1107
|
+
|
|
1108
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
1109
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
1110
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
1111
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
1112
|
+
|
|
1113
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
1114
|
+
then ``core_attrs`` is set to
|
|
1115
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
1116
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
1117
|
+
and/or conversions of the `dict` values of
|
|
1118
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
1119
|
+
copies or conversions are made in this case.
|
|
1090
1120
|
|
|
1091
1121
|
"""
|
|
1092
1122
|
ctor_param_names = ("undistorted_tds_model",
|
|
@@ -1221,7 +1251,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
1221
1251
|
def num_disks(self):
|
|
1222
1252
|
r"""`int`: The total number of CBED disks defined, :math:`N_{\text{D}}`.
|
|
1223
1253
|
|
|
1224
|
-
See the summary documentation
|
|
1254
|
+
See the summary documentation for the class
|
|
1225
1255
|
:class:`fakecbed.discretized.CBEDPattern` for additional context.
|
|
1226
1256
|
|
|
1227
1257
|
Let ``core_attrs`` denote the attribute
|
|
@@ -1259,7 +1289,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
1259
1289
|
_default_skip_validation_and_conversion):
|
|
1260
1290
|
r"""Override the target fake CBED pattern image and reapply masking.
|
|
1261
1291
|
|
|
1262
|
-
See the summary documentation
|
|
1292
|
+
See the summary documentation for the class
|
|
1263
1293
|
:class:`fakecbed.discretized.CBEDPattern` for additional context.
|
|
1264
1294
|
|
|
1265
1295
|
Let ``image``, ``illumination_support``, and ``core_attrs`` denote the
|
|
@@ -1655,20 +1685,17 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
1655
1685
|
clip_support = torch.unsqueeze(clip_support, dim=0)
|
|
1656
1686
|
clip_support = clip_support.to(dtype=torch.float)
|
|
1657
1687
|
|
|
1658
|
-
conv_weights = torch.ones((1, 1,
|
|
1688
|
+
conv_weights = torch.ones((1, 1, 3, 3),
|
|
1659
1689
|
device=illumination_support.device)
|
|
1660
1690
|
|
|
1661
1691
|
kwargs = {"input": clip_support,
|
|
1662
1692
|
"weight": conv_weights,
|
|
1663
1693
|
"padding": "same"}
|
|
1664
|
-
clip_support = (torch.nn.functional.conv2d(**kwargs) != 0)
|
|
1665
|
-
|
|
1666
|
-
clip_support =
|
|
1667
|
-
clip_support[
|
|
1668
|
-
clip_support[
|
|
1669
|
-
clip_support[0, 0, :, :L+2] = True
|
|
1670
|
-
clip_support[0, 0, :, max(N_I_x-R-2, 0):] = True
|
|
1671
|
-
clip_support = clip_support[0, 0]
|
|
1694
|
+
clip_support = (torch.nn.functional.conv2d(**kwargs) != 0)[0, 0]
|
|
1695
|
+
clip_support[:T+1, :] = True
|
|
1696
|
+
clip_support[max(N_I_y-B-1, 0):, :] = True
|
|
1697
|
+
clip_support[:, :L+1] = True
|
|
1698
|
+
clip_support[:, max(N_I_x-R-1, 0):] = True
|
|
1672
1699
|
|
|
1673
1700
|
return clip_support
|
|
1674
1701
|
|
|
@@ -1738,8 +1765,6 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
1738
1765
|
|
|
1739
1766
|
image = self._normalize_matrix(input_matrix=image)
|
|
1740
1767
|
|
|
1741
|
-
image = torch.clip(image, min=0)
|
|
1742
|
-
|
|
1743
1768
|
return image
|
|
1744
1769
|
|
|
1745
1770
|
|
|
@@ -1993,7 +2018,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
1993
2018
|
r"""`hyperspy._signals.signal2d.Signal2D`: The hyperspy signal
|
|
1994
2019
|
representation of the fake CBED pattern.
|
|
1995
2020
|
|
|
1996
|
-
See the summary documentation
|
|
2021
|
+
See the summary documentation for the class
|
|
1997
2022
|
:class:`fakecbed.discretized.CBEDPattern` for additional context.
|
|
1998
2023
|
|
|
1999
2024
|
Let ``image``, ``illumination_support``, ``disk_overlap_map``,
|
|
@@ -2111,7 +2136,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2111
2136
|
r"""`torch.Tensor`: The image of the target fake CBED pattern,
|
|
2112
2137
|
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
2113
2138
|
|
|
2114
|
-
See the summary documentation
|
|
2139
|
+
See the summary documentation for the class
|
|
2115
2140
|
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2116
2141
|
particular a description of the calculation of
|
|
2117
2142
|
:math:`\mathcal{I}_{\text{CBED};⌑;n,m}`.
|
|
@@ -2180,7 +2205,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2180
2205
|
r"""`torch.Tensor`: The image of the illumination support,
|
|
2181
2206
|
:math:`\mathcal{I}_{\text{OI};⌑;n,m}`.
|
|
2182
2207
|
|
|
2183
|
-
See the summary documentation
|
|
2208
|
+
See the summary documentation for the class
|
|
2184
2209
|
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2185
2210
|
particular a description of the calculation of
|
|
2186
2211
|
:math:`\mathcal{I}_{\text{OI};⌑;n,m}`.
|
|
@@ -2258,7 +2283,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2258
2283
|
:math:`\left\{\mathcal{I}_{k;
|
|
2259
2284
|
\text{DS};⌑;n,m}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2260
2285
|
|
|
2261
|
-
See the summary documentation
|
|
2286
|
+
See the summary documentation for the class
|
|
2262
2287
|
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2263
2288
|
particular a description of the calculation of
|
|
2264
2289
|
:math:`\left\{\mathcal{I}_{k;
|
|
@@ -2334,7 +2359,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2334
2359
|
r"""`torch.Tensor`: The image of the disk overlap map,
|
|
2335
2360
|
:math:`\mathcal{I}_{\text{DOM};⌑;n,m}`.
|
|
2336
2361
|
|
|
2337
|
-
See the summary documentation
|
|
2362
|
+
See the summary documentation for the class
|
|
2338
2363
|
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2339
2364
|
particular a description of the calculation of
|
|
2340
2365
|
:math:`\mathcal{I}_{\text{DOM};⌑;n,m}`.
|
|
@@ -2412,7 +2437,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2412
2437
|
r"""`torch.Tensor`: The disk clipping registry,
|
|
2413
2438
|
:math:`\left\{\Omega_{k;\text{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2414
2439
|
|
|
2415
|
-
See the summary documentation
|
|
2440
|
+
See the summary documentation for the class
|
|
2416
2441
|
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2417
2442
|
particular a description of the calculation of
|
|
2418
2443
|
:math:`\left\{\Omega_{k;\text{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
@@ -2428,13 +2453,14 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2428
2453
|
than :math:`N_{\text{D}}`, ``disk_clipping_registry[k]`` is
|
|
2429
2454
|
:math:`\Omega_{k;\text{DCR};⌑}`, with the integer :math:`k` being equal
|
|
2430
2455
|
to the value of ``k``. If ``disk_clipping_registry[k]`` is equal to
|
|
2431
|
-
``False``, then
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2456
|
+
``False``, then the image of the support of the :math:`k^{\text{th}}`
|
|
2457
|
+
distorted CBED disk has at least one nonzero pixel, and that the
|
|
2458
|
+
position of every nonzero pixel of said image is at least two pixels
|
|
2459
|
+
away from (i.e. at least pixel-wise next-nearest neighbours to) the
|
|
2460
|
+
position of every zero-valued pixel of the image of the illumination
|
|
2461
|
+
support, at least two pixels away from the position of every pixel
|
|
2462
|
+
inside the mask frame, and at least one pixel away from every pixel
|
|
2463
|
+
bordering the image of the illumination support. Otherwise, if
|
|
2438
2464
|
``disk_clipping_registry[k]`` is equal to ``True``, then the opposite of
|
|
2439
2465
|
the above scenario is true.
|
|
2440
2466
|
|
|
@@ -2448,7 +2474,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2448
2474
|
|
|
2449
2475
|
|
|
2450
2476
|
def get_disk_absence_registry(self, deep_copy=_default_deep_copy):
|
|
2451
|
-
r"""Return the disk
|
|
2477
|
+
r"""Return the disk absence registry,
|
|
2452
2478
|
:math:`\left\{\Omega_{k;\text{DAR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2453
2479
|
|
|
2454
2480
|
Parameters
|
|
@@ -2488,19 +2514,18 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2488
2514
|
|
|
2489
2515
|
@property
|
|
2490
2516
|
def disk_absence_registry(self):
|
|
2491
|
-
r"""`torch.Tensor`: The disk
|
|
2517
|
+
r"""`torch.Tensor`: The disk absence registry,
|
|
2492
2518
|
:math:`\left\{\Omega_{k;\text{DAR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2493
2519
|
|
|
2494
|
-
See the summary documentation
|
|
2520
|
+
See the summary documentation for the class
|
|
2495
2521
|
:class:`fakecbed.discretized.CBEDPattern` for additional context, in
|
|
2496
2522
|
particular a description of the calculation of
|
|
2497
|
-
:math:`\left\{\Omega_{k;\text{
|
|
2523
|
+
:math:`\left\{\Omega_{k;\text{DAR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
|
|
2498
2524
|
|
|
2499
2525
|
Note that :math:`N_{\text{D}}` is equal to the value of the attribute
|
|
2500
|
-
:attr:`fakecbed.discretized.CBEDPattern.num_disks`,
|
|
2501
|
-
:math:`\mathcal{I}_{
|
|
2502
|
-
|
|
2503
|
-
support of the :math:`k^{\text{th}}` distorted CBED disk.
|
|
2526
|
+
:attr:`fakecbed.discretized.CBEDPattern.num_disks`, and
|
|
2527
|
+
:math:`\mathcal{I}_{k;\text{DS};⌑;n,m}` is the image of the support of
|
|
2528
|
+
the :math:`k^{\text{th}}` distorted CBED disk.
|
|
2504
2529
|
|
|
2505
2530
|
``disk_absence_registry`` is a one-dimensional PyTorch tensor of length
|
|
2506
2531
|
equal to :math:`N_{\text{D}}`. For every nonnegative integer ``k`` less
|
|
@@ -2546,51 +2571,2746 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
|
2546
2571
|
|
|
2547
2572
|
|
|
2548
2573
|
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2574
|
+
def _check_and_convert_cbed_pattern(params):
|
|
2575
|
+
obj_name = "cbed_pattern"
|
|
2576
|
+
obj = params[obj_name]
|
|
2552
2577
|
|
|
2553
|
-
|
|
2554
|
-
("The object ``undistorted_disks`` must be a sequence of "
|
|
2555
|
-
"`fakecbed.shapes.NonuniformBoundedShape` objects, where for each element "
|
|
2556
|
-
"``elem`` in ``undistorted_disks``, "
|
|
2557
|
-
"``isinstance(elem.core_attrs['support'], "
|
|
2558
|
-
"(fakecbed.shapes.Circle, fakecbed.shapes.Ellipse))`` evaluates to "
|
|
2559
|
-
"``True``.")
|
|
2578
|
+
accepted_types = (fakecbed.discretized.CBEDPattern, type(None))
|
|
2560
2579
|
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
"`fakecbed.shapes.Arc`, "
|
|
2570
|
-
"`fakecbed.shapes.GenericBlob`, "
|
|
2571
|
-
"`fakecbed.shapes.Orbital`, "
|
|
2572
|
-
"`fakecbed.shapes.Lune`, "
|
|
2573
|
-
"`fakecbed.shapes.NonuniformBoundedShape`).")
|
|
2580
|
+
if isinstance(obj, accepted_types[-1]):
|
|
2581
|
+
cbed_pattern = accepted_types[0]()
|
|
2582
|
+
else:
|
|
2583
|
+
kwargs = {"obj": obj,
|
|
2584
|
+
"obj_name": obj_name,
|
|
2585
|
+
"accepted_types": accepted_types}
|
|
2586
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
2587
|
+
cbed_pattern = copy.deepcopy(obj)
|
|
2574
2588
|
|
|
2575
|
-
|
|
2576
|
-
("The dimensions, in units of pixels, of the distortion model sampling "
|
|
2577
|
-
"grid, specified by the object ``distortion_model``, must be divisible "
|
|
2578
|
-
"by the object ``num_pixels_across_pattern``.")
|
|
2589
|
+
return cbed_pattern
|
|
2579
2590
|
|
|
2580
|
-
_check_and_convert_rng_seed_err_msg_1 = \
|
|
2581
|
-
("The object ``rng_seed`` must be either a nonnegative integer or of the "
|
|
2582
|
-
"type `NoneType`.")
|
|
2583
2591
|
|
|
2584
|
-
_check_and_convert_cold_pixels_err_msg_1 = \
|
|
2585
|
-
("The object ``cold_pixels`` must be a sequence of integer pairs, where "
|
|
2586
|
-
"each integer pair specifies valid pixel coordinates (i.e. row and column "
|
|
2587
|
-
"indices) of a pixel in the discretized fake CBED pattern.")
|
|
2588
2592
|
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
+
def _pre_serialize_cbed_pattern(cbed_pattern):
|
|
2594
|
+
obj_to_pre_serialize = cbed_pattern
|
|
2595
|
+
serializable_rep = obj_to_pre_serialize.pre_serialize()
|
|
2596
|
+
|
|
2597
|
+
return serializable_rep
|
|
2593
2598
|
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2599
|
+
|
|
2600
|
+
|
|
2601
|
+
def _de_pre_serialize_cbed_pattern(serializable_rep):
|
|
2602
|
+
cbed_pattern = \
|
|
2603
|
+
fakecbed.discretized.CBEDPattern.de_pre_serialize(serializable_rep)
|
|
2604
|
+
|
|
2605
|
+
return cbed_pattern
|
|
2606
|
+
|
|
2607
|
+
|
|
2608
|
+
|
|
2609
|
+
def _check_and_convert_cropping_window_center(params):
|
|
2610
|
+
obj_name = "cropping_window_center"
|
|
2611
|
+
obj = params[obj_name]
|
|
2612
|
+
|
|
2613
|
+
current_func_name = "_check_and_convert_cropping_window_center"
|
|
2614
|
+
|
|
2615
|
+
if obj is not None:
|
|
2616
|
+
try:
|
|
2617
|
+
func_alias = czekitout.convert.to_pair_of_floats
|
|
2618
|
+
kwargs = {"obj": obj, "obj_name": obj_name}
|
|
2619
|
+
cropping_window_center = func_alias(**kwargs)
|
|
2620
|
+
except:
|
|
2621
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
2622
|
+
raise TypeError(err_msg)
|
|
2623
|
+
else:
|
|
2624
|
+
cls_alias = fakecbed.discretized.CBEDPattern
|
|
2625
|
+
cbed_pattern = (params["cbed_pattern"]
|
|
2626
|
+
if isinstance(params["cbed_pattern"], cls_alias)
|
|
2627
|
+
else _check_and_convert_cbed_pattern(params))
|
|
2628
|
+
params["cbed_pattern"] = cbed_pattern
|
|
2629
|
+
|
|
2630
|
+
cbed_pattern_core_attrs = cbed_pattern.get_core_attrs(deep_copy=False)
|
|
2631
|
+
N_W_h = cbed_pattern_core_attrs["num_pixels_across_pattern"]
|
|
2632
|
+
N_W_v = N_W_h
|
|
2633
|
+
|
|
2634
|
+
h_scale = 1/N_W_h
|
|
2635
|
+
v_scale = -1/N_W_v
|
|
2636
|
+
|
|
2637
|
+
h_offset = 0.5/N_W_h
|
|
2638
|
+
v_offset = 1 - (1-0.5)/N_W_v
|
|
2639
|
+
|
|
2640
|
+
cropping_window_center = (h_offset + h_scale*((N_W_h-1)//2),
|
|
2641
|
+
v_offset + v_scale*((N_W_v-1)//2))
|
|
2642
|
+
|
|
2643
|
+
return cropping_window_center
|
|
2644
|
+
|
|
2645
|
+
|
|
2646
|
+
|
|
2647
|
+
def _pre_serialize_cropping_window_center(cropping_window_center):
|
|
2648
|
+
obj_to_pre_serialize = cropping_window_center
|
|
2649
|
+
serializable_rep = obj_to_pre_serialize
|
|
2650
|
+
|
|
2651
|
+
return serializable_rep
|
|
2652
|
+
|
|
2653
|
+
|
|
2654
|
+
|
|
2655
|
+
def _de_pre_serialize_cropping_window_center(serializable_rep):
|
|
2656
|
+
cropping_window_center = serializable_rep
|
|
2657
|
+
|
|
2658
|
+
return cropping_window_center
|
|
2659
|
+
|
|
2660
|
+
|
|
2661
|
+
|
|
2662
|
+
def _check_and_convert_cropping_window_dims_in_pixels(params):
|
|
2663
|
+
obj_name = "cropping_window_dims_in_pixels"
|
|
2664
|
+
obj = params[obj_name]
|
|
2665
|
+
|
|
2666
|
+
current_func_name = "_check_and_convert_cropping_window_dims_in_pixels"
|
|
2667
|
+
|
|
2668
|
+
if obj is not None:
|
|
2669
|
+
try:
|
|
2670
|
+
kwargs = {"obj": obj, "obj_name": obj_name}
|
|
2671
|
+
func_alias = czekitout.convert.to_pair_of_positive_ints
|
|
2672
|
+
cropping_window_dims_in_pixels = func_alias(**kwargs)
|
|
2673
|
+
except:
|
|
2674
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
2675
|
+
raise TypeError(err_msg)
|
|
2676
|
+
else:
|
|
2677
|
+
cls_alias = fakecbed.discretized.CBEDPattern
|
|
2678
|
+
cbed_pattern = (params["cbed_pattern"]
|
|
2679
|
+
if isinstance(params["cbed_pattern"], cls_alias)
|
|
2680
|
+
else _check_and_convert_cbed_pattern(params))
|
|
2681
|
+
params["cbed_pattern"] = cbed_pattern
|
|
2682
|
+
|
|
2683
|
+
cbed_pattern_core_attrs = cbed_pattern.get_core_attrs(deep_copy=False)
|
|
2684
|
+
N_W_h = cbed_pattern_core_attrs["num_pixels_across_pattern"]
|
|
2685
|
+
N_W_v = N_W_h
|
|
2686
|
+
|
|
2687
|
+
cropping_window_dims_in_pixels = (N_W_h, N_W_v)
|
|
2688
|
+
|
|
2689
|
+
return cropping_window_dims_in_pixels
|
|
2690
|
+
|
|
2691
|
+
|
|
2692
|
+
|
|
2693
|
+
def _pre_serialize_cropping_window_dims_in_pixels(
|
|
2694
|
+
cropping_window_dims_in_pixels):
|
|
2695
|
+
obj_to_pre_serialize = cropping_window_dims_in_pixels
|
|
2696
|
+
serializable_rep = obj_to_pre_serialize
|
|
2697
|
+
|
|
2698
|
+
return serializable_rep
|
|
2699
|
+
|
|
2700
|
+
|
|
2701
|
+
|
|
2702
|
+
def _de_pre_serialize_cropping_window_dims_in_pixels(serializable_rep):
|
|
2703
|
+
cropping_window_dims_in_pixels = serializable_rep
|
|
2704
|
+
|
|
2705
|
+
return cropping_window_dims_in_pixels
|
|
2706
|
+
|
|
2707
|
+
|
|
2708
|
+
|
|
2709
|
+
def _check_and_convert_principal_disk_idx(params):
|
|
2710
|
+
obj_name = "principal_disk_idx"
|
|
2711
|
+
func_alias = czekitout.convert.to_nonnegative_int
|
|
2712
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2713
|
+
principal_disk_idx = func_alias(**kwargs)
|
|
2714
|
+
|
|
2715
|
+
return principal_disk_idx
|
|
2716
|
+
|
|
2717
|
+
|
|
2718
|
+
|
|
2719
|
+
def _pre_serialize_principal_disk_idx(principal_disk_idx):
|
|
2720
|
+
obj_to_pre_serialize = principal_disk_idx
|
|
2721
|
+
serializable_rep = obj_to_pre_serialize
|
|
2722
|
+
|
|
2723
|
+
return serializable_rep
|
|
2724
|
+
|
|
2725
|
+
|
|
2726
|
+
|
|
2727
|
+
def _de_pre_serialize_principal_disk_idx(serializable_rep):
|
|
2728
|
+
principal_disk_idx = serializable_rep
|
|
2729
|
+
|
|
2730
|
+
return principal_disk_idx
|
|
2731
|
+
|
|
2732
|
+
|
|
2733
|
+
|
|
2734
|
+
def _check_and_convert_disk_boundary_sample_size(params):
|
|
2735
|
+
obj_name = "disk_boundary_sample_size"
|
|
2736
|
+
func_alias = czekitout.convert.to_positive_int
|
|
2737
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2738
|
+
disk_boundary_sample_size = func_alias(**kwargs)
|
|
2739
|
+
|
|
2740
|
+
return disk_boundary_sample_size
|
|
2741
|
+
|
|
2742
|
+
|
|
2743
|
+
|
|
2744
|
+
def _pre_serialize_disk_boundary_sample_size(disk_boundary_sample_size):
|
|
2745
|
+
obj_to_pre_serialize = disk_boundary_sample_size
|
|
2746
|
+
serializable_rep = obj_to_pre_serialize
|
|
2747
|
+
|
|
2748
|
+
return serializable_rep
|
|
2749
|
+
|
|
2750
|
+
|
|
2751
|
+
|
|
2752
|
+
def _de_pre_serialize_disk_boundary_sample_size(serializable_rep):
|
|
2753
|
+
disk_boundary_sample_size = serializable_rep
|
|
2754
|
+
|
|
2755
|
+
return disk_boundary_sample_size
|
|
2756
|
+
|
|
2757
|
+
|
|
2758
|
+
|
|
2759
|
+
_default_cbed_pattern = None
|
|
2760
|
+
_default_cropping_window_center = None
|
|
2761
|
+
_default_cropping_window_dims_in_pixels = None
|
|
2762
|
+
_default_principal_disk_idx = 0
|
|
2763
|
+
_default_disk_boundary_sample_size = 512
|
|
2764
|
+
|
|
2765
|
+
|
|
2766
|
+
|
|
2767
|
+
class CroppedCBEDPattern(fancytypes.PreSerializableAndUpdatable):
|
|
2768
|
+
r"""A discretized cropped fake convergent beam electron diffraction (CBED)
|
|
2769
|
+
pattern.
|
|
2770
|
+
|
|
2771
|
+
A series of parameters need to be specified in order to create an image of a
|
|
2772
|
+
cropped fake CBED pattern, which are: the set of parameters of a discretized
|
|
2773
|
+
fake CBED pattern; the spatial dimensions and the target center of the
|
|
2774
|
+
cropping window to be applied to said pattern; an index specifying the CBED
|
|
2775
|
+
disk, should it exist, for which to analyze in further detail than any other
|
|
2776
|
+
CBED disk in the pattern; the number of points to sample from the boundary
|
|
2777
|
+
of the unclipped support of the CBED disk specified by the aforementioned
|
|
2778
|
+
index, again should the disk exist; and the mask frame to be applied after
|
|
2779
|
+
cropping the fake CBED pattern.
|
|
2780
|
+
|
|
2781
|
+
Parameters
|
|
2782
|
+
----------
|
|
2783
|
+
cbed_pattern : :class:`fakecbed.discretized.CBEDPattern` | `None`, optional
|
|
2784
|
+
The discretized fake CBED pattern for which to crop. If ``cbed_pattern``
|
|
2785
|
+
is set to ``None``, then the parameter will be reassigned to the value
|
|
2786
|
+
``fakecbed.discretized.CBEDPattern()``.
|
|
2787
|
+
cropping_window_center : `array_like` (`float`, shape=(2,)) | `None`, optional
|
|
2788
|
+
If ``cropping_window_center`` is set to ``None``, then the center of the
|
|
2789
|
+
cropping window is set to the fractional coordinates of the discretized
|
|
2790
|
+
fake CBED pattern corresponding to the pixel that is ``(h_dim+1)//2-1``
|
|
2791
|
+
pixels to the right of the upper left corner pixel, and
|
|
2792
|
+
``(v_dim+1)//2-1`` pixels below the same corner, where ``h_dim`` and
|
|
2793
|
+
``v_dim`` are the horizontal and vertical dimensions of the discretized
|
|
2794
|
+
fake CBED pattern in units of pixels. Otherwise, if
|
|
2795
|
+
``cropping_window_center`` is set to a pair of floating-point numbers,
|
|
2796
|
+
then ``cropping_window_center[0]`` and ``cropping_window_center[1]``
|
|
2797
|
+
specify the fractional horizontal and vertical coordinates,
|
|
2798
|
+
respectively, of the center of the cropping window prior to the subpixel
|
|
2799
|
+
shift to the nearest pixel. Note that the crop is applied after the
|
|
2800
|
+
subpixel shift to the nearest pixel. Moreover, note that the fractional
|
|
2801
|
+
coordinate pair :math:`\left(0, 0\right)` corresponds to the bottom left
|
|
2802
|
+
corner of the fake CBED pattern.
|
|
2803
|
+
|
|
2804
|
+
We define the center of the cropping window to be ``(N_W_h+1)//2 - 1``
|
|
2805
|
+
pixels to the right of the upper left corner of the cropping window, and
|
|
2806
|
+
``(N_W_v+1)//2 - 1`` pixels below the same corner, where ``N_W_h`` and
|
|
2807
|
+
``N_W_v`` are the horizontal and vertical dimensions of the cropping
|
|
2808
|
+
window in units of pixels.
|
|
2809
|
+
cropping_window_dims_in_pixels : `array_like` (`int`, shape=(2,)) | `None`, optional
|
|
2810
|
+
If ``cropping_window_dims_in_pixels`` is set to ``None``, then the
|
|
2811
|
+
dimensions of the cropping window are set to the dimensions of
|
|
2812
|
+
discretized fake CBED pattern. Otherwise, if
|
|
2813
|
+
``cropping_window_dims_in_pixels`` is set to a pair of positive
|
|
2814
|
+
integers, then ``cropping_window_dims_in_pixels[0]`` and
|
|
2815
|
+
``cropping_window_dims_in_pixels[1]`` specify the horizontal and
|
|
2816
|
+
vertical dimensions of the cropping window in units of pixels.
|
|
2817
|
+
principal_disk_idx : `int`, optional
|
|
2818
|
+
A nonnegative integer specifying the CBED disk, should it exist, for
|
|
2819
|
+
which to analyze in further detail than any other CBED disk in the
|
|
2820
|
+
pattern. Assuming that ``cbed_pattern`` has already been reassigned if
|
|
2821
|
+
necessary, and that ``principal_disk_idx <
|
|
2822
|
+
len(cbed_pattern.core_attrs["undistorted_disks"])``, then
|
|
2823
|
+
``principal_disk_idx`` specifies the CBED disk constructed from the
|
|
2824
|
+
undistorted intensity pattern stored in
|
|
2825
|
+
``cbed_pattern.core_attrs["undistorted_disks"][prinicpal_disk_idx]``. If
|
|
2826
|
+
``principal_disk_idx >=
|
|
2827
|
+
len(cbed_pattern.core_attrs["undistorted_disks"])``, then
|
|
2828
|
+
``principal_disk_idx`` specifies a nonexistent CBED disk. See the
|
|
2829
|
+
summary documentation for the class
|
|
2830
|
+
:class:`fakecbed.discretized.CBEDPattern`, in particular the description
|
|
2831
|
+
of the construction parameter ``undistorted_disks`` therein, for
|
|
2832
|
+
additional context.
|
|
2833
|
+
disk_boundary_sample_size : `int`, optional
|
|
2834
|
+
The number of points to sample from the boundary of the unclipped
|
|
2835
|
+
support of the CBED disk specified by ``principal_disk_idx``, should the
|
|
2836
|
+
disk exist, upon calling the method
|
|
2837
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`.
|
|
2838
|
+
Must be a positive integer.
|
|
2839
|
+
mask_frame : `array_like` (`int`, shape=(4,)), optional
|
|
2840
|
+
``mask_frame`` specifies the mask frame to be applied after cropping the
|
|
2841
|
+
fake CBED pattern. ``mask_frame[0]``, ``mask_frame[1]``,
|
|
2842
|
+
``mask_frame[2]``, and ``mask_frame[3]`` are the widths, in units of
|
|
2843
|
+
pixels, of the left, right, bottom, and top sides of the mask frame
|
|
2844
|
+
respectively.
|
|
2845
|
+
skip_validation_and_conversion : `bool`, optional
|
|
2846
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
2847
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
2848
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
2849
|
+
being `dict` objects.
|
|
2850
|
+
|
|
2851
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
2852
|
+
representation of the constructor parameters excluding the parameter
|
|
2853
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
2854
|
+
different constructor parameter name, excluding the name
|
|
2855
|
+
``"skip_validation_and_conversion"``, and
|
|
2856
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
2857
|
+
constructor parameter with the name given by ``key``.
|
|
2858
|
+
|
|
2859
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
2860
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
2861
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
2862
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
2863
|
+
|
|
2864
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
2865
|
+
then ``core_attrs`` is set to
|
|
2866
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
2867
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
2868
|
+
and/or conversions of the `dict` values of
|
|
2869
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
2870
|
+
copies or conversions are made in this case.
|
|
2871
|
+
|
|
2872
|
+
"""
|
|
2873
|
+
ctor_param_names = ("cbed_pattern",
|
|
2874
|
+
"cropping_window_center",
|
|
2875
|
+
"cropping_window_dims_in_pixels",
|
|
2876
|
+
"principal_disk_idx",
|
|
2877
|
+
"disk_boundary_sample_size",
|
|
2878
|
+
"mask_frame")
|
|
2879
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
2880
|
+
"ctor_param_names": ctor_param_names}
|
|
2881
|
+
|
|
2882
|
+
_validation_and_conversion_funcs_ = \
|
|
2883
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
2884
|
+
_pre_serialization_funcs_ = \
|
|
2885
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
2886
|
+
_de_pre_serialization_funcs_ = \
|
|
2887
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
2888
|
+
|
|
2889
|
+
del ctor_param_names, kwargs
|
|
2890
|
+
|
|
2891
|
+
|
|
2892
|
+
|
|
2893
|
+
def __init__(self,
|
|
2894
|
+
cbed_pattern=\
|
|
2895
|
+
_default_cbed_pattern,
|
|
2896
|
+
cropping_window_center=\
|
|
2897
|
+
_default_cropping_window_center,
|
|
2898
|
+
cropping_window_dims_in_pixels=\
|
|
2899
|
+
_default_cropping_window_dims_in_pixels,
|
|
2900
|
+
principal_disk_idx=\
|
|
2901
|
+
_default_principal_disk_idx,
|
|
2902
|
+
disk_boundary_sample_size=\
|
|
2903
|
+
_default_disk_boundary_sample_size,
|
|
2904
|
+
mask_frame=\
|
|
2905
|
+
_default_mask_frame,
|
|
2906
|
+
skip_validation_and_conversion=\
|
|
2907
|
+
_default_skip_validation_and_conversion):
|
|
2908
|
+
ctor_params = {key: val
|
|
2909
|
+
for key, val in locals().items()
|
|
2910
|
+
if (key not in ("self", "__class__"))}
|
|
2911
|
+
kwargs = ctor_params
|
|
2912
|
+
kwargs["skip_cls_tests"] = True
|
|
2913
|
+
fancytypes.PreSerializableAndUpdatable.__init__(self, **kwargs)
|
|
2914
|
+
|
|
2915
|
+
self.execute_post_core_attrs_update_actions()
|
|
2916
|
+
|
|
2917
|
+
return None
|
|
2918
|
+
|
|
2919
|
+
|
|
2920
|
+
|
|
2921
|
+
@classmethod
|
|
2922
|
+
def get_validation_and_conversion_funcs(cls):
|
|
2923
|
+
validation_and_conversion_funcs = \
|
|
2924
|
+
cls._validation_and_conversion_funcs_.copy()
|
|
2925
|
+
|
|
2926
|
+
return validation_and_conversion_funcs
|
|
2927
|
+
|
|
2928
|
+
|
|
2929
|
+
|
|
2930
|
+
@classmethod
|
|
2931
|
+
def get_pre_serialization_funcs(cls):
|
|
2932
|
+
pre_serialization_funcs = \
|
|
2933
|
+
cls._pre_serialization_funcs_.copy()
|
|
2934
|
+
|
|
2935
|
+
return pre_serialization_funcs
|
|
2936
|
+
|
|
2937
|
+
|
|
2938
|
+
|
|
2939
|
+
@classmethod
|
|
2940
|
+
def get_de_pre_serialization_funcs(cls):
|
|
2941
|
+
de_pre_serialization_funcs = \
|
|
2942
|
+
cls._de_pre_serialization_funcs_.copy()
|
|
2943
|
+
|
|
2944
|
+
return de_pre_serialization_funcs
|
|
2945
|
+
|
|
2946
|
+
|
|
2947
|
+
|
|
2948
|
+
def execute_post_core_attrs_update_actions(self):
|
|
2949
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
2950
|
+
for self_core_attr_name in self_core_attrs:
|
|
2951
|
+
attr_name = "_"+self_core_attr_name
|
|
2952
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
2953
|
+
setattr(self, attr_name, attr)
|
|
2954
|
+
|
|
2955
|
+
self._num_disks = \
|
|
2956
|
+
self._cbed_pattern._num_disks
|
|
2957
|
+
self._device = \
|
|
2958
|
+
self._cbed_pattern._device
|
|
2959
|
+
self._image_has_been_overridden = \
|
|
2960
|
+
self._cbed_pattern._image_has_been_overridden
|
|
2961
|
+
|
|
2962
|
+
cropped_blank_signal = self._generate_cropped_blank_signal()
|
|
2963
|
+
|
|
2964
|
+
self._cropped_blank_signal_offsets = \
|
|
2965
|
+
(cropped_blank_signal.axes_manager[0].offset,
|
|
2966
|
+
cropped_blank_signal.axes_manager[1].offset,
|
|
2967
|
+
cropped_blank_signal.axes_manager[2].offset)
|
|
2968
|
+
|
|
2969
|
+
attr_name_subset = ("_illumination_support",
|
|
2970
|
+
"_image",
|
|
2971
|
+
"_signal",
|
|
2972
|
+
"_disk_clipping_registry",
|
|
2973
|
+
"_disk_supports",
|
|
2974
|
+
"_disk_absence_registry",
|
|
2975
|
+
"_principal_disk_is_clipped",
|
|
2976
|
+
"_principal_disk_is_absent",
|
|
2977
|
+
"_disk_overlap_map",
|
|
2978
|
+
"_principal_disk_is_overlapping",
|
|
2979
|
+
"_principal_disk_analysis_results",
|
|
2980
|
+
"_principal_disk_bounding_box"
|
|
2981
|
+
"_in_uncropped_image_fractional_coords",
|
|
2982
|
+
"_principal_disk_bounding_box"
|
|
2983
|
+
"_in_cropped_image_fractional_coords",
|
|
2984
|
+
"_principal_disk_boundary_pts"
|
|
2985
|
+
"_in_uncropped_image_fractional_coords",
|
|
2986
|
+
"_principal_disk_boundary_pts"
|
|
2987
|
+
"_in_cropped_image_fractional_coords")
|
|
2988
|
+
|
|
2989
|
+
for attr_name in attr_name_subset:
|
|
2990
|
+
setattr(self, attr_name, None)
|
|
2991
|
+
|
|
2992
|
+
return None
|
|
2993
|
+
|
|
2994
|
+
|
|
2995
|
+
|
|
2996
|
+
def _generate_cropped_blank_signal(self):
|
|
2997
|
+
uncropped_blank_signal = self._generate_uncropped_blank_signal()
|
|
2998
|
+
|
|
2999
|
+
optional_params = self._generate_optional_cropping_params()
|
|
3000
|
+
|
|
3001
|
+
kwargs = {"input_signal": uncropped_blank_signal,
|
|
3002
|
+
"optional_params": optional_params}
|
|
3003
|
+
cropped_blank_signal = empix.crop(**kwargs)
|
|
3004
|
+
|
|
3005
|
+
return cropped_blank_signal
|
|
3006
|
+
|
|
3007
|
+
|
|
3008
|
+
|
|
3009
|
+
def _generate_uncropped_blank_signal(self):
|
|
3010
|
+
cbed_pattern_core_attrs = \
|
|
3011
|
+
self._cbed_pattern.get_core_attrs(deep_copy=False)
|
|
3012
|
+
|
|
3013
|
+
num_disks = self._num_disks
|
|
3014
|
+
N_x = cbed_pattern_core_attrs["num_pixels_across_pattern"]
|
|
3015
|
+
|
|
3016
|
+
uncropped_blank_signal_data = np.zeros((3+num_disks, N_x, N_x),
|
|
3017
|
+
dtype=float)
|
|
3018
|
+
kwargs = {"data": uncropped_blank_signal_data}
|
|
3019
|
+
uncropped_blank_signal = hyperspy.signals.Signal2D(**kwargs)
|
|
3020
|
+
|
|
3021
|
+
kwargs = {"signal": uncropped_blank_signal}
|
|
3022
|
+
self._cbed_pattern._update_signal_axes(**kwargs)
|
|
3023
|
+
|
|
3024
|
+
return uncropped_blank_signal
|
|
3025
|
+
|
|
3026
|
+
|
|
3027
|
+
|
|
3028
|
+
def _generate_optional_cropping_params(self):
|
|
3029
|
+
kwargs = {"center": self._cropping_window_center,
|
|
3030
|
+
"window_dims": self._cropping_window_dims_in_pixels,
|
|
3031
|
+
"pad_mode": "zeros",
|
|
3032
|
+
"apply_symmetric_mask": False,
|
|
3033
|
+
"title": "",
|
|
3034
|
+
"skip_validation_and_conversion": True}
|
|
3035
|
+
optional_cropping_params = empix.OptionalCroppingParams(**kwargs)
|
|
3036
|
+
|
|
3037
|
+
return optional_cropping_params
|
|
3038
|
+
|
|
3039
|
+
|
|
3040
|
+
|
|
3041
|
+
def update(self,
|
|
3042
|
+
new_core_attr_subset_candidate,
|
|
3043
|
+
skip_validation_and_conversion=\
|
|
3044
|
+
_default_skip_validation_and_conversion):
|
|
3045
|
+
super().update(new_core_attr_subset_candidate,
|
|
3046
|
+
skip_validation_and_conversion)
|
|
3047
|
+
self.execute_post_core_attrs_update_actions()
|
|
3048
|
+
|
|
3049
|
+
return None
|
|
3050
|
+
|
|
3051
|
+
|
|
3052
|
+
|
|
3053
|
+
@property
|
|
3054
|
+
def num_disks(self):
|
|
3055
|
+
r"""`int`: The total number of CBED disks defined, :math:`N_{\text{D}}`.
|
|
3056
|
+
|
|
3057
|
+
See the summary documentation for the class
|
|
3058
|
+
:class:`fakecbed.discretized.CBEDPattern` for additional context.
|
|
3059
|
+
|
|
3060
|
+
Let ``core_attrs`` denote the attribute
|
|
3061
|
+
:attr:`~fancytypes.Checkable.core_attrs`. ``num_disks`` is equal to
|
|
3062
|
+
``core_attrs["cbed_pattern"].num_disks``.
|
|
3063
|
+
|
|
3064
|
+
Note that ``num_disks`` should be considered **read-only**.
|
|
3065
|
+
|
|
3066
|
+
"""
|
|
3067
|
+
result = self._num_disks
|
|
3068
|
+
|
|
3069
|
+
return result
|
|
3070
|
+
|
|
3071
|
+
|
|
3072
|
+
|
|
3073
|
+
@property
|
|
3074
|
+
def device(self):
|
|
3075
|
+
r"""`torch.device`: The device on which computationally intensive
|
|
3076
|
+
PyTorch operations are performed and attributes of the type
|
|
3077
|
+
:class:`torch.Tensor` are stored.
|
|
3078
|
+
|
|
3079
|
+
Note that ``device`` should be considered **read-only**.
|
|
3080
|
+
|
|
3081
|
+
"""
|
|
3082
|
+
result = copy.deepcopy(self._device)
|
|
3083
|
+
|
|
3084
|
+
return result
|
|
3085
|
+
|
|
3086
|
+
|
|
3087
|
+
|
|
3088
|
+
def get_principal_disk_boundary_pts_in_uncropped_image_fractional_coords(
|
|
3089
|
+
self, deep_copy=_default_deep_copy):
|
|
3090
|
+
r"""Return the sample of points on the boundary of the "principal" CBED
|
|
3091
|
+
disk, should it exist, in fractional coordinates of the uncropped CBED
|
|
3092
|
+
pattern.
|
|
3093
|
+
|
|
3094
|
+
Let ``core_attrs`` denote the attribute
|
|
3095
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
3096
|
+
|
|
3097
|
+
Parameters
|
|
3098
|
+
----------
|
|
3099
|
+
deep_copy : `bool`, optional
|
|
3100
|
+
Let
|
|
3101
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
|
|
3102
|
+
denote the attribute
|
|
3103
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`.
|
|
3104
|
+
|
|
3105
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
3106
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
|
|
3107
|
+
is returned. Otherwise, a reference to
|
|
3108
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
|
|
3109
|
+
is returned.
|
|
3110
|
+
|
|
3111
|
+
Returns
|
|
3112
|
+
-------
|
|
3113
|
+
principal_disk_boundary_pts_in_uncropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
|
|
3114
|
+
The attribute
|
|
3115
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`.
|
|
3116
|
+
|
|
3117
|
+
"""
|
|
3118
|
+
params = {"deep_copy": deep_copy}
|
|
3119
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
3120
|
+
|
|
3121
|
+
attr_name = ("_principal_disk_boundary_pts"
|
|
3122
|
+
"_in_uncropped_image_fractional_coords")
|
|
3123
|
+
attr = getattr(self, attr_name)
|
|
3124
|
+
|
|
3125
|
+
if attr is None:
|
|
3126
|
+
method_name = ("_calc_principal_disk_analysis_results"
|
|
3127
|
+
"_and_cache_select_intermediates")
|
|
3128
|
+
method_alias = getattr(self, method_name)
|
|
3129
|
+
self._principal_disk_analysis_results = method_alias()
|
|
3130
|
+
|
|
3131
|
+
attr = getattr(self, attr_name)
|
|
3132
|
+
|
|
3133
|
+
principal_disk_boundary_pts_in_uncropped_image_fractional_coords = \
|
|
3134
|
+
(attr
|
|
3135
|
+
if ((deep_copy == False) or (attr is None))
|
|
3136
|
+
else attr.detach().clone())
|
|
3137
|
+
|
|
3138
|
+
return principal_disk_boundary_pts_in_uncropped_image_fractional_coords
|
|
3139
|
+
|
|
3140
|
+
|
|
3141
|
+
|
|
3142
|
+
def _calc_principal_disk_analysis_results_and_cache_select_intermediates(
|
|
3143
|
+
self):
|
|
3144
|
+
principal_disk_analysis_results = tuple()
|
|
3145
|
+
|
|
3146
|
+
method_name_set = \
|
|
3147
|
+
("_calc_disk_boundary_pts_in_uncropped_image_fractional_coords",
|
|
3148
|
+
"_calc_disk_bounding_box_in_uncropped_image_fractional_coords",
|
|
3149
|
+
"_calc_disk_boundary_pts_in_cropped_image_fractional_coords",
|
|
3150
|
+
"_calc_disk_bounding_box_in_cropped_image_fractional_coords")
|
|
3151
|
+
|
|
3152
|
+
for method_alias_idx, method_name in enumerate(method_name_set):
|
|
3153
|
+
method_alias = getattr(self, method_name)
|
|
3154
|
+
|
|
3155
|
+
if method_alias_idx in (0,):
|
|
3156
|
+
kwargs = \
|
|
3157
|
+
{"disk_idx": \
|
|
3158
|
+
self._principal_disk_idx}
|
|
3159
|
+
elif method_alias_idx in (1, 2):
|
|
3160
|
+
kwargs = \
|
|
3161
|
+
{"disk_boundary_pts_in_uncropped_image_fractional_coords": \
|
|
3162
|
+
principal_disk_analysis_results[0]}
|
|
3163
|
+
else:
|
|
3164
|
+
kwargs = \
|
|
3165
|
+
{"disk_boundary_pts_in_cropped_image_fractional_coords": \
|
|
3166
|
+
principal_disk_analysis_results[2]}
|
|
3167
|
+
|
|
3168
|
+
principal_disk_analysis_results += (method_alias(**kwargs),)
|
|
3169
|
+
|
|
3170
|
+
attr_name = "_principal" + method_name[5:]
|
|
3171
|
+
setattr(self, attr_name, principal_disk_analysis_results[-1])
|
|
3172
|
+
|
|
3173
|
+
return principal_disk_analysis_results
|
|
3174
|
+
|
|
3175
|
+
|
|
3176
|
+
|
|
3177
|
+
def _calc_disk_boundary_pts_in_uncropped_image_fractional_coords(self,
|
|
3178
|
+
disk_idx):
|
|
3179
|
+
cbed_pattern_core_attrs = \
|
|
3180
|
+
self._cbed_pattern.get_core_attrs(deep_copy=False)
|
|
3181
|
+
|
|
3182
|
+
undistorted_disks = cbed_pattern_core_attrs["undistorted_disks"]
|
|
3183
|
+
|
|
3184
|
+
if disk_idx < len(undistorted_disks):
|
|
3185
|
+
undistorted_disk = undistorted_disks[disk_idx]
|
|
3186
|
+
|
|
3187
|
+
num_pixels_across_uncropped_pattern = \
|
|
3188
|
+
cbed_pattern_core_attrs["num_pixels_across_pattern"]
|
|
3189
|
+
|
|
3190
|
+
method_name = \
|
|
3191
|
+
"_calc_undistorted_disk_boundary_starting_angular_coord"
|
|
3192
|
+
method_alias = \
|
|
3193
|
+
getattr(self, method_name)
|
|
3194
|
+
kwargs = \
|
|
3195
|
+
{"undistorted_disk": \
|
|
3196
|
+
undistorted_disk,
|
|
3197
|
+
"num_pixels_across_uncropped_pattern": \
|
|
3198
|
+
num_pixels_across_uncropped_pattern}
|
|
3199
|
+
undistorted_disk_boundary_starting_angular_coord = \
|
|
3200
|
+
method_alias(**kwargs)
|
|
3201
|
+
|
|
3202
|
+
kwargs = {"steps": self._disk_boundary_sample_size}
|
|
3203
|
+
kwargs["start"] = undistorted_disk_boundary_starting_angular_coord
|
|
3204
|
+
kwargs["end"] = (kwargs["start"]
|
|
3205
|
+
+ (kwargs["steps"]-1)*(2*np.pi/kwargs["steps"]))
|
|
3206
|
+
undistorted_disk_boundary_angular_coords = torch.linspace(**kwargs)
|
|
3207
|
+
|
|
3208
|
+
method_name = ("_calc_disk_boundary_pts_and_diffs"
|
|
3209
|
+
"_in_uncropped_image_fractional_coords")
|
|
3210
|
+
method_alias = getattr(self, method_name)
|
|
3211
|
+
kwargs = {"undistorted_disk": \
|
|
3212
|
+
undistorted_disk,
|
|
3213
|
+
"undistorted_disk_boundary_angular_coords": \
|
|
3214
|
+
undistorted_disk_boundary_angular_coords}
|
|
3215
|
+
pts_and_diffs = method_alias(**kwargs)
|
|
3216
|
+
|
|
3217
|
+
disk_boundary_pts_in_uncropped_image_fractional_coords = \
|
|
3218
|
+
pts_and_diffs[0]
|
|
3219
|
+
else:
|
|
3220
|
+
disk_boundary_pts_in_uncropped_image_fractional_coords = \
|
|
3221
|
+
None
|
|
3222
|
+
|
|
3223
|
+
return disk_boundary_pts_in_uncropped_image_fractional_coords
|
|
3224
|
+
|
|
3225
|
+
|
|
3226
|
+
|
|
3227
|
+
def _calc_undistorted_disk_boundary_starting_angular_coord(
|
|
3228
|
+
self, undistorted_disk, num_pixels_across_uncropped_pattern):
|
|
3229
|
+
max_num_iterations = 30
|
|
3230
|
+
iteration_idx = 0
|
|
3231
|
+
num_pts = self._disk_boundary_sample_size
|
|
3232
|
+
search_algorithm_has_not_finished = True
|
|
3233
|
+
|
|
3234
|
+
kwargs = {"steps": num_pts}
|
|
3235
|
+
kwargs["start"] = 0
|
|
3236
|
+
kwargs["end"] = (kwargs["start"]
|
|
3237
|
+
+ (kwargs["steps"]-1)*(2*np.pi/kwargs["steps"]))
|
|
3238
|
+
|
|
3239
|
+
while search_algorithm_has_not_finished:
|
|
3240
|
+
angular_coords = torch.linspace(**kwargs)
|
|
3241
|
+
|
|
3242
|
+
method_name = ("_calc_disk_boundary_pts_and_diffs"
|
|
3243
|
+
"_in_uncropped_image_fractional_coords")
|
|
3244
|
+
method_alias = getattr(self, method_name)
|
|
3245
|
+
kwargs = {"undistorted_disk": \
|
|
3246
|
+
undistorted_disk,
|
|
3247
|
+
"undistorted_disk_boundary_angular_coords": \
|
|
3248
|
+
angular_coords}
|
|
3249
|
+
pts, diffs = method_alias(**kwargs)
|
|
3250
|
+
|
|
3251
|
+
angular_coord_idx = torch.argmax(pts[:, 0])
|
|
3252
|
+
diff_subset = diffs if (iteration_idx==0) else diffs[:-1]
|
|
3253
|
+
N_x = num_pixels_across_uncropped_pattern
|
|
3254
|
+
|
|
3255
|
+
if iteration_idx == 0:
|
|
3256
|
+
min_angle = angular_coords[(angular_coord_idx-16)%num_pts]
|
|
3257
|
+
max_angle = angular_coords[(angular_coord_idx+16)%num_pts]
|
|
3258
|
+
else:
|
|
3259
|
+
min_angle = angular_coords[max(angular_coord_idx-16, 0)]
|
|
3260
|
+
max_angle = angular_coords[min(angular_coord_idx+16, num_pts-1)]
|
|
3261
|
+
|
|
3262
|
+
kwargs = {"steps": num_pts}
|
|
3263
|
+
kwargs["start"] = min_angle
|
|
3264
|
+
kwargs["end"] = max_angle + (min_angle > max_angle)*(2*np.pi)
|
|
3265
|
+
|
|
3266
|
+
search_algorithm_has_not_finished = \
|
|
3267
|
+
((100*N_x*torch.amax(diff_subset) > 1)
|
|
3268
|
+
and (iteration_idx < max_num_iterations-1))
|
|
3269
|
+
|
|
3270
|
+
iteration_idx += 1
|
|
3271
|
+
|
|
3272
|
+
undistorted_disk_boundary_starting_angular_coord = \
|
|
3273
|
+
angular_coords[angular_coord_idx].item() % (2*np.pi)
|
|
3274
|
+
|
|
3275
|
+
return undistorted_disk_boundary_starting_angular_coord
|
|
3276
|
+
|
|
3277
|
+
|
|
3278
|
+
|
|
3279
|
+
def _calc_disk_boundary_pts_and_diffs_in_uncropped_image_fractional_coords(
|
|
3280
|
+
self, undistorted_disk, undistorted_disk_boundary_angular_coords):
|
|
3281
|
+
method_name = \
|
|
3282
|
+
"_calc_shape_boundary_pts_in_uncropped_image_fractional_coords"
|
|
3283
|
+
method_alias = \
|
|
3284
|
+
getattr(self, method_name)
|
|
3285
|
+
kwargs = \
|
|
3286
|
+
{"shape": \
|
|
3287
|
+
undistorted_disk,
|
|
3288
|
+
"shape_boundary_angular_coords": \
|
|
3289
|
+
undistorted_disk_boundary_angular_coords}
|
|
3290
|
+
shape_boundary_pts_in_uncropped_image_fractional_coords = \
|
|
3291
|
+
method_alias(**kwargs)
|
|
3292
|
+
|
|
3293
|
+
u_x = shape_boundary_pts_in_uncropped_image_fractional_coords[:, 0:1]
|
|
3294
|
+
u_y = shape_boundary_pts_in_uncropped_image_fractional_coords[:, 1:2]
|
|
3295
|
+
|
|
3296
|
+
cbed_pattern_core_attrs = \
|
|
3297
|
+
self._cbed_pattern.get_core_attrs(deep_copy=False)
|
|
3298
|
+
distortion_model = \
|
|
3299
|
+
cbed_pattern_core_attrs["distortion_model"]
|
|
3300
|
+
|
|
3301
|
+
distortion_model_core_attrs = \
|
|
3302
|
+
distortion_model.get_core_attrs(deep_copy=False)
|
|
3303
|
+
coord_transform_params = \
|
|
3304
|
+
distortion_model_core_attrs["coord_transform_params"]
|
|
3305
|
+
|
|
3306
|
+
kwargs = {"u_x": u_x,
|
|
3307
|
+
"u_y": u_y,
|
|
3308
|
+
"coord_transform_params": coord_transform_params,
|
|
3309
|
+
"device": self._device,
|
|
3310
|
+
"skip_validation_and_conversion": True}
|
|
3311
|
+
q_x, q_y = distoptica.apply_coord_transform(**kwargs)
|
|
3312
|
+
|
|
3313
|
+
num_pts = self._disk_boundary_sample_size
|
|
3314
|
+
pts = torch.zeros((num_pts, 2), device=self._device)
|
|
3315
|
+
pts[:, 0] = q_x[:, 0]
|
|
3316
|
+
pts[:, 1] = q_y[:, 0]
|
|
3317
|
+
|
|
3318
|
+
diffs = torch.zeros_like(undistorted_disk_boundary_angular_coords)
|
|
3319
|
+
for angular_coord_idx in range(num_pts):
|
|
3320
|
+
pt_1 = pts[(angular_coord_idx)%num_pts]
|
|
3321
|
+
pt_2 = pts[(angular_coord_idx+1)%num_pts]
|
|
3322
|
+
diffs[angular_coord_idx] = torch.linalg.norm(pt_2-pt_1)
|
|
3323
|
+
|
|
3324
|
+
disk_boundary_pts_and_diffs_in_uncropped_image_fractional_coords = \
|
|
3325
|
+
(pts, diffs)
|
|
3326
|
+
|
|
3327
|
+
return disk_boundary_pts_and_diffs_in_uncropped_image_fractional_coords
|
|
3328
|
+
|
|
3329
|
+
|
|
3330
|
+
|
|
3331
|
+
def _calc_shape_boundary_pts_in_uncropped_image_fractional_coords(
|
|
3332
|
+
self, shape, shape_boundary_angular_coords):
|
|
3333
|
+
shape_core_attrs = shape.get_core_attrs(deep_copy=False)
|
|
3334
|
+
shape_support = shape_core_attrs["support"]
|
|
3335
|
+
|
|
3336
|
+
shape_support_core_attrs = shape_support.get_core_attrs(deep_copy=False)
|
|
3337
|
+
u_x_c, u_y_c = shape_support_core_attrs["center"]
|
|
3338
|
+
a = (shape_support_core_attrs["semi_major_axis"]
|
|
3339
|
+
if ("semi_major_axis" in shape_support_core_attrs)
|
|
3340
|
+
else shape_support_core_attrs["radius"])
|
|
3341
|
+
e = shape_support_core_attrs.get("eccentricity", 0)
|
|
3342
|
+
theta = shape_support_core_attrs.get("rotation_angle", 0)
|
|
3343
|
+
|
|
3344
|
+
b = a*np.sqrt(1-e*e).item()
|
|
3345
|
+
phi = shape_boundary_angular_coords
|
|
3346
|
+
|
|
3347
|
+
u_x = (u_x_c + a*torch.cos(phi+theta))[:, None]
|
|
3348
|
+
u_y = (u_y_c + b*torch.sin(phi+theta))[:, None]
|
|
3349
|
+
|
|
3350
|
+
num_pts = shape_boundary_angular_coords.numel()
|
|
3351
|
+
pts = torch.zeros((num_pts, 2), device=self._device)
|
|
3352
|
+
pts[:, 0] = u_x[:, 0]
|
|
3353
|
+
pts[:, 1] = u_y[:, 0]
|
|
3354
|
+
|
|
3355
|
+
shape_boundary_pts_in_uncropped_image_fractional_coords = pts
|
|
3356
|
+
|
|
3357
|
+
return shape_boundary_pts_in_uncropped_image_fractional_coords
|
|
3358
|
+
|
|
3359
|
+
|
|
3360
|
+
|
|
3361
|
+
def _calc_disk_bounding_box_in_uncropped_image_fractional_coords(
|
|
3362
|
+
self, disk_boundary_pts_in_uncropped_image_fractional_coords):
|
|
3363
|
+
if disk_boundary_pts_in_uncropped_image_fractional_coords is not None:
|
|
3364
|
+
pts = disk_boundary_pts_in_uncropped_image_fractional_coords
|
|
3365
|
+
|
|
3366
|
+
disk_bounding_box_in_uncropped_image_fractional_coords = \
|
|
3367
|
+
(torch.amin(pts[:, 0]).item(),
|
|
3368
|
+
torch.amax(pts[:, 0]).item(),
|
|
3369
|
+
torch.amin(pts[:, 1]).item(),
|
|
3370
|
+
torch.amax(pts[:, 1]).item())
|
|
3371
|
+
else:
|
|
3372
|
+
disk_bounding_box_in_uncropped_image_fractional_coords = None
|
|
3373
|
+
|
|
3374
|
+
return disk_bounding_box_in_uncropped_image_fractional_coords
|
|
3375
|
+
|
|
3376
|
+
|
|
3377
|
+
|
|
3378
|
+
def _calc_disk_boundary_pts_in_cropped_image_fractional_coords(
|
|
3379
|
+
self, disk_boundary_pts_in_uncropped_image_fractional_coords):
|
|
3380
|
+
if disk_boundary_pts_in_uncropped_image_fractional_coords is not None:
|
|
3381
|
+
cbed_pattern_core_attrs = \
|
|
3382
|
+
self._cbed_pattern.get_core_attrs(deep_copy=False)
|
|
3383
|
+
|
|
3384
|
+
N_x = cbed_pattern_core_attrs["num_pixels_across_pattern"]
|
|
3385
|
+
N_y = N_x
|
|
3386
|
+
|
|
3387
|
+
N_W_h, N_W_v = self._cropping_window_dims_in_pixels
|
|
3388
|
+
|
|
3389
|
+
kwargs = \
|
|
3390
|
+
{"input": \
|
|
3391
|
+
disk_boundary_pts_in_uncropped_image_fractional_coords}
|
|
3392
|
+
disk_boundary_pts_in_cropped_image_fractional_coords = \
|
|
3393
|
+
torch.zeros_like(**kwargs)
|
|
3394
|
+
|
|
3395
|
+
disk_boundary_pts_in_cropped_image_fractional_coords[:, 0] = \
|
|
3396
|
+
((disk_boundary_pts_in_uncropped_image_fractional_coords[:, 0]
|
|
3397
|
+
- self._cropped_blank_signal_offsets[1]) * (N_x/N_W_h)
|
|
3398
|
+
+ (0.5/N_W_h))
|
|
3399
|
+
disk_boundary_pts_in_cropped_image_fractional_coords[:, 1] = \
|
|
3400
|
+
((disk_boundary_pts_in_uncropped_image_fractional_coords[:, 1]
|
|
3401
|
+
- self._cropped_blank_signal_offsets[2]) * (N_y/N_W_v)
|
|
3402
|
+
+ (1-(1-0.5)/N_W_v))
|
|
3403
|
+
else:
|
|
3404
|
+
disk_boundary_pts_in_cropped_image_fractional_coords = None
|
|
3405
|
+
|
|
3406
|
+
return disk_boundary_pts_in_cropped_image_fractional_coords
|
|
3407
|
+
|
|
3408
|
+
|
|
3409
|
+
|
|
3410
|
+
def _calc_disk_bounding_box_in_cropped_image_fractional_coords(
|
|
3411
|
+
self, disk_boundary_pts_in_cropped_image_fractional_coords):
|
|
3412
|
+
if disk_boundary_pts_in_cropped_image_fractional_coords is not None:
|
|
3413
|
+
pts = disk_boundary_pts_in_cropped_image_fractional_coords
|
|
3414
|
+
|
|
3415
|
+
disk_bounding_box_in_cropped_image_fractional_coords = \
|
|
3416
|
+
(torch.amin(pts[:, 0]).item(),
|
|
3417
|
+
torch.amax(pts[:, 0]).item(),
|
|
3418
|
+
torch.amin(pts[:, 1]).item(),
|
|
3419
|
+
torch.amax(pts[:, 1]).item())
|
|
3420
|
+
else:
|
|
3421
|
+
disk_bounding_box_in_cropped_image_fractional_coords = None
|
|
3422
|
+
|
|
3423
|
+
return disk_bounding_box_in_cropped_image_fractional_coords
|
|
3424
|
+
|
|
3425
|
+
|
|
3426
|
+
|
|
3427
|
+
@property
|
|
3428
|
+
def principal_disk_boundary_pts_in_uncropped_image_fractional_coords(self):
|
|
3429
|
+
r"""`torch.Tensor` | `None`: The sample of points on the boundary of the
|
|
3430
|
+
unclipped support of the "principal" CBED disk, should it exist, in
|
|
3431
|
+
fractional coordinates of the uncropped CBED pattern.
|
|
3432
|
+
|
|
3433
|
+
See the summary documentation for the classes
|
|
3434
|
+
:class:`fakecbed.discretized.CBEDPattern` and
|
|
3435
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
|
|
3436
|
+
|
|
3437
|
+
Let ``core_attrs`` denote the attribute
|
|
3438
|
+
:attr:`~fancytypes.Checkable.core_attrs`. Furthermore, let
|
|
3439
|
+
``cbed_pattern``, ``disk_boundary_sample_size``, and
|
|
3440
|
+
``principal_disk_idx`` denote ``core_attrs["cbed_pattern"]``,
|
|
3441
|
+
``core_attrs["disk_boundary_sample_size"]``, and
|
|
3442
|
+
``core_attrs["principal_disk_idx"]`` respectively.
|
|
3443
|
+
|
|
3444
|
+
The "principal" CBED disk refers to the CBED disk, should it exist, that
|
|
3445
|
+
is specified by the nonnegative integer ``principal_disk_idx``. If
|
|
3446
|
+
``principal_disk_idx <
|
|
3447
|
+
len(cbed_pattern.core_attrs["undistorted_disks"])``, then
|
|
3448
|
+
``principal_disk_idx`` specifies the CBED disk constructed from the
|
|
3449
|
+
undistorted intensity pattern stored in
|
|
3450
|
+
``cbed_pattern.core_attrs["undistorted_disks"][prinicpal_disk_idx]``. If
|
|
3451
|
+
``principal_disk_idx >=
|
|
3452
|
+
len(cbed_pattern.core_attrs["undistorted_disks"])``, then
|
|
3453
|
+
``principal_disk_idx`` specifies a nonexistent CBED disk.
|
|
3454
|
+
|
|
3455
|
+
If ``principal_disk_idx`` specifies a nonexistent CBED disk, then
|
|
3456
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords`` is
|
|
3457
|
+
set to ``None``.
|
|
3458
|
+
|
|
3459
|
+
The remainder of the documentation for the current attribute describes
|
|
3460
|
+
how said attribute is calculated, assuming that the principal CBED disk
|
|
3461
|
+
exists. In :mod:`fakecbed`, the undistorted shape of the support of
|
|
3462
|
+
every CBED disk is assumed to be an ellipse, which incidentally can be a
|
|
3463
|
+
circle if the eccentricity is zero. The definitions of the center, the
|
|
3464
|
+
semi-major axis, the eccentricity, and the rotation angle of a ellipse
|
|
3465
|
+
that are adopted in :mod:`fakecbed` are given implictly in the
|
|
3466
|
+
documentation for the class :class:`fakecbed.shapes.Ellipse`. Let
|
|
3467
|
+
:math:`\left(u_{x;c;\text{PDS}},u_{y;c;\text{PDS}}\right)`,
|
|
3468
|
+
:math:`a_{\text{PDS}}`, :math:`e_{\text{PDS}}`, and
|
|
3469
|
+
:math:`\theta_{\text{PDS}}` be the center, the semi-major axis, the
|
|
3470
|
+
eccentricity, and the rotation angle of the principal CBED disk support.
|
|
3471
|
+
|
|
3472
|
+
Let :math:`u_{x}` and :math:`u_{y}` be the fractional horizontal and
|
|
3473
|
+
vertical coordinates, respectively, of a point in an undistorted image,
|
|
3474
|
+
where :math:`\left(u_{x},u_{y}\right)=\left(0,0\right)` is the bottom
|
|
3475
|
+
left corner of the image. Similarly, let :math:`q_{x}` and :math:`q_{y}`
|
|
3476
|
+
be the fractional horizontal and vertical coordinates, respectively, of
|
|
3477
|
+
a point in a distorted image, where
|
|
3478
|
+
:math:`\left(q_{x},q_{y}\right)=\left(0,0\right)` is the bottom left
|
|
3479
|
+
corner of the image. The core attribute ``cbed_pattern`` specifies a
|
|
3480
|
+
distortion model, which can be accessed via
|
|
3481
|
+
``cbed_pattern.core_attrs["distortion_model"]``. The distortion model
|
|
3482
|
+
specifies a coordinate transformation,
|
|
3483
|
+
:math:`\left(T_{⌑;x}\left(u_{x},u_{y}\right),
|
|
3484
|
+
T_{⌑;x}\left(u_{x},u_{y}\right)\right)`, which maps a given coordinate
|
|
3485
|
+
pair :math:`\left(u_{x},u_{y}\right)` to a corresponding coordinate pair
|
|
3486
|
+
:math:`\left(q_{x},q_{y}\right)`.
|
|
3487
|
+
|
|
3488
|
+
Next, let :math:`N_{\mathcal{I};x} and N_{\mathcal{I};y}` be the number
|
|
3489
|
+
of pixels in the image of the uncropped fake CBED pattern, where we
|
|
3490
|
+
assume that
|
|
3491
|
+
|
|
3492
|
+
.. math ::
|
|
3493
|
+
N_{\mathcal{I};x}=N_{\mathcal{I};y},
|
|
3494
|
+
:label: N_I_x_eq_N_I_y__2
|
|
3495
|
+
|
|
3496
|
+
Furthermore, let :math:`N_{\text{max-iter}}=30, and let N_{\text{SS}}`
|
|
3497
|
+
be ``disk_boundary_sample_size``.
|
|
3498
|
+
|
|
3499
|
+
Next, let :math:`\partial S_{\text{PDS};x;l}` and :math:`\partial
|
|
3500
|
+
S_{\text{PDS};y;l}` denote
|
|
3501
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords[l,
|
|
3502
|
+
0]`` and
|
|
3503
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords[l,
|
|
3504
|
+
1]`` respectively, where
|
|
3505
|
+
````principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
|
|
3506
|
+
is the current attribute, and ``l`` is equal to the value of
|
|
3507
|
+
:math:`l`. The current attribute is calculated effectively by executing
|
|
3508
|
+
the following steps:
|
|
3509
|
+
|
|
3510
|
+
1. Calculate
|
|
3511
|
+
|
|
3512
|
+
.. math ::
|
|
3513
|
+
X_{1} \leftarrow
|
|
3514
|
+
\left\{ l^{\prime}\right\}_{l^{\prime}=0}^{N_{\text{SS}}-1}.
|
|
3515
|
+
:label: X_1__1
|
|
3516
|
+
|
|
3517
|
+
2. Calculate
|
|
3518
|
+
|
|
3519
|
+
.. math ::
|
|
3520
|
+
X_{2} \leftarrow
|
|
3521
|
+
\left\{ l^{\prime}\right\}_{l^{\prime}=0}^{N_{\text{SS}}-2}
|
|
3522
|
+
:label: X_2__1
|
|
3523
|
+
|
|
3524
|
+
3. Calculate
|
|
3525
|
+
|
|
3526
|
+
.. math ::
|
|
3527
|
+
m \leftarrow 0.
|
|
3528
|
+
:label: m__1
|
|
3529
|
+
|
|
3530
|
+
4. Calculate
|
|
3531
|
+
|
|
3532
|
+
.. math ::
|
|
3533
|
+
N_{\text{iter}} \leftarrow 0.
|
|
3534
|
+
:label: N_iter__1
|
|
3535
|
+
|
|
3536
|
+
5. Calculate
|
|
3537
|
+
|
|
3538
|
+
.. math ::
|
|
3539
|
+
\phi_{l}\leftarrow\frac{2\pi l}{N_{\text{SS}}}
|
|
3540
|
+
\text{ for }l\in X_{1}.
|
|
3541
|
+
:label: phi_l__1
|
|
3542
|
+
|
|
3543
|
+
6. Calculate
|
|
3544
|
+
|
|
3545
|
+
.. math ::
|
|
3546
|
+
j\leftarrow\min\left(\left\{ N_{\text{iter}}+1,1\right\} \right).
|
|
3547
|
+
:label: j__1
|
|
3548
|
+
|
|
3549
|
+
7. Calculate
|
|
3550
|
+
|
|
3551
|
+
.. math ::
|
|
3552
|
+
u_{x;l}\leftarrow u_{x;c;\text{PDS}}
|
|
3553
|
+
+a_{\text{PDS}}\cos\left(\phi_{l}+\theta_{\text{PDS}}\right)
|
|
3554
|
+
\text{ for }l\in X_{1},
|
|
3555
|
+
:label: u_x_l__1
|
|
3556
|
+
|
|
3557
|
+
and
|
|
3558
|
+
|
|
3559
|
+
.. math ::
|
|
3560
|
+
u_{y;l}\leftarrow u_{y;c;\text{PDS}}
|
|
3561
|
+
+a_{\text{PDS}}\sqrt{1-e_{\text{PDS}}^{2}}
|
|
3562
|
+
\sin\left(\phi_{l}+\theta_{\text{PDS}}\right)\text{ for }l\in X_{1}.
|
|
3563
|
+
:label: u_y_l__1
|
|
3564
|
+
|
|
3565
|
+
8. Calculate
|
|
3566
|
+
|
|
3567
|
+
.. math ::
|
|
3568
|
+
q_{x;l}\leftarrow T_{⌑;x}\left(u_{x;l},u_{y;l}\right)
|
|
3569
|
+
\text{ for }l\in X_{1},
|
|
3570
|
+
:label: q_x_l__1
|
|
3571
|
+
|
|
3572
|
+
and
|
|
3573
|
+
|
|
3574
|
+
.. math ::
|
|
3575
|
+
q_{y;l}\leftarrow T_{⌑;y}\left(u_{x;l},u_{y;l}\right)
|
|
3576
|
+
\text{ for }l\in X_{1}.
|
|
3577
|
+
:label: q_y_l__1
|
|
3578
|
+
|
|
3579
|
+
9. If :math:`m=0`, then go to step 10, otherwise go to step 21.
|
|
3580
|
+
|
|
3581
|
+
10. Calculate
|
|
3582
|
+
|
|
3583
|
+
.. math ::
|
|
3584
|
+
n\leftarrow\text{argmax}_{l\in X_{1}}\left(u_{x;l}\right).
|
|
3585
|
+
:label: n__1
|
|
3586
|
+
|
|
3587
|
+
11. Calculate
|
|
3588
|
+
|
|
3589
|
+
.. math ::
|
|
3590
|
+
\Delta_{q;l}\leftarrow\sqrt{\sum_{\alpha\in\left\{ x,y\right\} }
|
|
3591
|
+
\left[q_{\alpha;l\mod N_{\text{SS}}}
|
|
3592
|
+
-q_{\alpha;\left\{ l+1\right\} \mod N_{\text{SS}}}\right]^{2}}
|
|
3593
|
+
\text{ for }l\in X_{j}.
|
|
3594
|
+
:label: Delta_q_l__1
|
|
3595
|
+
|
|
3596
|
+
12. If :math:`N_{\text{iter}}=0` then go to step 13, otherwise go to
|
|
3597
|
+
step 15.
|
|
3598
|
+
|
|
3599
|
+
13. Calculate
|
|
3600
|
+
|
|
3601
|
+
.. math ::
|
|
3602
|
+
\phi_{\vdash}\leftarrow\phi_{\left(n-16\right)\mod N_{\text{SS}}},
|
|
3603
|
+
:label: phi_vdash__1
|
|
3604
|
+
|
|
3605
|
+
and
|
|
3606
|
+
|
|
3607
|
+
.. math ::
|
|
3608
|
+
\phi_{\dashv}\leftarrow\phi_{\left(n+16\right)\mod N_{\text{SS}}}.
|
|
3609
|
+
:label: phi_dashv__1
|
|
3610
|
+
|
|
3611
|
+
14. Go to step 16.
|
|
3612
|
+
|
|
3613
|
+
15. Calculate
|
|
3614
|
+
|
|
3615
|
+
.. math ::
|
|
3616
|
+
\phi_{\vdash}\leftarrow
|
|
3617
|
+
\phi_{\max\left(\left\{n-16,0\right\}\right)},
|
|
3618
|
+
:label: phi_vdash__2
|
|
3619
|
+
|
|
3620
|
+
and
|
|
3621
|
+
|
|
3622
|
+
.. math ::
|
|
3623
|
+
\phi_{\dashv}\leftarrow
|
|
3624
|
+
\phi_{\min\left(\left\{n+16,N_{\text{SS}}-1\right\}\right)}.
|
|
3625
|
+
:label: phi_dashv__2
|
|
3626
|
+
|
|
3627
|
+
16. If :math:`\phi_{\vdash}>\phi_{\dashv}`, then calculate
|
|
3628
|
+
|
|
3629
|
+
.. math ::
|
|
3630
|
+
\phi_{\dashv}\leftarrow\phi_{\dashv}+2\pi.
|
|
3631
|
+
:label: phi_dashv__3
|
|
3632
|
+
|
|
3633
|
+
17. Calculate
|
|
3634
|
+
|
|
3635
|
+
.. math ::
|
|
3636
|
+
\phi_{l}\leftarrow
|
|
3637
|
+
\phi_{\vdash}+l\frac{\phi_{\dashv}-\phi_{\vdash}}{N_{\text{SS}}-1}
|
|
3638
|
+
\text{ for }l\in X_{1}.
|
|
3639
|
+
:label: phi_l__2
|
|
3640
|
+
|
|
3641
|
+
18. If :math:`100
|
|
3642
|
+
N_{\mathcal{I};x}\max\left(
|
|
3643
|
+
\left\{\Delta_{q;l}\right\}_{l\in S_{m}}\right) \le 1` or
|
|
3644
|
+
:math:`N_{\text{iter}}=N_{\text{max-iter}}`, then calculate
|
|
3645
|
+
|
|
3646
|
+
|
|
3647
|
+
.. math ::
|
|
3648
|
+
m \leftarrow 1,
|
|
3649
|
+
:label: m__2
|
|
3650
|
+
|
|
3651
|
+
.. math ::
|
|
3652
|
+
\phi_{\vdash}\leftarrow\phi_{n}\mod\left\{ 2\pi\right\},
|
|
3653
|
+
:label: phi_vdash__3
|
|
3654
|
+
|
|
3655
|
+
and
|
|
3656
|
+
|
|
3657
|
+
.. math ::
|
|
3658
|
+
\phi_{\dashv}\leftarrow\phi_{\vdash}+2\pi.
|
|
3659
|
+
:label: phi_dashv__4
|
|
3660
|
+
|
|
3661
|
+
|
|
3662
|
+
19. Calculate
|
|
3663
|
+
|
|
3664
|
+
.. math ::
|
|
3665
|
+
N_{\text{iter}}\leftarrow N_{\text{iter}}+1.
|
|
3666
|
+
:label: N_iter__2
|
|
3667
|
+
|
|
3668
|
+
20. Go to step 6.
|
|
3669
|
+
|
|
3670
|
+
21. Calculate
|
|
3671
|
+
|
|
3672
|
+
.. math ::
|
|
3673
|
+
\partial S_{\text{PDS};x;l}\leftarrow q_{x;l}\text{ for }l\in X_{1},
|
|
3674
|
+
:label: partial_S_PDS_x_l__1
|
|
3675
|
+
|
|
3676
|
+
and
|
|
3677
|
+
|
|
3678
|
+
.. math ::
|
|
3679
|
+
\partial S_{\text{PDS};y;l}\leftarrow q_{y;l}\text{ for }l\in X_{1}.
|
|
3680
|
+
:label: partial_S_PDS_y_l__1
|
|
3681
|
+
|
|
3682
|
+
A consequence of the above algorithm is that :math:`\left(\partial
|
|
3683
|
+
S_{\text{PDS};x;0},\partial S_{\text{PDS};y;0}\right)` is the right-most
|
|
3684
|
+
point of the entire sample of points :math:`\left\{ \left(\partial
|
|
3685
|
+
S_{\text{PDS};x;l},\partial S_{\text{PDS};y;l}\right)\right\} _{l\in
|
|
3686
|
+
S_{1}}` on the boundary of the principal CBED disk.
|
|
3687
|
+
|
|
3688
|
+
Note that
|
|
3689
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
|
|
3690
|
+
should be considered **read-only**.
|
|
3691
|
+
|
|
3692
|
+
"""
|
|
3693
|
+
method_name = ("get_principal_disk_boundary_pts"
|
|
3694
|
+
"_in_uncropped_image_fractional_coords")
|
|
3695
|
+
method_alias = getattr(self, method_name)
|
|
3696
|
+
result = method_alias(deep_copy=True)
|
|
3697
|
+
|
|
3698
|
+
return result
|
|
3699
|
+
|
|
3700
|
+
|
|
3701
|
+
|
|
3702
|
+
def get_principal_disk_bounding_box_in_uncropped_image_fractional_coords(
|
|
3703
|
+
self, deep_copy=_default_deep_copy):
|
|
3704
|
+
r"""Return the bounding box of the "principal" CBED disk, should it
|
|
3705
|
+
exist, in fractional coordinates of the uncropped CBED pattern.
|
|
3706
|
+
|
|
3707
|
+
Let ``core_attrs`` denote the attribute
|
|
3708
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
3709
|
+
|
|
3710
|
+
Parameters
|
|
3711
|
+
----------
|
|
3712
|
+
deep_copy : `bool`, optional
|
|
3713
|
+
Let
|
|
3714
|
+
``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
|
|
3715
|
+
denote the attribute
|
|
3716
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_uncropped_image_fractional_coords`.
|
|
3717
|
+
|
|
3718
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
3719
|
+
``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
|
|
3720
|
+
is returned. Otherwise, a reference to
|
|
3721
|
+
``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
|
|
3722
|
+
is returned.
|
|
3723
|
+
|
|
3724
|
+
Returns
|
|
3725
|
+
-------
|
|
3726
|
+
principal_disk_bounding_box_in_uncropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
|
|
3727
|
+
The attribute
|
|
3728
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_uncropped_image_fractional_coords`.
|
|
3729
|
+
|
|
3730
|
+
"""
|
|
3731
|
+
params = {"deep_copy": deep_copy}
|
|
3732
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
3733
|
+
|
|
3734
|
+
attr_name = ("_principal_disk_bounding_box"
|
|
3735
|
+
"_in_uncropped_image_fractional_coords")
|
|
3736
|
+
attr = getattr(self, attr_name)
|
|
3737
|
+
|
|
3738
|
+
if attr is None:
|
|
3739
|
+
method_name = ("_calc_principal_disk_analysis_results"
|
|
3740
|
+
"_and_cache_select_intermediates")
|
|
3741
|
+
method_alias = getattr(self, method_name)
|
|
3742
|
+
self._principal_disk_analysis_results = method_alias()
|
|
3743
|
+
|
|
3744
|
+
attr = getattr(self, attr_name)
|
|
3745
|
+
|
|
3746
|
+
principal_disk_bounding_box_in_uncropped_image_fractional_coords = \
|
|
3747
|
+
copy.deepcopy(attr) if (deep_copy == True) else attr
|
|
3748
|
+
|
|
3749
|
+
return principal_disk_bounding_box_in_uncropped_image_fractional_coords
|
|
3750
|
+
|
|
3751
|
+
|
|
3752
|
+
|
|
3753
|
+
@property
|
|
3754
|
+
def principal_disk_bounding_box_in_uncropped_image_fractional_coords(self):
|
|
3755
|
+
r"""`tuple` | `None`: The bounding box of the unclipped support of the
|
|
3756
|
+
"principal" CBED disk, should it exist, in fractional coordinates of the
|
|
3757
|
+
uncropped CBED pattern.
|
|
3758
|
+
|
|
3759
|
+
See the documentation for the attribute
|
|
3760
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`
|
|
3761
|
+
for additional context.
|
|
3762
|
+
|
|
3763
|
+
Let ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
|
|
3764
|
+
denote the attribute
|
|
3765
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`.
|
|
3766
|
+
|
|
3767
|
+
``principal_disk_bounding_box_in_uncropped_image_fractional_coords`` is
|
|
3768
|
+
calculated effectively by:
|
|
3769
|
+
|
|
3770
|
+
.. code-block:: python
|
|
3771
|
+
|
|
3772
|
+
import torch
|
|
3773
|
+
|
|
3774
|
+
pts = \
|
|
3775
|
+
principal_disk_boundary_pts_in_uncropped_image_fractional_coords
|
|
3776
|
+
|
|
3777
|
+
if pts is not None:
|
|
3778
|
+
bounding_box = (torch.amin(pts[:, 0]).item(),
|
|
3779
|
+
torch.amax(pts[:, 0]).item(),
|
|
3780
|
+
torch.amin(pts[:, 1]).item(),
|
|
3781
|
+
torch.amax(pts[:, 1]).item())
|
|
3782
|
+
else:
|
|
3783
|
+
bounding_box = None
|
|
3784
|
+
|
|
3785
|
+
principal_disk_bounding_box_in_uncropped_image_fractional_coords = \
|
|
3786
|
+
bounding_box
|
|
3787
|
+
|
|
3788
|
+
Note that
|
|
3789
|
+
``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
|
|
3790
|
+
should be considered **read-only**.
|
|
3791
|
+
|
|
3792
|
+
"""
|
|
3793
|
+
method_name = ("get_principal_disk_bounding_box"
|
|
3794
|
+
"_in_uncropped_image_fractional_coords")
|
|
3795
|
+
method_alias = getattr(self, method_name)
|
|
3796
|
+
result = method_alias(deep_copy=True)
|
|
3797
|
+
|
|
3798
|
+
return result
|
|
3799
|
+
|
|
3800
|
+
|
|
3801
|
+
|
|
3802
|
+
def get_principal_disk_boundary_pts_in_cropped_image_fractional_coords(
|
|
3803
|
+
self, deep_copy=_default_deep_copy):
|
|
3804
|
+
r"""Return the sample of points on the boundary of the "principal" CBED
|
|
3805
|
+
disk, should it exist, in fractional coordinates of the cropped CBED
|
|
3806
|
+
pattern.
|
|
3807
|
+
|
|
3808
|
+
Let ``core_attrs`` denote the attribute
|
|
3809
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
3810
|
+
|
|
3811
|
+
Parameters
|
|
3812
|
+
----------
|
|
3813
|
+
deep_copy : `bool`, optional
|
|
3814
|
+
Let
|
|
3815
|
+
``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
|
|
3816
|
+
denote the attribute
|
|
3817
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`.
|
|
3818
|
+
|
|
3819
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
3820
|
+
``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
|
|
3821
|
+
is returned. Otherwise, a reference to
|
|
3822
|
+
``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
|
|
3823
|
+
is returned.
|
|
3824
|
+
|
|
3825
|
+
Returns
|
|
3826
|
+
-------
|
|
3827
|
+
principal_disk_boundary_pts_in_cropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
|
|
3828
|
+
The attribute
|
|
3829
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`.
|
|
3830
|
+
|
|
3831
|
+
"""
|
|
3832
|
+
params = {"deep_copy": deep_copy}
|
|
3833
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
3834
|
+
|
|
3835
|
+
attr_name = ("_principal_disk_boundary_pts"
|
|
3836
|
+
"_in_cropped_image_fractional_coords")
|
|
3837
|
+
attr = getattr(self, attr_name)
|
|
3838
|
+
|
|
3839
|
+
if attr is None:
|
|
3840
|
+
method_name = ("_calc_principal_disk_analysis_results"
|
|
3841
|
+
"_and_cache_select_intermediates")
|
|
3842
|
+
method_alias = getattr(self, method_name)
|
|
3843
|
+
self._principal_disk_analysis_results = method_alias()
|
|
3844
|
+
|
|
3845
|
+
attr = getattr(self, attr_name)
|
|
3846
|
+
|
|
3847
|
+
principal_disk_boundary_pts_in_cropped_image_fractional_coords = \
|
|
3848
|
+
(attr
|
|
3849
|
+
if ((deep_copy == False) or (attr is None))
|
|
3850
|
+
else attr.detach().clone())
|
|
3851
|
+
|
|
3852
|
+
return principal_disk_boundary_pts_in_cropped_image_fractional_coords
|
|
3853
|
+
|
|
3854
|
+
|
|
3855
|
+
|
|
3856
|
+
@property
|
|
3857
|
+
def principal_disk_boundary_pts_in_cropped_image_fractional_coords(self):
|
|
3858
|
+
r"""`torch.Tensor` | `None`: The sample of points on the boundary of the
|
|
3859
|
+
unclipped support of the "principal" CBED disk, should it exist, in
|
|
3860
|
+
fractional coordinates of the cropped CBED pattern.
|
|
3861
|
+
|
|
3862
|
+
See the documentation for the attribute
|
|
3863
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`
|
|
3864
|
+
for additional context.
|
|
3865
|
+
|
|
3866
|
+
Let ``core_attrs``,
|
|
3867
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``,
|
|
3868
|
+
and ``signal``, denote the attributes
|
|
3869
|
+
:attr:`~fancytypes.Checkable.core_attrs`
|
|
3870
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`,
|
|
3871
|
+
and :attr:`fakecbed.discretized.CroppedCBEDPattern.signal` respectively.
|
|
3872
|
+
|
|
3873
|
+
``principal_disk_boundary_pts_in_cropped_image_fractional_coords`` is
|
|
3874
|
+
calculated effectively by:
|
|
3875
|
+
|
|
3876
|
+
.. code-block:: python
|
|
3877
|
+
|
|
3878
|
+
import torch
|
|
3879
|
+
|
|
3880
|
+
cbed_pattern = core_attrs["cbed_pattern"]
|
|
3881
|
+
N_x = cbed_pattern.core_attrs["num_pixels_across_pattern"]
|
|
3882
|
+
N_y = N_x
|
|
3883
|
+
|
|
3884
|
+
N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
|
|
3885
|
+
|
|
3886
|
+
boundary_pts_in_uncropped_image_fractional_coords = \
|
|
3887
|
+
principal_disk_boundary_pts_in_uncropped_image_fractional_coords
|
|
3888
|
+
|
|
3889
|
+
if boundary_pts_in_uncropped_image_fractional_coords is not None:
|
|
3890
|
+
|
|
3891
|
+
kwargs = \
|
|
3892
|
+
{"input": boundary_pts_in_uncropped_image_fractional_coords}
|
|
3893
|
+
boundary_pts_in_cropped_image_fractional_coords = \
|
|
3894
|
+
torch.zeros_like(**kwargs)
|
|
3895
|
+
|
|
3896
|
+
boundary_pts_in_cropped_image_fractional_coords[:, 0] = \
|
|
3897
|
+
((boundary_pts_in_uncropped_image_fractional_coords[:, 0]
|
|
3898
|
+
- signal.axes_manager[1].offset) * (N_x/N_W_h)
|
|
3899
|
+
+ (0.5/N_W_h))
|
|
3900
|
+
boundary_pts_in_cropped_image_fractional_coords[:, 1] = \
|
|
3901
|
+
((boundary_pts_in_uncropped_image_fractional_coords[:, 1]
|
|
3902
|
+
- signal.axes_manager[2].offset) * (N_y/N_W_v)
|
|
3903
|
+
+ (1-(1-0.5)/N_W_v))
|
|
3904
|
+
else:
|
|
3905
|
+
boundary_pts_in_cropped_image_fractional_coords = \
|
|
3906
|
+
None
|
|
3907
|
+
|
|
3908
|
+
principal_disk_boundary_pts_in_cropped_image_fractional_coords = \
|
|
3909
|
+
boundary_pts_in_cropped_image_fractional_coords
|
|
3910
|
+
|
|
3911
|
+
Note that
|
|
3912
|
+
``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
|
|
3913
|
+
should be considered **read-only**.
|
|
3914
|
+
|
|
3915
|
+
"""
|
|
3916
|
+
method_name = ("get_principal_disk_boundary_pts"
|
|
3917
|
+
"_in_cropped_image_fractional_coords")
|
|
3918
|
+
method_alias = getattr(self, method_name)
|
|
3919
|
+
result = method_alias(deep_copy=True)
|
|
3920
|
+
|
|
3921
|
+
return result
|
|
3922
|
+
|
|
3923
|
+
|
|
3924
|
+
|
|
3925
|
+
def get_principal_disk_bounding_box_in_cropped_image_fractional_coords(
|
|
3926
|
+
self, deep_copy=_default_deep_copy):
|
|
3927
|
+
r"""Return the bounding box of the "principal" CBED disk, should it
|
|
3928
|
+
exist, in fractional coordinates of the cropped CBED pattern.
|
|
3929
|
+
|
|
3930
|
+
Let ``core_attrs`` denote the attribute
|
|
3931
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
3932
|
+
|
|
3933
|
+
Parameters
|
|
3934
|
+
----------
|
|
3935
|
+
deep_copy : `bool`, optional
|
|
3936
|
+
Let
|
|
3937
|
+
``principal_disk_bounding_box_in_cropped_image_fractional_coords``
|
|
3938
|
+
denote the attribute
|
|
3939
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_cropped_image_fractional_coords`.
|
|
3940
|
+
|
|
3941
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
3942
|
+
``principal_disk_bounding_box_in_cropped_image_fractional_coords``
|
|
3943
|
+
is returned. Otherwise, a reference to
|
|
3944
|
+
``principal_disk_bounding_box_in_cropped_image_fractional_coords``
|
|
3945
|
+
is returned.
|
|
3946
|
+
|
|
3947
|
+
Returns
|
|
3948
|
+
-------
|
|
3949
|
+
principal_disk_bounding_box_in_cropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
|
|
3950
|
+
The attribute
|
|
3951
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_cropped_image_fractional_coords`.
|
|
3952
|
+
|
|
3953
|
+
"""
|
|
3954
|
+
params = {"deep_copy": deep_copy}
|
|
3955
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
3956
|
+
|
|
3957
|
+
attr_name = ("_principal_disk_bounding_box"
|
|
3958
|
+
"_in_cropped_image_fractional_coords")
|
|
3959
|
+
attr = getattr(self, attr_name)
|
|
3960
|
+
|
|
3961
|
+
if attr is None:
|
|
3962
|
+
method_name = ("_calc_principal_disk_analysis_results"
|
|
3963
|
+
"_and_cache_select_intermediates")
|
|
3964
|
+
method_alias = getattr(self, method_name)
|
|
3965
|
+
self._principal_disk_analysis_results = method_alias()
|
|
3966
|
+
|
|
3967
|
+
attr = getattr(self, attr_name)
|
|
3968
|
+
|
|
3969
|
+
principal_disk_bounding_box_in_cropped_image_fractional_coords = \
|
|
3970
|
+
copy.deepcopy(attr) if (deep_copy == True) else attr
|
|
3971
|
+
|
|
3972
|
+
return principal_disk_bounding_box_in_cropped_image_fractional_coords
|
|
3973
|
+
|
|
3974
|
+
|
|
3975
|
+
|
|
3976
|
+
@property
|
|
3977
|
+
def principal_disk_bounding_box_in_cropped_image_fractional_coords(self):
|
|
3978
|
+
r"""`tuple` | `None`: The bounding box of the unclipped support of the
|
|
3979
|
+
"principal" CBED disk, should it exist, in fractional coordinates of the
|
|
3980
|
+
cropped CBED pattern.
|
|
3981
|
+
|
|
3982
|
+
See the documentation for the attribute
|
|
3983
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`
|
|
3984
|
+
for additional context.
|
|
3985
|
+
|
|
3986
|
+
Let ``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
|
|
3987
|
+
denote the attribute
|
|
3988
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`.
|
|
3989
|
+
|
|
3990
|
+
``principal_disk_bounding_box_in_cropped_image_fractional_coords`` is
|
|
3991
|
+
calculated effectively by:
|
|
3992
|
+
|
|
3993
|
+
.. code-block:: python
|
|
3994
|
+
|
|
3995
|
+
import torch
|
|
3996
|
+
|
|
3997
|
+
pts = \
|
|
3998
|
+
principal_disk_boundary_pts_in_cropped_image_fractional_coords
|
|
3999
|
+
|
|
4000
|
+
if pts is not None:
|
|
4001
|
+
bounding_box = (torch.amin(pts[:, 0]).item(),
|
|
4002
|
+
torch.amax(pts[:, 0]).item(),
|
|
4003
|
+
torch.amin(pts[:, 1]).item(),
|
|
4004
|
+
torch.amax(pts[:, 1]).item())
|
|
4005
|
+
else:
|
|
4006
|
+
bounding_box = None
|
|
4007
|
+
|
|
4008
|
+
principal_disk_bounding_box_in_cropped_image_fractional_coords = \
|
|
4009
|
+
bounding_box
|
|
4010
|
+
|
|
4011
|
+
Note that
|
|
4012
|
+
``principal_disk_bounding_box_in_cropped_image_fractional_coords``
|
|
4013
|
+
should be considered **read-only**.
|
|
4014
|
+
|
|
4015
|
+
"""
|
|
4016
|
+
method_name = ("get_principal_disk_bounding_box"
|
|
4017
|
+
"_in_cropped_image_fractional_coords")
|
|
4018
|
+
method_alias = getattr(self, method_name)
|
|
4019
|
+
result = method_alias(deep_copy=True)
|
|
4020
|
+
|
|
4021
|
+
return result
|
|
4022
|
+
|
|
4023
|
+
|
|
4024
|
+
|
|
4025
|
+
def get_disk_supports(self, deep_copy=_default_deep_copy):
|
|
4026
|
+
r"""Return the image stack of the disk supports of the cropped fake CBED
|
|
4027
|
+
pattern.
|
|
4028
|
+
|
|
4029
|
+
Parameters
|
|
4030
|
+
----------
|
|
4031
|
+
deep_copy : `bool`, optional
|
|
4032
|
+
Let ``disk_supports`` denote the attribute
|
|
4033
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`.
|
|
4034
|
+
|
|
4035
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
4036
|
+
``disk_supports`` is returned. Otherwise, a reference to
|
|
4037
|
+
``disk_supports`` is returned.
|
|
4038
|
+
|
|
4039
|
+
Returns
|
|
4040
|
+
-------
|
|
4041
|
+
disk_supports : `torch.Tensor` (`bool`, ndim=3)
|
|
4042
|
+
The attribute
|
|
4043
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`.
|
|
4044
|
+
|
|
4045
|
+
"""
|
|
4046
|
+
params = {"deep_copy": deep_copy}
|
|
4047
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
4048
|
+
|
|
4049
|
+
if self._disk_supports is None:
|
|
4050
|
+
method_name = "_calc_disk_supports"
|
|
4051
|
+
method_alias = getattr(self, method_name)
|
|
4052
|
+
self._disk_supports = method_alias()
|
|
4053
|
+
|
|
4054
|
+
disk_supports = (self._disk_supports.detach().clone()
|
|
4055
|
+
if (deep_copy == True)
|
|
4056
|
+
else self._disk_supports)
|
|
4057
|
+
|
|
4058
|
+
return disk_supports
|
|
4059
|
+
|
|
4060
|
+
|
|
4061
|
+
|
|
4062
|
+
def _calc_disk_supports(self):
|
|
4063
|
+
N_W_h, N_W_v = self._cropping_window_dims_in_pixels
|
|
4064
|
+
L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
|
|
4065
|
+
|
|
4066
|
+
uncropped_cbed_pattern_disk_supports = \
|
|
4067
|
+
self._cbed_pattern.get_disk_supports(deep_copy=False)
|
|
4068
|
+
uncropped_cbed_pattern_disk_supports = \
|
|
4069
|
+
uncropped_cbed_pattern_disk_supports.cpu().detach().clone()
|
|
4070
|
+
|
|
4071
|
+
signal_to_crop = \
|
|
4072
|
+
self._generate_uncropped_blank_signal()
|
|
4073
|
+
signal_to_crop.data[3:] = \
|
|
4074
|
+
uncropped_cbed_pattern_disk_supports.numpy(force=True)[:]
|
|
4075
|
+
|
|
4076
|
+
optional_params = self._generate_optional_cropping_params()
|
|
4077
|
+
|
|
4078
|
+
kwargs = {"input_signal": signal_to_crop,
|
|
4079
|
+
"optional_params": optional_params}
|
|
4080
|
+
cropped_signal = empix.crop(**kwargs)
|
|
4081
|
+
|
|
4082
|
+
disk_supports = cropped_signal.data[3:]
|
|
4083
|
+
disk_supports_dtype = uncropped_cbed_pattern_disk_supports.dtype
|
|
4084
|
+
disk_supports = torch.from_numpy(disk_supports)
|
|
4085
|
+
disk_supports = disk_supports.to(device=self._device,
|
|
4086
|
+
dtype=disk_supports_dtype)
|
|
4087
|
+
disk_supports[:, :T, :] = 0
|
|
4088
|
+
disk_supports[:, max(N_W_v-B, 0):, :] = 0
|
|
4089
|
+
disk_supports[:, :, :L] = 0
|
|
4090
|
+
disk_supports[:, :, max(N_W_h-R, 0):] = 0
|
|
4091
|
+
|
|
4092
|
+
return disk_supports
|
|
4093
|
+
|
|
4094
|
+
|
|
4095
|
+
|
|
4096
|
+
@property
|
|
4097
|
+
def disk_supports(self):
|
|
4098
|
+
r"""`torch.Tensor`: The image stack of the disk supports of the cropped
|
|
4099
|
+
fake CBED pattern.
|
|
4100
|
+
|
|
4101
|
+
See the documentation for the attribute
|
|
4102
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal` for additional
|
|
4103
|
+
context.
|
|
4104
|
+
|
|
4105
|
+
Let ``signal``, ``device``, and ``core_attrs`` denote the attributes
|
|
4106
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal`,
|
|
4107
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.device`, and
|
|
4108
|
+
:attr:`~fancytypes.Checkable.core_attrs` respectively.
|
|
4109
|
+
|
|
4110
|
+
``disk_supports`` is calculated effectively by:
|
|
4111
|
+
|
|
4112
|
+
.. code-block:: python
|
|
4113
|
+
|
|
4114
|
+
N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
|
|
4115
|
+
L, R, B, T = core_attrs["mask_frame"]
|
|
4116
|
+
|
|
4117
|
+
disk_supports = signal.data[3:]
|
|
4118
|
+
disk_supports = torch.from_numpy(disk_supports)
|
|
4119
|
+
disk_supports = disk_supports.to(device=device, dtype=bool)
|
|
4120
|
+
disk_supports[:, :T, :] = 0
|
|
4121
|
+
disk_supports[:, max(N_W_v-B, 0):, :] = 0
|
|
4122
|
+
disk_supports[:, :, :L] = 0
|
|
4123
|
+
disk_supports[:, :, max(N_W_h-R, 0):] = 0
|
|
4124
|
+
|
|
4125
|
+
Note that ``disk_supports`` should be considered **read-only**.
|
|
4126
|
+
|
|
4127
|
+
"""
|
|
4128
|
+
result = self.get_disk_supports(deep_copy=True)
|
|
4129
|
+
|
|
4130
|
+
return result
|
|
4131
|
+
|
|
4132
|
+
|
|
4133
|
+
|
|
4134
|
+
def get_disk_clipping_registry(self, deep_copy=_default_deep_copy):
|
|
4135
|
+
r"""Return the disk clipping registry of the cropped fake CBED pattern.
|
|
4136
|
+
|
|
4137
|
+
Parameters
|
|
4138
|
+
----------
|
|
4139
|
+
deep_copy : `bool`, optional
|
|
4140
|
+
Let ``disk_clipping_registry`` denote the attribute
|
|
4141
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`.
|
|
4142
|
+
|
|
4143
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
4144
|
+
``disk_clipping_registry`` is returned. Otherwise, a reference to
|
|
4145
|
+
``disk_clipping_registry`` is returned.
|
|
4146
|
+
|
|
4147
|
+
Returns
|
|
4148
|
+
-------
|
|
4149
|
+
disk_clipping_registry : `torch.Tensor` (`bool`, ndim=1)
|
|
4150
|
+
The attribute
|
|
4151
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`.
|
|
4152
|
+
|
|
4153
|
+
"""
|
|
4154
|
+
params = {"deep_copy": deep_copy}
|
|
4155
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
4156
|
+
|
|
4157
|
+
if self._disk_clipping_registry is None:
|
|
4158
|
+
method_name = ("_calc_disk_clipping_registry"
|
|
4159
|
+
"_and_cache_select_intermediates")
|
|
4160
|
+
method_alias = getattr(self, method_name)
|
|
4161
|
+
self._disk_clipping_registry = method_alias()
|
|
4162
|
+
|
|
4163
|
+
disk_clipping_registry = (self._disk_clipping_registry.detach().clone()
|
|
4164
|
+
if (deep_copy == True)
|
|
4165
|
+
else self._disk_clipping_registry)
|
|
4166
|
+
|
|
4167
|
+
return disk_clipping_registry
|
|
4168
|
+
|
|
4169
|
+
|
|
4170
|
+
|
|
4171
|
+
def _calc_disk_clipping_registry_and_cache_select_intermediates(self):
|
|
4172
|
+
num_disks = self._num_disks
|
|
4173
|
+
|
|
4174
|
+
if num_disks > 0:
|
|
4175
|
+
if self._disk_supports is None:
|
|
4176
|
+
method_name = "_calc_disk_supports"
|
|
4177
|
+
method_alias = getattr(self, method_name)
|
|
4178
|
+
self._disk_supports = method_alias()
|
|
4179
|
+
disk_supports = self._disk_supports
|
|
4180
|
+
|
|
4181
|
+
clip_support = self._calc_clip_support()
|
|
4182
|
+
|
|
4183
|
+
disk_clipping_map = disk_supports*clip_support[None, :, :]
|
|
4184
|
+
|
|
4185
|
+
disk_clipping_registry = ((disk_clipping_map.sum(dim=(1, 2)) != 0)
|
|
4186
|
+
+ (disk_supports.sum(dim=(1, 2)) == 0))
|
|
4187
|
+
else:
|
|
4188
|
+
disk_clipping_registry = torch.zeros((num_disks,),
|
|
4189
|
+
device=self._device,
|
|
4190
|
+
dtype=torch.bool)
|
|
4191
|
+
|
|
4192
|
+
return disk_clipping_registry
|
|
4193
|
+
|
|
4194
|
+
|
|
4195
|
+
|
|
4196
|
+
def _calc_clip_support(self):
|
|
4197
|
+
cbed_pattern = self._cbed_pattern
|
|
4198
|
+
|
|
4199
|
+
cbed_pattern_core_attrs = cbed_pattern.get_core_attrs(deep_copy=False)
|
|
4200
|
+
|
|
4201
|
+
illumination_support = \
|
|
4202
|
+
cbed_pattern.get_illumination_support(deep_copy=False)
|
|
4203
|
+
illumination_support = \
|
|
4204
|
+
illumination_support.cpu().detach().clone()
|
|
4205
|
+
|
|
4206
|
+
L, R, B, T = cbed_pattern._mask_frame
|
|
4207
|
+
|
|
4208
|
+
N_x = cbed_pattern_core_attrs["num_pixels_across_pattern"]
|
|
4209
|
+
N_y = N_x
|
|
4210
|
+
|
|
4211
|
+
signal_to_crop = self._generate_uncropped_blank_signal()
|
|
4212
|
+
signal_to_crop.data[0] = illumination_support.numpy(force=True)
|
|
4213
|
+
signal_to_crop.data[0, :T, :] = 0
|
|
4214
|
+
signal_to_crop.data[0, max(N_y-B, 0):, :] = 0
|
|
4215
|
+
signal_to_crop.data[0, :, :L] = 0
|
|
4216
|
+
signal_to_crop.data[0, :, max(N_x-R, 0):] = 0
|
|
4217
|
+
|
|
4218
|
+
optional_params = self._generate_optional_cropping_params()
|
|
4219
|
+
|
|
4220
|
+
kwargs = {"input_signal": signal_to_crop,
|
|
4221
|
+
"optional_params": optional_params}
|
|
4222
|
+
cropped_signal = empix.crop(**kwargs)
|
|
4223
|
+
|
|
4224
|
+
clip_support = torch.from_numpy(1.0-cropped_signal.data[0])
|
|
4225
|
+
clip_support = clip_support.to(device=self._device, dtype=torch.float)
|
|
4226
|
+
for _ in range(2):
|
|
4227
|
+
clip_support = torch.unsqueeze(clip_support, dim=0)
|
|
4228
|
+
|
|
4229
|
+
conv_weights = torch.ones((1, 1, 3, 3), device=self._device)
|
|
4230
|
+
|
|
4231
|
+
N_W_h, N_W_v = self._cropping_window_dims_in_pixels
|
|
4232
|
+
L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
|
|
4233
|
+
|
|
4234
|
+
kwargs = {"input": clip_support,
|
|
4235
|
+
"weight": conv_weights,
|
|
4236
|
+
"padding": "same"}
|
|
4237
|
+
clip_support = (torch.nn.functional.conv2d(**kwargs) != 0)[0, 0]
|
|
4238
|
+
clip_support[:T+1, :] = True
|
|
4239
|
+
clip_support[max(N_W_v-B-1, 0):, :] = True
|
|
4240
|
+
clip_support[:, :L+1] = True
|
|
4241
|
+
clip_support[:, max(N_W_h-R-1, 0):] = True
|
|
4242
|
+
|
|
4243
|
+
return clip_support
|
|
4244
|
+
|
|
4245
|
+
|
|
4246
|
+
|
|
4247
|
+
@property
|
|
4248
|
+
def disk_clipping_registry(self):
|
|
4249
|
+
r"""`torch.Tensor`: The disk clipping registry of the cropped fake CBED
|
|
4250
|
+
pattern.
|
|
4251
|
+
|
|
4252
|
+
See the summary documentation for the classes
|
|
4253
|
+
:class:`fakecbed.discretized.CBEDPattern`, and
|
|
4254
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
|
|
4255
|
+
|
|
4256
|
+
Let ``num_disks``, ``disk_supports``, ``illumination_support``, and
|
|
4257
|
+
``image`` denote the attributes
|
|
4258
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`,
|
|
4259
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`,
|
|
4260
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.illumination_support`,
|
|
4261
|
+
and :attr:`fakecbed.discretized.CroppedCBEDPattern.image`
|
|
4262
|
+
respectively. Moreover, let ``k`` be a nonnegative integer less than
|
|
4263
|
+
``num_disks``.
|
|
4264
|
+
|
|
4265
|
+
``disk_clipping_registry`` is a one-dimensional PyTorch tensor of length
|
|
4266
|
+
equal to ``num_disks``. If ``disk_clipping_registry[k]`` is equal to
|
|
4267
|
+
``False``, then the image stored in ``disk_supports[k]`` has at least
|
|
4268
|
+
one nonzero pixel, and that the position of every nonzero pixel of said
|
|
4269
|
+
image is at least two pixels away from (i.e. at least pixel-wise
|
|
4270
|
+
next-nearest neighbours to) the position of every zero-valued pixel of
|
|
4271
|
+
the image stored in ``illumination_support``, at least two pixels away
|
|
4272
|
+
from the position of every pixel inside any of the mask frames that
|
|
4273
|
+
appears in the image stored in ``image``, and at least one pixel away
|
|
4274
|
+
from every pixel bordering the image stored in ``image``. Otherwise, if
|
|
4275
|
+
``disk_clipping_registry[k]`` is equal to ``True``, then the opposite of
|
|
4276
|
+
the above scenario is true.
|
|
4277
|
+
|
|
4278
|
+
Note that there are potentially two mask frames that may appear in the
|
|
4279
|
+
image stored in ``image``: the mask frame applied to the uncropped fake
|
|
4280
|
+
CBED pattern, and the mask frame that is applied after cropping the fake
|
|
4281
|
+
CBED pattern.
|
|
4282
|
+
|
|
4283
|
+
Note that ``disk_clipping_registry`` should be considered **read-only**.
|
|
4284
|
+
|
|
4285
|
+
"""
|
|
4286
|
+
result = self.get_disk_clipping_registry(deep_copy=True)
|
|
4287
|
+
|
|
4288
|
+
return result
|
|
4289
|
+
|
|
4290
|
+
|
|
4291
|
+
|
|
4292
|
+
def get_disk_absence_registry(self, deep_copy=_default_deep_copy):
|
|
4293
|
+
r"""Return the disk absence registry of the cropped fake CBED pattern.
|
|
4294
|
+
|
|
4295
|
+
Parameters
|
|
4296
|
+
----------
|
|
4297
|
+
deep_copy : `bool`, optional
|
|
4298
|
+
Let ``disk_absence_registry`` denote the attribute
|
|
4299
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`.
|
|
4300
|
+
|
|
4301
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
4302
|
+
``disk_absence_registry`` is returned. Otherwise, a reference to
|
|
4303
|
+
``disk_absence_registry`` is returned.
|
|
4304
|
+
|
|
4305
|
+
Returns
|
|
4306
|
+
-------
|
|
4307
|
+
disk_absence_registry : `torch.Tensor` (`bool`, ndim=1)
|
|
4308
|
+
The attribute
|
|
4309
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`.
|
|
4310
|
+
|
|
4311
|
+
"""
|
|
4312
|
+
params = {"deep_copy": deep_copy}
|
|
4313
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
4314
|
+
|
|
4315
|
+
if self._disk_absence_registry is None:
|
|
4316
|
+
method_name = ("_calc_disk_absence_registry"
|
|
4317
|
+
"_and_cache_select_intermediates")
|
|
4318
|
+
method_alias = getattr(self, method_name)
|
|
4319
|
+
self._disk_absence_registry = method_alias()
|
|
4320
|
+
|
|
4321
|
+
disk_absence_registry = (self._disk_absence_registry.detach().clone()
|
|
4322
|
+
if (deep_copy == True)
|
|
4323
|
+
else self._disk_absence_registry)
|
|
4324
|
+
|
|
4325
|
+
return disk_absence_registry
|
|
4326
|
+
|
|
4327
|
+
|
|
4328
|
+
|
|
4329
|
+
def _calc_disk_absence_registry_and_cache_select_intermediates(self):
|
|
4330
|
+
num_disks = self._num_disks
|
|
4331
|
+
|
|
4332
|
+
if num_disks > 0:
|
|
4333
|
+
if self._disk_supports is None:
|
|
4334
|
+
method_name = "_calc_disk_supports"
|
|
4335
|
+
method_alias = getattr(self, method_name)
|
|
4336
|
+
self._disk_supports = method_alias()
|
|
4337
|
+
disk_supports = self._disk_supports
|
|
4338
|
+
|
|
4339
|
+
disk_absence_registry = (disk_supports.sum(dim=(1, 2)) == 0)
|
|
4340
|
+
else:
|
|
4341
|
+
disk_absence_registry = torch.zeros((num_disks,),
|
|
4342
|
+
device=self._device,
|
|
4343
|
+
dtype=torch.bool)
|
|
4344
|
+
|
|
4345
|
+
return disk_absence_registry
|
|
4346
|
+
|
|
4347
|
+
|
|
4348
|
+
|
|
4349
|
+
@property
|
|
4350
|
+
def disk_absence_registry(self):
|
|
4351
|
+
r"""`torch.Tensor`: The disk absence registry of the cropped fake CBED
|
|
4352
|
+
pattern.
|
|
4353
|
+
|
|
4354
|
+
See the summary documentation for the classes
|
|
4355
|
+
:class:`fakecbed.discretized.CBEDPattern`, and
|
|
4356
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
|
|
4357
|
+
|
|
4358
|
+
Let ``num_disks``, and ``disk_supports`` denote the attributes
|
|
4359
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`, and
|
|
4360
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`
|
|
4361
|
+
respectively. Moreover, let ``k`` be a nonnegative integer less than
|
|
4362
|
+
``num_disks``.
|
|
4363
|
+
|
|
4364
|
+
``disk_absence_registry`` is a one-dimensional PyTorch tensor of length
|
|
4365
|
+
equal to ``num_disks``. If ``disk_absence_registry[k]`` is equal to
|
|
4366
|
+
``False``, then the image stored in ``disk_supports[k]`` has at least
|
|
4367
|
+
one nonzero pixel. Otherwise, if ``disk_absence_registry[k]`` is equal
|
|
4368
|
+
to ``True``, then the opposite of the above scenario is true.
|
|
4369
|
+
|
|
4370
|
+
Note that ``disk_absence_registry`` should be considered **read-only**.
|
|
4371
|
+
|
|
4372
|
+
"""
|
|
4373
|
+
result = self.get_disk_absence_registry(deep_copy=True)
|
|
4374
|
+
|
|
4375
|
+
return result
|
|
4376
|
+
|
|
4377
|
+
|
|
4378
|
+
|
|
4379
|
+
@property
|
|
4380
|
+
def principal_disk_is_clipped(self):
|
|
4381
|
+
r"""`bool`: Equals ``True`` if the "principal" CBED disk either does not
|
|
4382
|
+
exists, or it exists and is clipped in image of the cropped fake CBED
|
|
4383
|
+
pattern.
|
|
4384
|
+
|
|
4385
|
+
See the summary documentation for the class
|
|
4386
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context,
|
|
4387
|
+
as well as the documentation for the attribute
|
|
4388
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`.
|
|
4389
|
+
|
|
4390
|
+
Let ``core_attrs``, ``num_disks``, and ``disk_clipping_registry`` denote
|
|
4391
|
+
the attributes :attr:`~fancytypes.Checkable.core_attrs`,
|
|
4392
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`, and
|
|
4393
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`
|
|
4394
|
+
respectively.
|
|
4395
|
+
|
|
4396
|
+
``principal_disk_is_clipped`` is calculated effectively by:
|
|
4397
|
+
|
|
4398
|
+
.. code-block:: python
|
|
4399
|
+
|
|
4400
|
+
principal_disk_idx = \
|
|
4401
|
+
core_attrs["principal_disk_idx"]
|
|
4402
|
+
principal_disk_is_clipped = \
|
|
4403
|
+
(disk_clipping_registry[principal_disk_idx].item()
|
|
4404
|
+
if (principal_disk_idx < num_disks)
|
|
4405
|
+
else True)
|
|
4406
|
+
|
|
4407
|
+
Note that ``principal_disk_is_clipped`` should be considered
|
|
4408
|
+
**read-only**.
|
|
4409
|
+
|
|
4410
|
+
"""
|
|
4411
|
+
if self._principal_disk_is_clipped is None:
|
|
4412
|
+
method_name = ("_calc_principal_disk_is_clipped"
|
|
4413
|
+
"_and_cache_select_intermediates")
|
|
4414
|
+
method_alias = getattr(self, method_name)
|
|
4415
|
+
self._principal_disk_is_clipped = method_alias()
|
|
4416
|
+
|
|
4417
|
+
result = self._principal_disk_is_clipped
|
|
4418
|
+
|
|
4419
|
+
return result
|
|
4420
|
+
|
|
4421
|
+
|
|
4422
|
+
|
|
4423
|
+
def _calc_principal_disk_is_clipped_and_cache_select_intermediates(self):
|
|
4424
|
+
principal_disk_idx = self._principal_disk_idx
|
|
4425
|
+
num_disks = self._num_disks
|
|
4426
|
+
|
|
4427
|
+
if principal_disk_idx < num_disks:
|
|
4428
|
+
if self._disk_clipping_registry is None:
|
|
4429
|
+
method_name = ("_calc_disk_clipping_registry"
|
|
4430
|
+
"_and_cache_select_intermediates")
|
|
4431
|
+
method_alias = getattr(self, method_name)
|
|
4432
|
+
self._disk_clipping_registry = method_alias()
|
|
4433
|
+
disk_clipping_registry = self._disk_clipping_registry
|
|
4434
|
+
|
|
4435
|
+
disk_idx = principal_disk_idx
|
|
4436
|
+
principal_disk_is_clipped = disk_clipping_registry[disk_idx].item()
|
|
4437
|
+
else:
|
|
4438
|
+
principal_disk_is_clipped = True
|
|
4439
|
+
|
|
4440
|
+
return principal_disk_is_clipped
|
|
4441
|
+
|
|
4442
|
+
|
|
4443
|
+
|
|
4444
|
+
@property
|
|
4445
|
+
def principal_disk_is_absent(self):
|
|
4446
|
+
r"""`bool`: Equals ``True`` if the "principal" CBED disk either does not
|
|
4447
|
+
exists, or it exists and is absent from the image of the cropped fake
|
|
4448
|
+
CBED pattern.
|
|
4449
|
+
|
|
4450
|
+
See the summary documentation for the class
|
|
4451
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context,
|
|
4452
|
+
as well as the documentation for the attribute
|
|
4453
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`.
|
|
4454
|
+
|
|
4455
|
+
Let ``core_attrs``, ``num_disks``, and ``disk_absence_registry`` denote
|
|
4456
|
+
the attributes :attr:`~fancytypes.Checkable.core_attrs`,
|
|
4457
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`, and
|
|
4458
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`
|
|
4459
|
+
respectively.
|
|
4460
|
+
|
|
4461
|
+
``principal_disk_is_absent`` is calculated effectively by:
|
|
4462
|
+
|
|
4463
|
+
.. code-block:: python
|
|
4464
|
+
|
|
4465
|
+
principal_disk_idx = \
|
|
4466
|
+
core_attrs["principal_disk_idx"]
|
|
4467
|
+
principal_disk_is_absent = \
|
|
4468
|
+
(disk_absence_registry[principal_disk_idx].item()
|
|
4469
|
+
if (principal_disk_idx < num_disks)
|
|
4470
|
+
else True)
|
|
4471
|
+
|
|
4472
|
+
Note that ``principal_disk_is_absent`` should be considered
|
|
4473
|
+
**read-only**.
|
|
4474
|
+
|
|
4475
|
+
"""
|
|
4476
|
+
if self._principal_disk_is_absent is None:
|
|
4477
|
+
method_name = ("_calc_principal_disk_is_absent"
|
|
4478
|
+
"_and_cache_select_intermediates")
|
|
4479
|
+
method_alias = getattr(self, method_name)
|
|
4480
|
+
self._principal_disk_is_absent = method_alias()
|
|
4481
|
+
|
|
4482
|
+
result = self._principal_disk_is_absent
|
|
4483
|
+
|
|
4484
|
+
return result
|
|
4485
|
+
|
|
4486
|
+
|
|
4487
|
+
|
|
4488
|
+
def _calc_principal_disk_is_absent_and_cache_select_intermediates(self):
|
|
4489
|
+
principal_disk_idx = self._principal_disk_idx
|
|
4490
|
+
num_disks = self._num_disks
|
|
4491
|
+
|
|
4492
|
+
if principal_disk_idx < num_disks:
|
|
4493
|
+
if self._disk_absence_registry is None:
|
|
4494
|
+
method_name = ("_calc_disk_absence_registry"
|
|
4495
|
+
"_and_cache_select_intermediates")
|
|
4496
|
+
method_alias = getattr(self, method_name)
|
|
4497
|
+
self._disk_absence_registry = method_alias()
|
|
4498
|
+
disk_absence_registry = self._disk_absence_registry
|
|
4499
|
+
|
|
4500
|
+
disk_idx = principal_disk_idx
|
|
4501
|
+
principal_disk_is_absent = disk_absence_registry[disk_idx].item()
|
|
4502
|
+
else:
|
|
4503
|
+
principal_disk_is_absent = True
|
|
4504
|
+
|
|
4505
|
+
return principal_disk_is_absent
|
|
4506
|
+
|
|
4507
|
+
|
|
4508
|
+
|
|
4509
|
+
def get_disk_overlap_map(self, deep_copy=_default_deep_copy):
|
|
4510
|
+
r"""Return the image of the disk overlap map of the cropped fake CBED
|
|
4511
|
+
pattern.
|
|
4512
|
+
|
|
4513
|
+
Parameters
|
|
4514
|
+
----------
|
|
4515
|
+
deep_copy : `bool`, optional
|
|
4516
|
+
Let ``disk_overlap_map`` denote the attribute
|
|
4517
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_overlap_map`.
|
|
4518
|
+
|
|
4519
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
4520
|
+
``disk_overlap_map`` is returned. Otherwise, a reference to
|
|
4521
|
+
``disk_overlap_map`` is returned.
|
|
4522
|
+
|
|
4523
|
+
Returns
|
|
4524
|
+
-------
|
|
4525
|
+
disk_overlap_map : `torch.Tensor` (`int`, ndim=2)
|
|
4526
|
+
The attribute
|
|
4527
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_overlap_map`.
|
|
4528
|
+
|
|
4529
|
+
"""
|
|
4530
|
+
params = {"deep_copy": deep_copy}
|
|
4531
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
4532
|
+
|
|
4533
|
+
if self._disk_overlap_map is None:
|
|
4534
|
+
method_name = "_calc_disk_overlap_map"
|
|
4535
|
+
method_alias = getattr(self, method_name)
|
|
4536
|
+
self._disk_overlap_map = method_alias()
|
|
4537
|
+
|
|
4538
|
+
disk_overlap_map = (self._disk_overlap_map.detach().clone()
|
|
4539
|
+
if (deep_copy == True)
|
|
4540
|
+
else self._disk_overlap_map)
|
|
4541
|
+
|
|
4542
|
+
return disk_overlap_map
|
|
4543
|
+
|
|
4544
|
+
|
|
4545
|
+
|
|
4546
|
+
def _calc_disk_overlap_map(self):
|
|
4547
|
+
uncropped_cbed_pattern_disk_overlap_map = \
|
|
4548
|
+
self._cbed_pattern.get_disk_overlap_map(deep_copy=False)
|
|
4549
|
+
uncropped_cbed_pattern_disk_overlap_map = \
|
|
4550
|
+
uncropped_cbed_pattern_disk_overlap_map.cpu().detach().clone()
|
|
4551
|
+
|
|
4552
|
+
signal_to_crop = \
|
|
4553
|
+
self._generate_uncropped_blank_signal()
|
|
4554
|
+
signal_to_crop.data[2] = \
|
|
4555
|
+
uncropped_cbed_pattern_disk_overlap_map.numpy(force=True)
|
|
4556
|
+
|
|
4557
|
+
optional_params = self._generate_optional_cropping_params()
|
|
4558
|
+
|
|
4559
|
+
kwargs = {"input_signal": signal_to_crop,
|
|
4560
|
+
"optional_params": optional_params}
|
|
4561
|
+
cropped_signal = empix.crop(**kwargs)
|
|
4562
|
+
|
|
4563
|
+
N_W_h, N_W_v = self._cropping_window_dims_in_pixels
|
|
4564
|
+
L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
|
|
4565
|
+
|
|
4566
|
+
disk_overlap_map = cropped_signal.data[2]
|
|
4567
|
+
disk_overlap_map_dtype = uncropped_cbed_pattern_disk_overlap_map.dtype
|
|
4568
|
+
disk_overlap_map = torch.from_numpy(disk_overlap_map)
|
|
4569
|
+
disk_overlap_map = disk_overlap_map.to(device=self._device,
|
|
4570
|
+
dtype=disk_overlap_map_dtype)
|
|
4571
|
+
disk_overlap_map[:T, :] = 0
|
|
4572
|
+
disk_overlap_map[max(N_W_v-B, 0):, :] = 0
|
|
4573
|
+
disk_overlap_map[:, :L] = 0
|
|
4574
|
+
disk_overlap_map[:, max(N_W_h-R, 0):] = 0
|
|
4575
|
+
|
|
4576
|
+
return disk_overlap_map
|
|
4577
|
+
|
|
4578
|
+
|
|
4579
|
+
|
|
4580
|
+
@property
|
|
4581
|
+
def disk_overlap_map(self):
|
|
4582
|
+
r"""`torch.Tensor`: The image of the disk overlap map of the cropped
|
|
4583
|
+
fake CBED pattern.
|
|
4584
|
+
|
|
4585
|
+
See the documentation for the attribute
|
|
4586
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal` for additional
|
|
4587
|
+
context.
|
|
4588
|
+
|
|
4589
|
+
Let ``signal``, ``device``, and ``core_attrs`` denote the attributes
|
|
4590
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal`,
|
|
4591
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.device`, and
|
|
4592
|
+
:attr:`~fancytypes.Checkable.core_attrs` respectively.
|
|
4593
|
+
|
|
4594
|
+
``disk_overlap_map`` is calculated effectively by:
|
|
4595
|
+
|
|
4596
|
+
.. code-block:: python
|
|
4597
|
+
|
|
4598
|
+
N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
|
|
4599
|
+
L, R, B, T = core_attrs["mask_frame"]
|
|
4600
|
+
|
|
4601
|
+
disk_overlap_map = signal.data[2]
|
|
4602
|
+
disk_overlap_map = torch.from_numpy(disk_overlap_map)
|
|
4603
|
+
disk_overlap_map = disk_overlap_map.to(device=device, dtype=int)
|
|
4604
|
+
disk_overlap_map[:T, :] = 0
|
|
4605
|
+
disk_overlap_map[max(N_W_v-B, 0):, :] = 0
|
|
4606
|
+
disk_overlap_map[:, :L] = 0
|
|
4607
|
+
disk_overlap_map[:, max(N_W_h-R, 0):] = 0
|
|
4608
|
+
|
|
4609
|
+
Note that ``disk_overlap_map`` should be considered **read-only**.
|
|
4610
|
+
|
|
4611
|
+
"""
|
|
4612
|
+
result = self.get_disk_overlap_map(deep_copy=True)
|
|
4613
|
+
|
|
4614
|
+
return result
|
|
4615
|
+
|
|
4616
|
+
|
|
4617
|
+
|
|
4618
|
+
@property
|
|
4619
|
+
def principal_disk_is_overlapping(self):
|
|
4620
|
+
r"""`bool`: Equals ``True`` if the "principal" CBED disk exists and is
|
|
4621
|
+
overlapping with another CBED disk in the image of the cropped fake CBED
|
|
4622
|
+
pattern.
|
|
4623
|
+
|
|
4624
|
+
See the summary documentation for the class
|
|
4625
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context,
|
|
4626
|
+
as well as the documentation for the attribute
|
|
4627
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_overlap_map`.
|
|
4628
|
+
|
|
4629
|
+
Let ``core_attrs``, ``num_disks``, ``disk_supports``, and
|
|
4630
|
+
``disk_overlap_map`` denote the attributes
|
|
4631
|
+
:attr:`~fancytypes.Checkable.core_attrs`,
|
|
4632
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`,
|
|
4633
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`, and
|
|
4634
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_overlap_map`
|
|
4635
|
+
respectively.
|
|
4636
|
+
|
|
4637
|
+
``principal_disk_is_overlapping`` is calculated effectively by:
|
|
4638
|
+
|
|
4639
|
+
.. code-block:: python
|
|
4640
|
+
|
|
4641
|
+
principal_disk_idx = core_attrs["principal_disk_idx"]
|
|
4642
|
+
|
|
4643
|
+
principal_disk_support = (disk_supports[principal_disk_idx]
|
|
4644
|
+
if (principal_disk_idx < num_disks)
|
|
4645
|
+
else torch.zeros_like(disk_overlap_map))
|
|
4646
|
+
|
|
4647
|
+
principal_disk_is_overlapping = \
|
|
4648
|
+
torch.any(disk_overlap_map*principal_disk_support > 1.5).item()
|
|
4649
|
+
|
|
4650
|
+
Note that ``principal_disk_is_overlapping`` should be considered
|
|
4651
|
+
**read-only**.
|
|
4652
|
+
|
|
4653
|
+
"""
|
|
4654
|
+
if self._principal_disk_is_overlapping is None:
|
|
4655
|
+
method_name = ("_calc_principal_disk_is_overlapping"
|
|
4656
|
+
"_and_cache_select_intermediates")
|
|
4657
|
+
method_alias = getattr(self, method_name)
|
|
4658
|
+
self._principal_disk_is_overlapping = method_alias()
|
|
4659
|
+
|
|
4660
|
+
result = self._principal_disk_is_overlapping
|
|
4661
|
+
|
|
4662
|
+
return result
|
|
4663
|
+
|
|
4664
|
+
|
|
4665
|
+
|
|
4666
|
+
def _calc_principal_disk_is_overlapping_and_cache_select_intermediates(
|
|
4667
|
+
self):
|
|
4668
|
+
principal_disk_idx = self._principal_disk_idx
|
|
4669
|
+
num_disks = self._num_disks
|
|
4670
|
+
|
|
4671
|
+
if self._disk_supports is None:
|
|
4672
|
+
method_name = "_calc_disk_supports"
|
|
4673
|
+
method_alias = getattr(self, method_name)
|
|
4674
|
+
self._disk_supports = method_alias()
|
|
4675
|
+
disk_supports = self._disk_supports
|
|
4676
|
+
|
|
4677
|
+
if self._disk_overlap_map is None:
|
|
4678
|
+
method_name = "_calc_disk_overlap_map"
|
|
4679
|
+
method_alias = getattr(self, method_name)
|
|
4680
|
+
self._disk_overlap_map = method_alias()
|
|
4681
|
+
disk_overlap_map = self._disk_overlap_map
|
|
4682
|
+
|
|
4683
|
+
principal_disk_support = (disk_supports[principal_disk_idx]
|
|
4684
|
+
if (principal_disk_idx < num_disks)
|
|
4685
|
+
else torch.zeros_like(disk_overlap_map))
|
|
4686
|
+
|
|
4687
|
+
principal_disk_is_overlapping = \
|
|
4688
|
+
torch.any(disk_overlap_map*principal_disk_support > 1.5).item()
|
|
4689
|
+
|
|
4690
|
+
return principal_disk_is_overlapping
|
|
4691
|
+
|
|
4692
|
+
|
|
4693
|
+
|
|
4694
|
+
def get_signal(self, deep_copy=_default_deep_copy):
|
|
4695
|
+
r"""Return the hyperspy signal representation of the cropped fake CBED
|
|
4696
|
+
pattern.
|
|
4697
|
+
|
|
4698
|
+
Parameters
|
|
4699
|
+
----------
|
|
4700
|
+
deep_copy : `bool`, optional
|
|
4701
|
+
Let ``signal`` denote the attribute
|
|
4702
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal`.
|
|
4703
|
+
|
|
4704
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of ``signal``
|
|
4705
|
+
is returned. Otherwise, a reference to ``signal`` is returned.
|
|
4706
|
+
|
|
4707
|
+
Returns
|
|
4708
|
+
-------
|
|
4709
|
+
signal : :class:`hyperspy._signals.signal2d.Signal2D`
|
|
4710
|
+
The attribute
|
|
4711
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal`.
|
|
4712
|
+
|
|
4713
|
+
"""
|
|
4714
|
+
params = {"deep_copy": deep_copy}
|
|
4715
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
4716
|
+
|
|
4717
|
+
if self._signal is None:
|
|
4718
|
+
method_name = "_calc_signal_and_cache_select_intermediates"
|
|
4719
|
+
method_alias = getattr(self, method_name)
|
|
4720
|
+
self._signal = method_alias()
|
|
4721
|
+
|
|
4722
|
+
signal = (copy.deepcopy(self._signal)
|
|
4723
|
+
if (deep_copy == True)
|
|
4724
|
+
else self._signal)
|
|
4725
|
+
|
|
4726
|
+
return signal
|
|
4727
|
+
|
|
4728
|
+
|
|
4729
|
+
|
|
4730
|
+
def _calc_signal_and_cache_select_intermediates(self):
|
|
4731
|
+
method_name = "_calc_signal_metadata_and_cache_select_intermediates"
|
|
4732
|
+
method_alias = getattr(self, method_name)
|
|
4733
|
+
signal_metadata = method_alias()
|
|
4734
|
+
|
|
4735
|
+
optional_params = self._generate_optional_cropping_params()
|
|
4736
|
+
|
|
4737
|
+
kwargs = {"input_signal": \
|
|
4738
|
+
self._cbed_pattern.get_signal(deep_copy=False),
|
|
4739
|
+
"optional_params": \
|
|
4740
|
+
optional_params}
|
|
4741
|
+
signal_data = empix.crop(**kwargs).data
|
|
4742
|
+
|
|
4743
|
+
N_W_h, N_W_v = self._cropping_window_dims_in_pixels
|
|
4744
|
+
L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
|
|
4745
|
+
|
|
4746
|
+
signal_data[:, :T, :] = 0
|
|
4747
|
+
signal_data[:, max(N_W_v-B, 0):, :] = 0
|
|
4748
|
+
signal_data[:, :, :L] = 0
|
|
4749
|
+
signal_data[:, :, max(N_W_h-R, 0):] = 0
|
|
4750
|
+
|
|
4751
|
+
matrix_to_normalize = torch.from_numpy(signal_data[0])
|
|
4752
|
+
kwargs = {"input_matrix": matrix_to_normalize.to(device=self._device)}
|
|
4753
|
+
normalized_matrix = self._cbed_pattern._normalize_matrix(**kwargs)
|
|
4754
|
+
|
|
4755
|
+
signal_data[0] = \
|
|
4756
|
+
normalized_matrix.cpu().detach().clone().numpy(force=True)
|
|
4757
|
+
|
|
4758
|
+
signal = \
|
|
4759
|
+
hyperspy.signals.Signal2D(data=signal_data,
|
|
4760
|
+
metadata=signal_metadata)
|
|
4761
|
+
signal.axes_manager = \
|
|
4762
|
+
self._generate_cropped_blank_signal().axes_manager
|
|
4763
|
+
|
|
4764
|
+
signal.axes_manager[0].name = \
|
|
4765
|
+
"cropped fake CBED pattern attribute"
|
|
4766
|
+
signal.axes_manager[1].name = \
|
|
4767
|
+
"fractional horizontal coordinate of uncropped fake CBED pattern"
|
|
4768
|
+
signal.axes_manager[2].name = \
|
|
4769
|
+
"fractional vertical coordinate of uncropped fake CBED pattern"
|
|
4770
|
+
|
|
4771
|
+
return signal
|
|
4772
|
+
|
|
4773
|
+
|
|
4774
|
+
|
|
4775
|
+
def _calc_signal_metadata_and_cache_select_intermediates(self):
|
|
4776
|
+
cbed_pattern_signal = self._cbed_pattern.get_signal(deep_copy=False)
|
|
4777
|
+
|
|
4778
|
+
title = "Cropped " + cbed_pattern_signal.metadata.General.title
|
|
4779
|
+
|
|
4780
|
+
pre_serialized_core_attrs = self.pre_serialize()
|
|
4781
|
+
|
|
4782
|
+
attr_name_subset = ("_principal_disk_analysis_results",
|
|
4783
|
+
"_disk_clipping_registry",
|
|
4784
|
+
"_disk_absence_registry",
|
|
4785
|
+
"_principal_disk_is_clipped",
|
|
4786
|
+
"_principal_disk_is_absent",
|
|
4787
|
+
"_principal_disk_is_overlapping")
|
|
4788
|
+
for attr_name in attr_name_subset:
|
|
4789
|
+
attr = getattr(self, attr_name)
|
|
4790
|
+
if attr is None:
|
|
4791
|
+
method_name = ("_calc{}_and_cache_select"
|
|
4792
|
+
"_intermediates").format(attr_name)
|
|
4793
|
+
method_alias = getattr(self, method_name)
|
|
4794
|
+
attr = method_alias()
|
|
4795
|
+
setattr(self, attr_name, attr)
|
|
4796
|
+
|
|
4797
|
+
fakecbed_metadata = \
|
|
4798
|
+
cbed_pattern_signal.metadata.FakeCBED.as_dictionary()
|
|
4799
|
+
fakecbed_metadata["pre_serialized_core_attrs"] = \
|
|
4800
|
+
pre_serialized_core_attrs
|
|
4801
|
+
|
|
4802
|
+
attr_name_subset += ("_principal_disk_bounding_box"
|
|
4803
|
+
"_in_uncropped_image_fractional_coords",
|
|
4804
|
+
"_principal_disk_bounding_box"
|
|
4805
|
+
"_in_cropped_image_fractional_coords",
|
|
4806
|
+
"_principal_disk_boundary_pts"
|
|
4807
|
+
"_in_uncropped_image_fractional_coords",
|
|
4808
|
+
"_principal_disk_boundary_pts"
|
|
4809
|
+
"_in_cropped_image_fractional_coords")
|
|
4810
|
+
for attr_name in attr_name_subset[1:]:
|
|
4811
|
+
key = attr_name[1:]
|
|
4812
|
+
attr = getattr(self, attr_name)
|
|
4813
|
+
metadata_item = self._convert_attr_to_metadata_item(attr)
|
|
4814
|
+
fakecbed_metadata[key] = metadata_item
|
|
4815
|
+
|
|
4816
|
+
signal_metadata = {"General": {"title": title},
|
|
4817
|
+
"Signal": {"pixel value units": "dimensionless"},
|
|
4818
|
+
"FakeCBED": fakecbed_metadata}
|
|
4819
|
+
|
|
4820
|
+
return signal_metadata
|
|
4821
|
+
|
|
4822
|
+
|
|
4823
|
+
|
|
4824
|
+
def _convert_attr_to_metadata_item(self, attr):
|
|
4825
|
+
if isinstance(attr, (tuple, bool)):
|
|
4826
|
+
metadata_item = attr
|
|
4827
|
+
else:
|
|
4828
|
+
if len(attr.shape) == 1:
|
|
4829
|
+
metadata_item = tuple(elem
|
|
4830
|
+
for elem
|
|
4831
|
+
in attr.cpu().detach().clone().tolist())
|
|
4832
|
+
else:
|
|
4833
|
+
metadata_item = tuple(tuple(pt)
|
|
4834
|
+
for pt
|
|
4835
|
+
in attr.cpu().detach().clone().tolist())
|
|
4836
|
+
|
|
4837
|
+
return metadata_item
|
|
4838
|
+
|
|
4839
|
+
|
|
4840
|
+
|
|
4841
|
+
@property
|
|
4842
|
+
def signal(self):
|
|
4843
|
+
r"""`hyperspy._signals.signal2d.Signal2D`: The hyperspy signal
|
|
4844
|
+
representation of the cropped fake CBED pattern.
|
|
4845
|
+
|
|
4846
|
+
See the summary documentation for the classes
|
|
4847
|
+
:class:`fakecbed.discretized.CBEDPattern` and
|
|
4848
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
|
|
4849
|
+
|
|
4850
|
+
Let ``disk_clipping_registry``, ``disk_absence_registry``,
|
|
4851
|
+
``principal_disk_is_clipped``, ``principal_disk_is_absent``,
|
|
4852
|
+
``principal_disk_is_overlapping``,
|
|
4853
|
+
``principal_disk_bounding_box_in_uncropped_image_fractional_coords``,
|
|
4854
|
+
``principal_disk_bounding_box_in_cropped_image_fractional_coords``,
|
|
4855
|
+
``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``,
|
|
4856
|
+
``principal_disk_boundary_pts_in_cropped_image_fractional_coords``, and
|
|
4857
|
+
``core_attrs`` denote the attributes
|
|
4858
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`,
|
|
4859
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`,
|
|
4860
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_is_clipped`,
|
|
4861
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_is_absent`,
|
|
4862
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_is_overlapping`,
|
|
4863
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_uncropped_image_fractional_coords`,
|
|
4864
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_cropped_image_fractional_coords`,
|
|
4865
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`,
|
|
4866
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`,
|
|
4867
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively. Furthermore,
|
|
4868
|
+
let ``pre_serialize`` denote the method
|
|
4869
|
+
:meth:`~fancytypes.PreSerializable.pre_serialize`.
|
|
4870
|
+
|
|
4871
|
+
``signal`` is calculated effectively by:
|
|
4872
|
+
|
|
4873
|
+
.. code-block:: python
|
|
4874
|
+
|
|
4875
|
+
import empix
|
|
4876
|
+
import numpy as np
|
|
4877
|
+
|
|
4878
|
+
cbed_pattern = core_attrs["cbed_pattern"]
|
|
4879
|
+
N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
|
|
4880
|
+
L, R, B, T = core_attrs["mask_frame"]
|
|
4881
|
+
|
|
4882
|
+
cbed_pattern_signal = cbed_pattern.signal
|
|
4883
|
+
|
|
4884
|
+
title = "Cropped " + cbed_pattern_signal.metadata.General.title
|
|
4885
|
+
|
|
4886
|
+
cropping_window_center = \
|
|
4887
|
+
core_attrs["cropping_window_center"]
|
|
4888
|
+
cropping_window_dims_in_pixels = \
|
|
4889
|
+
core_attrs["cropping_window_dims_in_pixels"]
|
|
4890
|
+
|
|
4891
|
+
kwargs = {"center": cropping_window_center,
|
|
4892
|
+
"window_dims": cropping_window_dims_in_pixels,
|
|
4893
|
+
"pad_mode": "zeros",
|
|
4894
|
+
"apply_symmetric_mask": False}
|
|
4895
|
+
optional_params = empix.OptionalCroppingParams(**kwargs)
|
|
4896
|
+
|
|
4897
|
+
kwargs = {"input_signal": cbed_pattern_signal,
|
|
4898
|
+
"optional_params": optional_params}
|
|
4899
|
+
signal = empix.crop(**kwargs)
|
|
4900
|
+
|
|
4901
|
+
signal.data[:, :T, :] = 0
|
|
4902
|
+
signal.data[:, max(N_W_v-B, 0):, :] = 0
|
|
4903
|
+
signal.data[:, :, :L] = 0
|
|
4904
|
+
signal.data[:, :, max(N_W_h-R, 0):] = 0
|
|
4905
|
+
|
|
4906
|
+
# Normalize ``signal.data[0]``.
|
|
4907
|
+
matrix = signal.data[0]
|
|
4908
|
+
if matrix.max()-matrix.min() > 0:
|
|
4909
|
+
normalization_weight = 1 / (matrix.max()-matrix.min())
|
|
4910
|
+
normalization_bias = -normalization_weight*matrix.min()
|
|
4911
|
+
matrix = (matrix*normalization_weight
|
|
4912
|
+
+ normalization_bias).clip(min=0, max=1)
|
|
4913
|
+
else:
|
|
4914
|
+
matrix = np.zeros_like(matrix)
|
|
4915
|
+
signal.data[0] = matrix
|
|
4916
|
+
|
|
4917
|
+
kwargs = {"item_path": "General.title", "value": title}
|
|
4918
|
+
signal.metadata.set_item(**kwargs)
|
|
4919
|
+
|
|
4920
|
+
kwargs["item_path"] = "FakeCBED.pre_serialized_core_attrs"
|
|
4921
|
+
kwargs["value"] = pre_serialize()
|
|
4922
|
+
signal.metadata.set_item(**kwargs)
|
|
4923
|
+
|
|
4924
|
+
def _convert_attr_to_metadata_item(attr):
|
|
4925
|
+
if isinstance(attr, (tuple, bool)):
|
|
4926
|
+
metadata_item = \
|
|
4927
|
+
attr
|
|
4928
|
+
else:
|
|
4929
|
+
if len(attr.shape) == 1:
|
|
4930
|
+
metadata_item = \
|
|
4931
|
+
tuple(elem
|
|
4932
|
+
for elem
|
|
4933
|
+
in attr.cpu().detach().clone().tolist())
|
|
4934
|
+
else:
|
|
4935
|
+
metadata_item = \
|
|
4936
|
+
tuple(tuple(pt)
|
|
4937
|
+
for pt
|
|
4938
|
+
in attr.cpu().detach().clone().tolist())
|
|
4939
|
+
|
|
4940
|
+
return metadata_item
|
|
4941
|
+
|
|
4942
|
+
attr = disk_clipping_registry
|
|
4943
|
+
kwargs["item_path"] = "FakeCBED.disk_clipping_registry"
|
|
4944
|
+
kwargs["value"] = _convert_attr_to_metadata_item(attr)
|
|
4945
|
+
signal.metadata.set_item(**kwargs)
|
|
4946
|
+
|
|
4947
|
+
attr = disk_absence_registry
|
|
4948
|
+
kwargs["item_path"] = "FakeCBED.disk_absence_registry"
|
|
4949
|
+
kwargs["value"] = _convert_attr_to_metadata_item(attr)
|
|
4950
|
+
signal.metadata.set_item(**kwargs)
|
|
4951
|
+
|
|
4952
|
+
attr = principal_disk_is_clipped
|
|
4953
|
+
kwargs["item_path"] = "FakeCBED.principal_disk_is_clipped"
|
|
4954
|
+
kwargs["value"] = _convert_attr_to_metadata_item(attr)
|
|
4955
|
+
signal.metadata.set_item(**kwargs)
|
|
4956
|
+
|
|
4957
|
+
attr = principal_disk_is_absent
|
|
4958
|
+
kwargs["item_path"] = "FakeCBED.principal_disk_is_absent"
|
|
4959
|
+
kwargs["value"] = _convert_attr_to_metadata_item(attr)
|
|
4960
|
+
signal.metadata.set_item(**kwargs)
|
|
4961
|
+
|
|
4962
|
+
attr = principal_disk_is_overlapping
|
|
4963
|
+
kwargs["item_path"] = "FakeCBED.principal_disk_is_overlapping"
|
|
4964
|
+
kwargs["value"] = _convert_attr_to_metadata_item(attr)
|
|
4965
|
+
signal.metadata.set_item(**kwargs)
|
|
4966
|
+
|
|
4967
|
+
attr = \
|
|
4968
|
+
principal_disk_bounding_box_in_uncropped_image_fractional_coords
|
|
4969
|
+
kwargs["item_path"] = \
|
|
4970
|
+
("FakeCBED.principal_disk_bounding_box"
|
|
4971
|
+
"_in_uncropped_image_fractional_coords")
|
|
4972
|
+
kwargs["value"] = \
|
|
4973
|
+
_convert_attr_to_metadata_item(attr)
|
|
4974
|
+
_ = \
|
|
4975
|
+
signal.metadata.set_item(**kwargs)
|
|
4976
|
+
|
|
4977
|
+
attr = \
|
|
4978
|
+
principal_disk_bounding_box_in_cropped_image_fractional_coords
|
|
4979
|
+
kwargs["item_path"] = \
|
|
4980
|
+
("FakeCBED.principal_disk_bounding_box"
|
|
4981
|
+
"_in_cropped_image_fractional_coords")
|
|
4982
|
+
kwargs["value"] = \
|
|
4983
|
+
_convert_attr_to_metadata_item(attr)
|
|
4984
|
+
_ = \
|
|
4985
|
+
signal.metadata.set_item(**kwargs)
|
|
4986
|
+
|
|
4987
|
+
attr = \
|
|
4988
|
+
principal_disk_boundary_pts_in_uncropped_image_fractional_coords
|
|
4989
|
+
kwargs["item_path"] = \
|
|
4990
|
+
("FakeCBED.principal_disk_boundary_pts"
|
|
4991
|
+
"_in_uncropped_image_fractional_coords")
|
|
4992
|
+
kwargs["value"] = \
|
|
4993
|
+
_convert_attr_to_metadata_item(attr)
|
|
4994
|
+
_ = \
|
|
4995
|
+
signal.metadata.set_item(**kwargs)
|
|
4996
|
+
|
|
4997
|
+
attr = \
|
|
4998
|
+
principal_disk_boundary_pts_in_cropped_image_fractional_coords
|
|
4999
|
+
kwargs["item_path"] = \
|
|
5000
|
+
("FakeCBED.principal_disk_boundary_pts"
|
|
5001
|
+
"_in_cropped_image_fractional_coords")
|
|
5002
|
+
kwargs["value"] = \
|
|
5003
|
+
_convert_attr_to_metadata_item(attr)
|
|
5004
|
+
_ = \
|
|
5005
|
+
signal.metadata.set_item(**kwargs)
|
|
5006
|
+
|
|
5007
|
+
signal.axes_manager[0].name = \
|
|
5008
|
+
"cropped fake CBED pattern attribute"
|
|
5009
|
+
signal.axes_manager[1].name = \
|
|
5010
|
+
"fractional horizontal coordinate of uncropped fake CBED pattern"
|
|
5011
|
+
signal.axes_manager[2].name = \
|
|
5012
|
+
"fractional vertical coordinate of uncropped fake CBED pattern"
|
|
5013
|
+
|
|
5014
|
+
Note that ``signal`` should be considered **read-only**.
|
|
5015
|
+
|
|
5016
|
+
"""
|
|
5017
|
+
result = self.get_signal(deep_copy=True)
|
|
5018
|
+
|
|
5019
|
+
return result
|
|
5020
|
+
|
|
5021
|
+
|
|
5022
|
+
|
|
5023
|
+
@property
|
|
5024
|
+
def image_has_been_overridden(self):
|
|
5025
|
+
r"""`bool`: Equals ``True`` if the image of the fake cropped CBED
|
|
5026
|
+
pattern has been overridden.
|
|
5027
|
+
|
|
5028
|
+
See the summary documentation for the classes
|
|
5029
|
+
:class:`fakecbed.discretized.CBEDPattern` and
|
|
5030
|
+
:class:`fakecbed.discretized.CroppedCBEDPattern` for additional context,
|
|
5031
|
+
as well as the documentation for the attribute
|
|
5032
|
+
:attr:`fakecbed.discretized.CBEDPattern.image_has_been_overridden`.
|
|
5033
|
+
|
|
5034
|
+
Let ``core_attrs`` denote the attribute
|
|
5035
|
+
:attr:`~fancytypes.Checkable.core_attrs`.
|
|
5036
|
+
|
|
5037
|
+
``image_has_been_overridden`` is calculated effectively by:
|
|
5038
|
+
|
|
5039
|
+
.. code-block:: python
|
|
5040
|
+
|
|
5041
|
+
cbed_pattern = core_attrs["cbed_pattern"]
|
|
5042
|
+
image_has_been_overridden = cbed_pattern.image_has_been_overridden
|
|
5043
|
+
|
|
5044
|
+
Note that ``image_has_been_overridden`` should be considered
|
|
5045
|
+
**read-only**.
|
|
5046
|
+
|
|
5047
|
+
"""
|
|
5048
|
+
result = self._image_has_been_overridden
|
|
5049
|
+
|
|
5050
|
+
return result
|
|
5051
|
+
|
|
5052
|
+
|
|
5053
|
+
|
|
5054
|
+
def get_image(self, deep_copy=_default_deep_copy):
|
|
5055
|
+
r"""Return the image of the cropped fake CBED pattern.
|
|
5056
|
+
|
|
5057
|
+
Parameters
|
|
5058
|
+
----------
|
|
5059
|
+
deep_copy : `bool`, optional
|
|
5060
|
+
Let ``image`` denote the attribute
|
|
5061
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.image`.
|
|
5062
|
+
|
|
5063
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of ``image``
|
|
5064
|
+
is returned. Otherwise, a reference to ``image`` is returned.
|
|
5065
|
+
|
|
5066
|
+
Returns
|
|
5067
|
+
-------
|
|
5068
|
+
image : `torch.Tensor` (`float`, ndim=2)
|
|
5069
|
+
The attribute :attr:`fakecbed.discretized.CroppedCBEDPattern.image`.
|
|
5070
|
+
|
|
5071
|
+
"""
|
|
5072
|
+
params = {"deep_copy": deep_copy}
|
|
5073
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
5074
|
+
|
|
5075
|
+
if self._image is None:
|
|
5076
|
+
method_name = "_calc_image"
|
|
5077
|
+
method_alias = getattr(self, method_name)
|
|
5078
|
+
self._image = method_alias()
|
|
5079
|
+
|
|
5080
|
+
image = (self._image.detach().clone()
|
|
5081
|
+
if (deep_copy == True)
|
|
5082
|
+
else self._image)
|
|
5083
|
+
|
|
5084
|
+
return image
|
|
5085
|
+
|
|
5086
|
+
|
|
5087
|
+
|
|
5088
|
+
def _calc_image(self):
|
|
5089
|
+
uncropped_cbed_pattern_image = \
|
|
5090
|
+
self._cbed_pattern.get_image(deep_copy=False)
|
|
5091
|
+
uncropped_cbed_pattern_image = \
|
|
5092
|
+
uncropped_cbed_pattern_image.cpu().detach().clone()
|
|
5093
|
+
|
|
5094
|
+
signal_to_crop = \
|
|
5095
|
+
self._generate_uncropped_blank_signal()
|
|
5096
|
+
signal_to_crop.data[0] = \
|
|
5097
|
+
uncropped_cbed_pattern_image.numpy(force=True)
|
|
5098
|
+
|
|
5099
|
+
optional_params = self._generate_optional_cropping_params()
|
|
5100
|
+
|
|
5101
|
+
kwargs = {"input_signal": signal_to_crop,
|
|
5102
|
+
"optional_params": optional_params}
|
|
5103
|
+
cropped_signal = empix.crop(**kwargs)
|
|
5104
|
+
|
|
5105
|
+
image = cropped_signal.data[0]
|
|
5106
|
+
image_dtype = uncropped_cbed_pattern_image.dtype
|
|
5107
|
+
image = torch.from_numpy(image)
|
|
5108
|
+
image = image.to(device=self._device, dtype=image_dtype)
|
|
5109
|
+
|
|
5110
|
+
N_W_h, N_W_v = self._cropping_window_dims_in_pixels
|
|
5111
|
+
L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
|
|
5112
|
+
|
|
5113
|
+
image[:T, :] = 0
|
|
5114
|
+
image[max(N_W_v-B, 0):, :] = 0
|
|
5115
|
+
image[:, :L] = 0
|
|
5116
|
+
image[:, max(N_W_h-R, 0):] = 0
|
|
5117
|
+
|
|
5118
|
+
kwargs = {"input_matrix": image}
|
|
5119
|
+
image = self._cbed_pattern._normalize_matrix(**kwargs)
|
|
5120
|
+
|
|
5121
|
+
return image
|
|
5122
|
+
|
|
5123
|
+
|
|
5124
|
+
|
|
5125
|
+
@property
|
|
5126
|
+
def image(self):
|
|
5127
|
+
r"""`torch.Tensor`: The image of the cropped fake CBED pattern.
|
|
5128
|
+
|
|
5129
|
+
See the documentation for the attribute
|
|
5130
|
+
:attr:`fakecbed.discretized.CBEDPattern.signal` for additional
|
|
5131
|
+
context.
|
|
5132
|
+
|
|
5133
|
+
Let ``signal``, ``device``, and ``core_attrs`` denote the attributes
|
|
5134
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal`,
|
|
5135
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.device`, and
|
|
5136
|
+
:attr:`~fancytypes.Checkable.core_attrs` respectively.
|
|
5137
|
+
|
|
5138
|
+
``image`` is calculated effectively by:
|
|
5139
|
+
|
|
5140
|
+
.. code-block:: python
|
|
5141
|
+
|
|
5142
|
+
N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
|
|
5143
|
+
L, R, B, T = core_attrs["mask_frame"]
|
|
5144
|
+
|
|
5145
|
+
image = signal.data[0]
|
|
5146
|
+
image_dtype = image.dtype
|
|
5147
|
+
image = torch.from_numpy(image)
|
|
5148
|
+
image = image.to(device=device, dtype=image_dtype)
|
|
5149
|
+
image[:T, :] = 0
|
|
5150
|
+
image[max(N_W_v-B, 0):, :] = 0
|
|
5151
|
+
image[:, :L] = 0
|
|
5152
|
+
image[:, max(N_W_h-R, 0):] = 0
|
|
5153
|
+
|
|
5154
|
+
Note that ``image`` should be considered **read-only**.
|
|
5155
|
+
|
|
5156
|
+
"""
|
|
5157
|
+
result = self.get_image(deep_copy=True)
|
|
5158
|
+
|
|
5159
|
+
return result
|
|
5160
|
+
|
|
5161
|
+
|
|
5162
|
+
|
|
5163
|
+
def get_illumination_support(self, deep_copy=_default_deep_copy):
|
|
5164
|
+
r"""Return the illumination support of the cropped fake CBED pattern.
|
|
5165
|
+
|
|
5166
|
+
Parameters
|
|
5167
|
+
----------
|
|
5168
|
+
deep_copy : `bool`, optional
|
|
5169
|
+
Let ``illumination_support`` denote the attribute
|
|
5170
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.illumination_support`.
|
|
5171
|
+
|
|
5172
|
+
If ``deep_copy`` is set to ``True``, then a deep copy of
|
|
5173
|
+
``illumination_support`` is returned. Otherwise, a reference to
|
|
5174
|
+
``illumination_support`` is returned.
|
|
5175
|
+
|
|
5176
|
+
Returns
|
|
5177
|
+
-------
|
|
5178
|
+
illumination_support : `torch.Tensor` (`float`, ndim=2)
|
|
5179
|
+
The attribute
|
|
5180
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.illumination_support`.
|
|
5181
|
+
|
|
5182
|
+
"""
|
|
5183
|
+
params = {"deep_copy": deep_copy}
|
|
5184
|
+
deep_copy = _check_and_convert_deep_copy(params)
|
|
5185
|
+
|
|
5186
|
+
if self._illumination_support is None:
|
|
5187
|
+
method_name = "_calc_illumination_support"
|
|
5188
|
+
method_alias = getattr(self, method_name)
|
|
5189
|
+
self._illumination_support = method_alias()
|
|
5190
|
+
|
|
5191
|
+
illumination_support = (self._illumination_support.detach().clone()
|
|
5192
|
+
if (deep_copy == True)
|
|
5193
|
+
else self._illumination_support)
|
|
5194
|
+
|
|
5195
|
+
return illumination_support
|
|
5196
|
+
|
|
5197
|
+
|
|
5198
|
+
|
|
5199
|
+
def _calc_illumination_support(self):
|
|
5200
|
+
uncropped_cbed_pattern_illumination_support = \
|
|
5201
|
+
self._cbed_pattern.get_illumination_support(deep_copy=False)
|
|
5202
|
+
uncropped_cbed_pattern_illumination_support = \
|
|
5203
|
+
uncropped_cbed_pattern_illumination_support.cpu().detach().clone()
|
|
5204
|
+
|
|
5205
|
+
signal_to_crop = \
|
|
5206
|
+
self._generate_uncropped_blank_signal()
|
|
5207
|
+
signal_to_crop.data[1] = \
|
|
5208
|
+
uncropped_cbed_pattern_illumination_support.numpy(force=True)
|
|
5209
|
+
|
|
5210
|
+
optional_params = self._generate_optional_cropping_params()
|
|
5211
|
+
|
|
5212
|
+
kwargs = {"input_signal": signal_to_crop,
|
|
5213
|
+
"optional_params": optional_params}
|
|
5214
|
+
cropped_signal = empix.crop(**kwargs)
|
|
5215
|
+
|
|
5216
|
+
illumination_support = \
|
|
5217
|
+
cropped_signal.data[1]
|
|
5218
|
+
illumination_support_dtype = \
|
|
5219
|
+
uncropped_cbed_pattern_illumination_support.dtype
|
|
5220
|
+
illumination_support = \
|
|
5221
|
+
torch.from_numpy(illumination_support)
|
|
5222
|
+
illumination_support = \
|
|
5223
|
+
illumination_support.to(device=self._device,
|
|
5224
|
+
dtype=illumination_support_dtype)
|
|
5225
|
+
|
|
5226
|
+
return illumination_support
|
|
5227
|
+
|
|
5228
|
+
|
|
5229
|
+
|
|
5230
|
+
@property
|
|
5231
|
+
def illumination_support(self):
|
|
5232
|
+
r"""`torch.Tensor`: The illumination support of the cropped fake CBED
|
|
5233
|
+
pattern.
|
|
5234
|
+
|
|
5235
|
+
See the documentation for the attribute
|
|
5236
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal` for additional
|
|
5237
|
+
context.
|
|
5238
|
+
|
|
5239
|
+
Let ``signal``, and ``device`` denote the attributes
|
|
5240
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.signal`, and
|
|
5241
|
+
:attr:`fakecbed.discretized.CroppedCBEDPattern.device` respectively.
|
|
5242
|
+
|
|
5243
|
+
``illumination_support`` is calculated effectively by:
|
|
5244
|
+
|
|
5245
|
+
.. code-block:: python
|
|
5246
|
+
|
|
5247
|
+
illumination_support = signal.data[1]
|
|
5248
|
+
illumination_support = torch.from_numpy(illumination_support)
|
|
5249
|
+
illumination_support = illumination_support.to(device=device,
|
|
5250
|
+
dtype=bool)
|
|
5251
|
+
|
|
5252
|
+
Note that ``illumination_support`` should be considered **read-only**.
|
|
5253
|
+
|
|
5254
|
+
"""
|
|
5255
|
+
result = self.get_illumination_support(deep_copy=True)
|
|
5256
|
+
|
|
5257
|
+
return result
|
|
5258
|
+
|
|
5259
|
+
|
|
5260
|
+
|
|
5261
|
+
###########################
|
|
5262
|
+
## Define error messages ##
|
|
5263
|
+
###########################
|
|
5264
|
+
|
|
5265
|
+
_check_and_convert_undistorted_disks_err_msg_1 = \
|
|
5266
|
+
("The object ``undistorted_disks`` must be a sequence of "
|
|
5267
|
+
"`fakecbed.shapes.NonuniformBoundedShape` objects, where for each element "
|
|
5268
|
+
"``elem`` in ``undistorted_disks``, "
|
|
5269
|
+
"``isinstance(elem.core_attrs['support'], "
|
|
5270
|
+
"(fakecbed.shapes.Circle, fakecbed.shapes.Ellipse))`` evaluates to "
|
|
5271
|
+
"``True``.")
|
|
5272
|
+
|
|
5273
|
+
_check_and_convert_undistorted_misc_shapes_err_msg_1 = \
|
|
5274
|
+
("The object ``undistorted_misc_shapes`` must be a sequence of objects of "
|
|
5275
|
+
"any of the following types: ("
|
|
5276
|
+
"`fakecbed.shapes.Circle`, "
|
|
5277
|
+
"`fakecbed.shapes.Ellipse`, "
|
|
5278
|
+
"`fakecbed.shapes.Peak`, "
|
|
5279
|
+
"`fakecbed.shapes.Band`, "
|
|
5280
|
+
"`fakecbed.shapes.PlaneWave`, "
|
|
5281
|
+
"`fakecbed.shapes.Arc`, "
|
|
5282
|
+
"`fakecbed.shapes.GenericBlob`, "
|
|
5283
|
+
"`fakecbed.shapes.Orbital`, "
|
|
5284
|
+
"`fakecbed.shapes.Lune`, "
|
|
5285
|
+
"`fakecbed.shapes.NonuniformBoundedShape`).")
|
|
5286
|
+
|
|
5287
|
+
_check_and_convert_distortion_model_err_msg_1 = \
|
|
5288
|
+
("The dimensions, in units of pixels, of the distortion model sampling "
|
|
5289
|
+
"grid, specified by the object ``distortion_model``, must be divisible "
|
|
5290
|
+
"by the object ``num_pixels_across_pattern``.")
|
|
5291
|
+
|
|
5292
|
+
_check_and_convert_rng_seed_err_msg_1 = \
|
|
5293
|
+
("The object ``rng_seed`` must be either a nonnegative integer or of the "
|
|
5294
|
+
"type `NoneType`.")
|
|
5295
|
+
|
|
5296
|
+
_check_and_convert_cold_pixels_err_msg_1 = \
|
|
5297
|
+
("The object ``cold_pixels`` must be a sequence of integer pairs, where "
|
|
5298
|
+
"each integer pair specifies valid pixel coordinates (i.e. row and column "
|
|
5299
|
+
"indices) of a pixel in the discretized fake CBED pattern.")
|
|
5300
|
+
|
|
5301
|
+
_check_and_convert_overriding_image_err_msg_1 = \
|
|
5302
|
+
("The object ``overriding_image`` must have dimensions, in units of "
|
|
5303
|
+
"pixels, equal to those of the original CBED pattern intensity image "
|
|
5304
|
+
"being overridden, which in this case are ``({}, {})``.")
|
|
5305
|
+
|
|
5306
|
+
_cbed_pattern_err_msg_1 = \
|
|
5307
|
+
("Failed to generate discretized fake CBED pattern. See traceback for "
|
|
5308
|
+
"details.")
|
|
5309
|
+
|
|
5310
|
+
_check_and_convert_cropping_window_center_err_msg_1 = \
|
|
5311
|
+
("The object ``cropping_window_center`` must be `NoneType` or a pair of "
|
|
5312
|
+
"real numbers.")
|
|
5313
|
+
|
|
5314
|
+
_check_and_convert_cropping_window_dims_in_pixels_err_msg_1 = \
|
|
5315
|
+
("The object ``cropping_window_dims_in_pixels`` must be `NoneType` or a "
|
|
5316
|
+
"pair of positive integers.")
|