fakecbed 0.3.5__py3-none-any.whl → 0.4.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 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"""The parameters of a discretized fake convergent beam electron
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_{⌑;x}\left(u_{x},u_{y}\right)\right)`, which maps a given coordinate pair
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 }\sum_{n,m}\mathcal{I}_{k;\text{DCM};⌑;n,m}
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 of the class
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 of the class
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, 5, 5),
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 = clip_support.to(dtype=torch.bool)
1667
- clip_support[0, 0, :T+2, :] = True
1668
- clip_support[0, 0, max(N_I_y-B-2, 0):, :] = True
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
 
@@ -1709,7 +1736,7 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
1709
1736
  # ``torch.poisson`` was occasionally causing CUDA errors.
1710
1737
  rng = np.random.default_rng(self._rng_seed)
1711
1738
  image_dtype = image.dtype
1712
- image = image.numpy(force=True)
1739
+ image = image.numpy(force=True).clip(min=0, max=1.0e15)
1713
1740
  image = rng.poisson(image)
1714
1741
  image = torch.from_numpy(image)
1715
1742
  image = image.to(device=self._device, dtype=image_dtype)
@@ -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 of the class
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 of the class
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 of the class
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 of the class
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 of the class
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 of the class
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 every nonzero pixel of the image of the support of the
2432
- :math:`k^{\text{th}}` distorted CBED disk is at least two pixels away
2433
- from (i.e. at least next-nearest neighbours to) every zero-valued pixel
2434
- of the image of the illumination support and is at least one pixel away
2435
- from every pixel bordering the image of the illumination support, and
2436
- that the image of the support of the :math:`k^{\text{th}}` distorted
2437
- CBED disk has at least one nonzero pixel. Otherwise, if
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 clipping registry,
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 clipping registry,
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 of the class
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{DCR};⌑}\right\}_{k=0}^{N_{\text{D}}-1}`.
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}_{\text{OI};⌑;n,m}` is the image of the illumination
2502
- support, and :math:`\mathcal{I}_{k;\text{DS};⌑;n,m}` is the image of the
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,2611 @@ class CBEDPattern(fancytypes.PreSerializableAndUpdatable):
2546
2571
 
2547
2572
 
2548
2573
 
2549
- ###########################
2550
- ## Define error messages ##
2551
- ###########################
2574
+ def _check_and_convert_cbed_pattern(params):
2575
+ obj_name = "cbed_pattern"
2576
+ obj = params[obj_name]
2552
2577
 
2553
- _check_and_convert_undistorted_disks_err_msg_1 = \
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
- _check_and_convert_undistorted_misc_shapes_err_msg_1 = \
2562
- ("The object ``undistorted_misc_shapes`` must be a sequence of objects of "
2563
- "any of the following types: ("
2564
- "`fakecbed.shapes.Circle`, "
2565
- "`fakecbed.shapes.Ellipse`, "
2566
- "`fakecbed.shapes.Peak`, "
2567
- "`fakecbed.shapes.Band`, "
2568
- "`fakecbed.shapes.PlaneWave`, "
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
- _check_and_convert_distortion_model_err_msg_1 = \
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
- _check_and_convert_overriding_image_err_msg_1 = \
2590
- ("The object ``overriding_image`` must have dimensions, in units of "
2591
- "pixels, equal to those of the original CBED pattern intensity image "
2592
- "being overridden, which in this case are ``({}, {})``.")
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
- _cbed_pattern_err_msg_1 = \
2595
- ("Failed to generate discretized fake CBED pattern. See traceback for "
2596
- "details.")
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_analysis_results",
2979
+ "_principal_disk_bounding_box"
2980
+ "_in_uncropped_image_fractional_coords",
2981
+ "_principal_disk_bounding_box"
2982
+ "_in_cropped_image_fractional_coords",
2983
+ "_principal_disk_boundary_pts"
2984
+ "_in_uncropped_image_fractional_coords",
2985
+ "_principal_disk_boundary_pts"
2986
+ "_in_cropped_image_fractional_coords")
2987
+
2988
+ for attr_name in attr_name_subset:
2989
+ setattr(self, attr_name, None)
2990
+
2991
+ return None
2992
+
2993
+
2994
+
2995
+ def _generate_cropped_blank_signal(self):
2996
+ uncropped_blank_signal = self._generate_uncropped_blank_signal()
2997
+
2998
+ optional_params = self._generate_optional_cropping_params()
2999
+
3000
+ kwargs = {"input_signal": uncropped_blank_signal,
3001
+ "optional_params": optional_params}
3002
+ cropped_blank_signal = empix.crop(**kwargs)
3003
+
3004
+ return cropped_blank_signal
3005
+
3006
+
3007
+
3008
+ def _generate_uncropped_blank_signal(self):
3009
+ cbed_pattern_core_attrs = \
3010
+ self._cbed_pattern.get_core_attrs(deep_copy=False)
3011
+
3012
+ num_disks = self._num_disks
3013
+ N_x = cbed_pattern_core_attrs["num_pixels_across_pattern"]
3014
+
3015
+ uncropped_blank_signal_data = np.zeros((3+num_disks, N_x, N_x),
3016
+ dtype=float)
3017
+ kwargs = {"data": uncropped_blank_signal_data}
3018
+ uncropped_blank_signal = hyperspy.signals.Signal2D(**kwargs)
3019
+
3020
+ kwargs = {"signal": uncropped_blank_signal}
3021
+ self._cbed_pattern._update_signal_axes(**kwargs)
3022
+
3023
+ return uncropped_blank_signal
3024
+
3025
+
3026
+
3027
+ def _generate_optional_cropping_params(self):
3028
+ kwargs = {"center": self._cropping_window_center,
3029
+ "window_dims": self._cropping_window_dims_in_pixels,
3030
+ "pad_mode": "zeros",
3031
+ "apply_symmetric_mask": False,
3032
+ "title": "",
3033
+ "skip_validation_and_conversion": True}
3034
+ optional_cropping_params = empix.OptionalCroppingParams(**kwargs)
3035
+
3036
+ return optional_cropping_params
3037
+
3038
+
3039
+
3040
+ def update(self,
3041
+ new_core_attr_subset_candidate,
3042
+ skip_validation_and_conversion=\
3043
+ _default_skip_validation_and_conversion):
3044
+ super().update(new_core_attr_subset_candidate,
3045
+ skip_validation_and_conversion)
3046
+ self.execute_post_core_attrs_update_actions()
3047
+
3048
+ return None
3049
+
3050
+
3051
+
3052
+ @property
3053
+ def num_disks(self):
3054
+ r"""`int`: The total number of CBED disks defined, :math:`N_{\text{D}}`.
3055
+
3056
+ See the summary documentation for the class
3057
+ :class:`fakecbed.discretized.CBEDPattern` for additional context.
3058
+
3059
+ Let ``core_attrs`` denote the attribute
3060
+ :attr:`~fancytypes.Checkable.core_attrs`. ``num_disks`` is equal to
3061
+ ``core_attrs["cbed_pattern"].num_disks``.
3062
+
3063
+ Note that ``num_disks`` should be considered **read-only**.
3064
+
3065
+ """
3066
+ result = self._num_disks
3067
+
3068
+ return result
3069
+
3070
+
3071
+
3072
+ @property
3073
+ def device(self):
3074
+ r"""`torch.device`: The device on which computationally intensive
3075
+ PyTorch operations are performed and attributes of the type
3076
+ :class:`torch.Tensor` are stored.
3077
+
3078
+ Note that ``device`` should be considered **read-only**.
3079
+
3080
+ """
3081
+ result = copy.deepcopy(self._device)
3082
+
3083
+ return result
3084
+
3085
+
3086
+
3087
+ def get_principal_disk_boundary_pts_in_uncropped_image_fractional_coords(
3088
+ self, deep_copy=_default_deep_copy):
3089
+ r"""Return the sample of points on the boundary of the "principal" CBED
3090
+ disk, should it exist, in fractional coordinates of the uncropped CBED
3091
+ pattern.
3092
+
3093
+ Let ``core_attrs`` denote the attribute
3094
+ :attr:`~fancytypes.Checkable.core_attrs`.
3095
+
3096
+ Parameters
3097
+ ----------
3098
+ deep_copy : `bool`, optional
3099
+ Let
3100
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
3101
+ denote the attribute
3102
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`.
3103
+
3104
+ If ``deep_copy`` is set to ``True``, then a deep copy of
3105
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
3106
+ is returned. Otherwise, a reference to
3107
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
3108
+ is returned.
3109
+
3110
+ Returns
3111
+ -------
3112
+ principal_disk_boundary_pts_in_uncropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
3113
+ The attribute
3114
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`.
3115
+
3116
+ """
3117
+ params = {"deep_copy": deep_copy}
3118
+ deep_copy = _check_and_convert_deep_copy(params)
3119
+
3120
+ attr_name = ("_principal_disk_boundary_pts"
3121
+ "_in_uncropped_image_fractional_coords")
3122
+ attr = getattr(self, attr_name)
3123
+
3124
+ if attr is None:
3125
+ method_name = ("_calc_principal_disk_analysis_results"
3126
+ "_and_cache_select_intermediates")
3127
+ method_alias = getattr(self, method_name)
3128
+ self._principal_disk_analysis_results = method_alias()
3129
+
3130
+ attr = getattr(self, attr_name)
3131
+
3132
+ principal_disk_boundary_pts_in_uncropped_image_fractional_coords = \
3133
+ (attr
3134
+ if ((deep_copy == False) or (attr is None))
3135
+ else attr.detach().clone())
3136
+
3137
+ return principal_disk_boundary_pts_in_uncropped_image_fractional_coords
3138
+
3139
+
3140
+
3141
+ def _calc_principal_disk_analysis_results_and_cache_select_intermediates(
3142
+ self):
3143
+ principal_disk_analysis_results = tuple()
3144
+
3145
+ method_name_set = \
3146
+ ("_calc_disk_boundary_pts_in_uncropped_image_fractional_coords",
3147
+ "_calc_disk_bounding_box_in_uncropped_image_fractional_coords",
3148
+ "_calc_disk_boundary_pts_in_cropped_image_fractional_coords",
3149
+ "_calc_disk_bounding_box_in_cropped_image_fractional_coords")
3150
+
3151
+ for method_alias_idx, method_name in enumerate(method_name_set):
3152
+ method_alias = getattr(self, method_name)
3153
+
3154
+ if method_alias_idx in (0,):
3155
+ kwargs = \
3156
+ {"disk_idx": \
3157
+ self._principal_disk_idx}
3158
+ elif method_alias_idx in (1, 2):
3159
+ kwargs = \
3160
+ {"disk_boundary_pts_in_uncropped_image_fractional_coords": \
3161
+ principal_disk_analysis_results[0]}
3162
+ else:
3163
+ kwargs = \
3164
+ {"disk_boundary_pts_in_cropped_image_fractional_coords": \
3165
+ principal_disk_analysis_results[2]}
3166
+
3167
+ principal_disk_analysis_results += (method_alias(**kwargs),)
3168
+
3169
+ attr_name = "_principal" + method_name[5:]
3170
+ setattr(self, attr_name, principal_disk_analysis_results[-1])
3171
+
3172
+ return principal_disk_analysis_results
3173
+
3174
+
3175
+
3176
+ def _calc_disk_boundary_pts_in_uncropped_image_fractional_coords(self,
3177
+ disk_idx):
3178
+ cbed_pattern_core_attrs = \
3179
+ self._cbed_pattern.get_core_attrs(deep_copy=False)
3180
+
3181
+ undistorted_disks = cbed_pattern_core_attrs["undistorted_disks"]
3182
+
3183
+ if disk_idx < len(undistorted_disks):
3184
+ undistorted_disk = undistorted_disks[disk_idx]
3185
+
3186
+ num_pixels_across_uncropped_pattern = \
3187
+ cbed_pattern_core_attrs["num_pixels_across_pattern"]
3188
+
3189
+ method_name = \
3190
+ "_calc_undistorted_disk_boundary_starting_angular_coord"
3191
+ method_alias = \
3192
+ getattr(self, method_name)
3193
+ kwargs = \
3194
+ {"undistorted_disk": \
3195
+ undistorted_disk,
3196
+ "num_pixels_across_uncropped_pattern": \
3197
+ num_pixels_across_uncropped_pattern}
3198
+ undistorted_disk_boundary_starting_angular_coord = \
3199
+ method_alias(**kwargs)
3200
+
3201
+ kwargs = {"steps": self._disk_boundary_sample_size}
3202
+ kwargs["start"] = undistorted_disk_boundary_starting_angular_coord
3203
+ kwargs["end"] = (kwargs["start"]
3204
+ + (kwargs["steps"]-1)*(2*np.pi/kwargs["steps"]))
3205
+ undistorted_disk_boundary_angular_coords = torch.linspace(**kwargs)
3206
+
3207
+ method_name = ("_calc_disk_boundary_pts_and_diffs"
3208
+ "_in_uncropped_image_fractional_coords")
3209
+ method_alias = getattr(self, method_name)
3210
+ kwargs = {"undistorted_disk": \
3211
+ undistorted_disk,
3212
+ "undistorted_disk_boundary_angular_coords": \
3213
+ undistorted_disk_boundary_angular_coords}
3214
+ pts_and_diffs = method_alias(**kwargs)
3215
+
3216
+ disk_boundary_pts_in_uncropped_image_fractional_coords = \
3217
+ pts_and_diffs[0]
3218
+ else:
3219
+ disk_boundary_pts_in_uncropped_image_fractional_coords = \
3220
+ None
3221
+
3222
+ return disk_boundary_pts_in_uncropped_image_fractional_coords
3223
+
3224
+
3225
+
3226
+ def _calc_undistorted_disk_boundary_starting_angular_coord(
3227
+ self, undistorted_disk, num_pixels_across_uncropped_pattern):
3228
+ max_num_iterations = 30
3229
+ iteration_idx = 0
3230
+ num_pts = self._disk_boundary_sample_size
3231
+ search_algorithm_has_not_finished = True
3232
+
3233
+ kwargs = {"steps": num_pts}
3234
+ kwargs["start"] = 0
3235
+ kwargs["end"] = (kwargs["start"]
3236
+ + (kwargs["steps"]-1)*(2*np.pi/kwargs["steps"]))
3237
+
3238
+ while search_algorithm_has_not_finished:
3239
+ angular_coords = torch.linspace(**kwargs)
3240
+
3241
+ method_name = ("_calc_disk_boundary_pts_and_diffs"
3242
+ "_in_uncropped_image_fractional_coords")
3243
+ method_alias = getattr(self, method_name)
3244
+ kwargs = {"undistorted_disk": \
3245
+ undistorted_disk,
3246
+ "undistorted_disk_boundary_angular_coords": \
3247
+ angular_coords}
3248
+ pts, diffs = method_alias(**kwargs)
3249
+
3250
+ angular_coord_idx = torch.argmax(pts[:, 0])
3251
+ diff_subset = diffs if (iteration_idx==0) else diffs[:-1]
3252
+ N_x = num_pixels_across_uncropped_pattern
3253
+
3254
+ if iteration_idx == 0:
3255
+ min_angle = angular_coords[(angular_coord_idx-16)%num_pts]
3256
+ max_angle = angular_coords[(angular_coord_idx+16)%num_pts]
3257
+ else:
3258
+ min_angle = angular_coords[max(angular_coord_idx-16, 0)]
3259
+ max_angle = angular_coords[min(angular_coord_idx+16, num_pts-1)]
3260
+
3261
+ kwargs = {"steps": num_pts}
3262
+ kwargs["start"] = min_angle
3263
+ kwargs["end"] = max_angle + (min_angle > max_angle)*(2*np.pi)
3264
+
3265
+ search_algorithm_has_not_finished = \
3266
+ ((100*N_x*torch.amax(diff_subset) > 1)
3267
+ and (iteration_idx < max_num_iterations-1))
3268
+
3269
+ iteration_idx += 1
3270
+
3271
+ undistorted_disk_boundary_starting_angular_coord = \
3272
+ angular_coords[angular_coord_idx].item() % (2*np.pi)
3273
+
3274
+ return undistorted_disk_boundary_starting_angular_coord
3275
+
3276
+
3277
+
3278
+ def _calc_disk_boundary_pts_and_diffs_in_uncropped_image_fractional_coords(
3279
+ self, undistorted_disk, undistorted_disk_boundary_angular_coords):
3280
+ method_name = \
3281
+ "_calc_shape_boundary_pts_in_uncropped_image_fractional_coords"
3282
+ method_alias = \
3283
+ getattr(self, method_name)
3284
+ kwargs = \
3285
+ {"shape": \
3286
+ undistorted_disk,
3287
+ "shape_boundary_angular_coords": \
3288
+ undistorted_disk_boundary_angular_coords}
3289
+ shape_boundary_pts_in_uncropped_image_fractional_coords = \
3290
+ method_alias(**kwargs)
3291
+
3292
+ u_x = shape_boundary_pts_in_uncropped_image_fractional_coords[:, 0:1]
3293
+ u_y = shape_boundary_pts_in_uncropped_image_fractional_coords[:, 1:2]
3294
+
3295
+ cbed_pattern_core_attrs = \
3296
+ self._cbed_pattern.get_core_attrs(deep_copy=False)
3297
+ distortion_model = \
3298
+ cbed_pattern_core_attrs["distortion_model"]
3299
+
3300
+ distortion_model_core_attrs = \
3301
+ distortion_model.get_core_attrs(deep_copy=False)
3302
+ coord_transform_params = \
3303
+ distortion_model_core_attrs["coord_transform_params"]
3304
+
3305
+ kwargs = {"u_x": u_x,
3306
+ "u_y": u_y,
3307
+ "coord_transform_params": coord_transform_params,
3308
+ "device": self._device,
3309
+ "skip_validation_and_conversion": True}
3310
+ q_x, q_y = distoptica.apply_coord_transform(**kwargs)
3311
+
3312
+ num_pts = self._disk_boundary_sample_size
3313
+ pts = torch.zeros((num_pts, 2), device=self._device)
3314
+ pts[:, 0] = q_x[:, 0]
3315
+ pts[:, 1] = q_y[:, 0]
3316
+
3317
+ diffs = torch.zeros_like(undistorted_disk_boundary_angular_coords)
3318
+ for angular_coord_idx in range(num_pts):
3319
+ pt_1 = pts[(angular_coord_idx)%num_pts]
3320
+ pt_2 = pts[(angular_coord_idx+1)%num_pts]
3321
+ diffs[angular_coord_idx] = torch.linalg.norm(pt_2-pt_1)
3322
+
3323
+ disk_boundary_pts_and_diffs_in_uncropped_image_fractional_coords = \
3324
+ (pts, diffs)
3325
+
3326
+ return disk_boundary_pts_and_diffs_in_uncropped_image_fractional_coords
3327
+
3328
+
3329
+
3330
+ def _calc_shape_boundary_pts_in_uncropped_image_fractional_coords(
3331
+ self, shape, shape_boundary_angular_coords):
3332
+ shape_core_attrs = shape.get_core_attrs(deep_copy=False)
3333
+ shape_support = shape_core_attrs["support"]
3334
+
3335
+ shape_support_core_attrs = shape_support.get_core_attrs(deep_copy=False)
3336
+ u_x_c, u_y_c = shape_support_core_attrs["center"]
3337
+ a = (shape_support_core_attrs["semi_major_axis"]
3338
+ if ("semi_major_axis" in shape_support_core_attrs)
3339
+ else shape_support_core_attrs["radius"])
3340
+ e = shape_support_core_attrs.get("eccentricity", 0)
3341
+ theta = shape_support_core_attrs.get("rotation_angle", 0)
3342
+
3343
+ b = a*np.sqrt(1-e*e).item()
3344
+ phi = shape_boundary_angular_coords
3345
+
3346
+ u_x = (u_x_c + a*torch.cos(phi+theta))[:, None]
3347
+ u_y = (u_y_c + b*torch.sin(phi+theta))[:, None]
3348
+
3349
+ num_pts = shape_boundary_angular_coords.numel()
3350
+ pts = torch.zeros((num_pts, 2), device=self._device)
3351
+ pts[:, 0] = u_x[:, 0]
3352
+ pts[:, 1] = u_y[:, 0]
3353
+
3354
+ shape_boundary_pts_in_uncropped_image_fractional_coords = pts
3355
+
3356
+ return shape_boundary_pts_in_uncropped_image_fractional_coords
3357
+
3358
+
3359
+
3360
+ def _calc_disk_bounding_box_in_uncropped_image_fractional_coords(
3361
+ self, disk_boundary_pts_in_uncropped_image_fractional_coords):
3362
+ if disk_boundary_pts_in_uncropped_image_fractional_coords is not None:
3363
+ pts = disk_boundary_pts_in_uncropped_image_fractional_coords
3364
+
3365
+ disk_bounding_box_in_uncropped_image_fractional_coords = \
3366
+ (torch.amin(pts[:, 0]).item(),
3367
+ torch.amax(pts[:, 0]).item(),
3368
+ torch.amin(pts[:, 1]).item(),
3369
+ torch.amax(pts[:, 1]).item())
3370
+ else:
3371
+ disk_bounding_box_in_uncropped_image_fractional_coords = None
3372
+
3373
+ return disk_bounding_box_in_uncropped_image_fractional_coords
3374
+
3375
+
3376
+
3377
+ def _calc_disk_boundary_pts_in_cropped_image_fractional_coords(
3378
+ self, disk_boundary_pts_in_uncropped_image_fractional_coords):
3379
+ if disk_boundary_pts_in_uncropped_image_fractional_coords is not None:
3380
+ cbed_pattern_core_attrs = \
3381
+ self._cbed_pattern.get_core_attrs(deep_copy=False)
3382
+
3383
+ N_x = cbed_pattern_core_attrs["num_pixels_across_pattern"]
3384
+ N_y = N_x
3385
+
3386
+ N_W_h, N_W_v = self._cropping_window_dims_in_pixels
3387
+
3388
+ kwargs = \
3389
+ {"input": \
3390
+ disk_boundary_pts_in_uncropped_image_fractional_coords}
3391
+ disk_boundary_pts_in_cropped_image_fractional_coords = \
3392
+ torch.zeros_like(**kwargs)
3393
+
3394
+ disk_boundary_pts_in_cropped_image_fractional_coords[:, 0] = \
3395
+ ((disk_boundary_pts_in_uncropped_image_fractional_coords[:, 0]
3396
+ - self._cropped_blank_signal_offsets[1]) * (N_x/N_W_h)
3397
+ + (0.5/N_W_h))
3398
+ disk_boundary_pts_in_cropped_image_fractional_coords[:, 1] = \
3399
+ ((disk_boundary_pts_in_uncropped_image_fractional_coords[:, 1]
3400
+ - self._cropped_blank_signal_offsets[2]) * (N_y/N_W_v)
3401
+ + (1-(1-0.5)/N_W_v))
3402
+ else:
3403
+ disk_boundary_pts_in_cropped_image_fractional_coords = None
3404
+
3405
+ return disk_boundary_pts_in_cropped_image_fractional_coords
3406
+
3407
+
3408
+
3409
+ def _calc_disk_bounding_box_in_cropped_image_fractional_coords(
3410
+ self, disk_boundary_pts_in_cropped_image_fractional_coords):
3411
+ if disk_boundary_pts_in_cropped_image_fractional_coords is not None:
3412
+ pts = disk_boundary_pts_in_cropped_image_fractional_coords
3413
+
3414
+ disk_bounding_box_in_cropped_image_fractional_coords = \
3415
+ (torch.amin(pts[:, 0]).item(),
3416
+ torch.amax(pts[:, 0]).item(),
3417
+ torch.amin(pts[:, 1]).item(),
3418
+ torch.amax(pts[:, 1]).item())
3419
+ else:
3420
+ disk_bounding_box_in_cropped_image_fractional_coords = None
3421
+
3422
+ return disk_bounding_box_in_cropped_image_fractional_coords
3423
+
3424
+
3425
+
3426
+ @property
3427
+ def principal_disk_boundary_pts_in_uncropped_image_fractional_coords(self):
3428
+ r"""`torch.Tensor` | `None`: The sample of points on the boundary of the
3429
+ unclipped support of the "principal" CBED disk, should it exist, in
3430
+ fractional coordinates of the uncropped CBED pattern.
3431
+
3432
+ See the summary documentation for the classes
3433
+ :class:`fakecbed.discretized.CBEDPattern` and
3434
+ :class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
3435
+
3436
+ Let ``core_attrs`` denote the attribute
3437
+ :attr:`~fancytypes.Checkable.core_attrs`. Furthermore, let
3438
+ ``cbed_pattern``, ``disk_boundary_sample_size``, and
3439
+ ``principal_disk_idx`` denote ``core_attrs["cbed_pattern"]``,
3440
+ ``core_attrs["disk_boundary_sample_size"]``, and
3441
+ ``core_attrs["principal_disk_idx"]`` respectively.
3442
+
3443
+ The "principal" CBED disk refers to the CBED disk, should it exist, that
3444
+ is specified by the nonnegative integer ``principal_disk_idx``. If
3445
+ ``principal_disk_idx <
3446
+ len(cbed_pattern.core_attrs["undistorted_disks"])``, then
3447
+ ``principal_disk_idx`` specifies the CBED disk constructed from the
3448
+ undistorted intensity pattern stored in
3449
+ ``cbed_pattern.core_attrs["undistorted_disks"][prinicpal_disk_idx]``. If
3450
+ ``principal_disk_idx >=
3451
+ len(cbed_pattern.core_attrs["undistorted_disks"])``, then
3452
+ ``principal_disk_idx`` specifies a nonexistent CBED disk.
3453
+
3454
+ If ``principal_disk_idx`` specifies a nonexistent CBED disk, then
3455
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords`` is
3456
+ set to ``None``.
3457
+
3458
+ The remainder of the documentation for the current attribute describes
3459
+ how said attribute is calculated, assuming that the principal CBED disk
3460
+ exists. In :mod:`fakecbed`, the undistorted shape of the support of
3461
+ every CBED disk is assumed to be an ellipse, which incidentally can be a
3462
+ circle if the eccentricity is zero. The definitions of the center, the
3463
+ semi-major axis, the eccentricity, and the rotation angle of a ellipse
3464
+ that are adopted in :mod:`fakecbed` are given implictly in the
3465
+ documentation for the class :class:`fakecbed.shapes.Ellipse`. Let
3466
+ :math:`\left(u_{x;c;\text{PDS}},u_{y;c;\text{PDS}}\right)`,
3467
+ :math:`a_{\text{PDS}}`, :math:`e_{\text{PDS}}`, and
3468
+ :math:`\theta_{\text{PDS}}` be the center, the semi-major axis, the
3469
+ eccentricity, and the rotation angle of the principal CBED disk support.
3470
+
3471
+ Let :math:`u_{x}` and :math:`u_{y}` be the fractional horizontal and
3472
+ vertical coordinates, respectively, of a point in an undistorted image,
3473
+ where :math:`\left(u_{x},u_{y}\right)=\left(0,0\right)` is the bottom
3474
+ left corner of the image. Similarly, let :math:`q_{x}` and :math:`q_{y}`
3475
+ be the fractional horizontal and vertical coordinates, respectively, of
3476
+ a point in a distorted image, where
3477
+ :math:`\left(q_{x},q_{y}\right)=\left(0,0\right)` is the bottom left
3478
+ corner of the image. The core attribute ``cbed_pattern`` specifies a
3479
+ distortion model, which can be accessed via
3480
+ ``cbed_pattern.core_attrs["distortion_model"]``. The distortion model
3481
+ specifies a coordinate transformation,
3482
+ :math:`\left(T_{⌑;x}\left(u_{x},u_{y}\right),
3483
+ T_{⌑;x}\left(u_{x},u_{y}\right)\right)`, which maps a given coordinate
3484
+ pair :math:`\left(u_{x},u_{y}\right)` to a corresponding coordinate pair
3485
+ :math:`\left(q_{x},q_{y}\right)`.
3486
+
3487
+ Next, let :math:`N_{\mathcal{I};x} and N_{\mathcal{I};y}` be the number
3488
+ of pixels in the image of the uncropped fake CBED pattern, where we
3489
+ assume that
3490
+
3491
+ .. math ::
3492
+ N_{\mathcal{I};x}=N_{\mathcal{I};y},
3493
+ :label: N_I_x_eq_N_I_y__2
3494
+
3495
+ Furthermore, let :math:`N_{\text{max-iter}}=30, and let N_{\text{SS}}`
3496
+ be ``disk_boundary_sample_size``.
3497
+
3498
+ Next, let :math:`\partial S_{\text{PDS};x;l}` and :math:`\partial
3499
+ S_{\text{PDS};y;l}` denote
3500
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords[l,
3501
+ 0]`` and
3502
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords[l,
3503
+ 1]`` respectively, where
3504
+ ````principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
3505
+ is the current attribute, and ``l`` is equal to the value of
3506
+ :math:`l`. The current attribute is calculated effectively by executing
3507
+ the following steps:
3508
+
3509
+ 1. Calculate
3510
+
3511
+ .. math ::
3512
+ X_{1} \leftarrow
3513
+ \left\{ l^{\prime}\right\}_{l^{\prime}=0}^{N_{\text{SS}}-1}.
3514
+ :label: X_1__1
3515
+
3516
+ 2. Calculate
3517
+
3518
+ .. math ::
3519
+ X_{2} \leftarrow
3520
+ \left\{ l^{\prime}\right\}_{l^{\prime}=0}^{N_{\text{SS}}-2}
3521
+ :label: X_2__1
3522
+
3523
+ 3. Calculate
3524
+
3525
+ .. math ::
3526
+ m \leftarrow 0.
3527
+ :label: m__1
3528
+
3529
+ 4. Calculate
3530
+
3531
+ .. math ::
3532
+ N_{\text{iter}} \leftarrow 0.
3533
+ :label: N_iter__1
3534
+
3535
+ 5. Calculate
3536
+
3537
+ .. math ::
3538
+ \phi_{l}\leftarrow\frac{2\pi l}{N_{\text{SS}}}
3539
+ \text{ for }l\in X_{1}.
3540
+ :label: phi_l__1
3541
+
3542
+ 6. Calculate
3543
+
3544
+ .. math ::
3545
+ j\leftarrow\min\left(\left\{ N_{\text{iter}}+1,1\right\} \right).
3546
+ :label: j__1
3547
+
3548
+ 7. Calculate
3549
+
3550
+ .. math ::
3551
+ u_{x;l}\leftarrow u_{x;c;\text{PDS}}
3552
+ +a_{\text{PDS}}\cos\left(\phi_{l}+\theta_{\text{PDS}}\right)
3553
+ \text{ for }l\in X_{1},
3554
+ :label: u_x_l__1
3555
+
3556
+ and
3557
+
3558
+ .. math ::
3559
+ u_{y;l}\leftarrow u_{y;c;\text{PDS}}
3560
+ +a_{\text{PDS}}\sqrt{1-e_{\text{PDS}}^{2}}
3561
+ \sin\left(\phi_{l}+\theta_{\text{PDS}}\right)\text{ for }l\in X_{1}.
3562
+ :label: u_y_l__1
3563
+
3564
+ 8. Calculate
3565
+
3566
+ .. math ::
3567
+ q_{x;l}\leftarrow T_{⌑;x}\left(u_{x;l},u_{y;l}\right)
3568
+ \text{ for }l\in X_{1},
3569
+ :label: q_x_l__1
3570
+
3571
+ and
3572
+
3573
+ .. math ::
3574
+ q_{y;l}\leftarrow T_{⌑;y}\left(u_{x;l},u_{y;l}\right)
3575
+ \text{ for }l\in X_{1}.
3576
+ :label: q_y_l__1
3577
+
3578
+ 9. If :math:`m=0`, then go to step 10, otherwise go to step 21.
3579
+
3580
+ 10. Calculate
3581
+
3582
+ .. math ::
3583
+ n\leftarrow\text{argmax}_{l\in X_{1}}\left(u_{x;l}\right).
3584
+ :label: n__1
3585
+
3586
+ 11. Calculate
3587
+
3588
+ .. math ::
3589
+ \Delta_{q;l}\leftarrow\sqrt{\sum_{\alpha\in\left\{ x,y\right\} }
3590
+ \left[q_{\alpha;l\mod N_{\text{SS}}}
3591
+ -q_{\alpha;\left\{ l+1\right\} \mod N_{\text{SS}}}\right]^{2}}
3592
+ \text{ for }l\in X_{j}.
3593
+ :label: Delta_q_l__1
3594
+
3595
+ 12. If :math:`N_{\text{iter}}=0` then go to step 13, otherwise go to
3596
+ step 15.
3597
+
3598
+ 13. Calculate
3599
+
3600
+ .. math ::
3601
+ \phi_{\vdash}\leftarrow\phi_{\left(n-16\right)\mod N_{\text{SS}}},
3602
+ :label: phi_vdash__1
3603
+
3604
+ and
3605
+
3606
+ .. math ::
3607
+ \phi_{\dashv}\leftarrow\phi_{\left(n+16\right)\mod N_{\text{SS}}}.
3608
+ :label: phi_dashv__1
3609
+
3610
+ 14. Go to step 16.
3611
+
3612
+ 15. Calculate
3613
+
3614
+ .. math ::
3615
+ \phi_{\vdash}\leftarrow
3616
+ \phi_{\max\left(\left\{n-16,0\right\}\right)},
3617
+ :label: phi_vdash__2
3618
+
3619
+ and
3620
+
3621
+ .. math ::
3622
+ \phi_{\dashv}\leftarrow
3623
+ \phi_{\min\left(\left\{n+16,N_{\text{SS}}-1\right\}\right)}.
3624
+ :label: phi_dashv__2
3625
+
3626
+ 16. If :math:`\phi_{\vdash}>\phi_{\dashv}`, then calculate
3627
+
3628
+ .. math ::
3629
+ \phi_{\dashv}\leftarrow\phi_{\dashv}+2\pi.
3630
+ :label: phi_dashv__3
3631
+
3632
+ 17. Calculate
3633
+
3634
+ .. math ::
3635
+ \phi_{l}\leftarrow
3636
+ \phi_{\vdash}+l\frac{\phi_{\dashv}-\phi_{\vdash}}{N_{\text{SS}}-1}
3637
+ \text{ for }l\in X_{1}.
3638
+ :label: phi_l__2
3639
+
3640
+ 18. If :math:`100
3641
+ N_{\mathcal{I};x}\max\left(
3642
+ \left\{\Delta_{q;l}\right\}_{l\in S_{m}}\right) \le 1` or
3643
+ :math:`N_{\text{iter}}=N_{\text{max-iter}}`, then calculate
3644
+
3645
+
3646
+ .. math ::
3647
+ m \leftarrow 1,
3648
+ :label: m__2
3649
+
3650
+ .. math ::
3651
+ \phi_{\vdash}\leftarrow\phi_{n}\mod\left\{ 2\pi\right\},
3652
+ :label: phi_vdash__3
3653
+
3654
+ and
3655
+
3656
+ .. math ::
3657
+ \phi_{\dashv}\leftarrow\phi_{\vdash}+2\pi.
3658
+ :label: phi_dashv__4
3659
+
3660
+
3661
+ 19. Calculate
3662
+
3663
+ .. math ::
3664
+ N_{\text{iter}}\leftarrow N_{\text{iter}}+1.
3665
+ :label: N_iter__2
3666
+
3667
+ 20. Go to step 6.
3668
+
3669
+ 21. Calculate
3670
+
3671
+ .. math ::
3672
+ \partial S_{\text{PDS};x;l}\leftarrow q_{x;l}\text{ for }l\in X_{1},
3673
+ :label: partial_S_PDS_x_l__1
3674
+
3675
+ and
3676
+
3677
+ .. math ::
3678
+ \partial S_{\text{PDS};y;l}\leftarrow q_{y;l}\text{ for }l\in X_{1}.
3679
+ :label: partial_S_PDS_y_l__1
3680
+
3681
+ A consequence of the above algorithm is that :math:`\left(\partial
3682
+ S_{\text{PDS};x;0},\partial S_{\text{PDS};y;0}\right)` is the right-most
3683
+ point of the entire sample of points :math:`\left\{ \left(\partial
3684
+ S_{\text{PDS};x;l},\partial S_{\text{PDS};y;l}\right)\right\} _{l\in
3685
+ S_{1}}` on the boundary of the principal CBED disk.
3686
+
3687
+ Note that
3688
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
3689
+ should be considered **read-only**.
3690
+
3691
+ """
3692
+ method_name = ("get_principal_disk_boundary_pts"
3693
+ "_in_uncropped_image_fractional_coords")
3694
+ method_alias = getattr(self, method_name)
3695
+ result = method_alias(deep_copy=True)
3696
+
3697
+ return result
3698
+
3699
+
3700
+
3701
+ def get_principal_disk_bounding_box_in_uncropped_image_fractional_coords(
3702
+ self, deep_copy=_default_deep_copy):
3703
+ r"""Return the bounding box of the "principal" CBED disk, should it
3704
+ exist, in fractional coordinates of the uncropped CBED pattern.
3705
+
3706
+ Let ``core_attrs`` denote the attribute
3707
+ :attr:`~fancytypes.Checkable.core_attrs`.
3708
+
3709
+ Parameters
3710
+ ----------
3711
+ deep_copy : `bool`, optional
3712
+ Let
3713
+ ``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
3714
+ denote the attribute
3715
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_uncropped_image_fractional_coords`.
3716
+
3717
+ If ``deep_copy`` is set to ``True``, then a deep copy of
3718
+ ``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
3719
+ is returned. Otherwise, a reference to
3720
+ ``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
3721
+ is returned.
3722
+
3723
+ Returns
3724
+ -------
3725
+ principal_disk_bounding_box_in_uncropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
3726
+ The attribute
3727
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_uncropped_image_fractional_coords`.
3728
+
3729
+ """
3730
+ params = {"deep_copy": deep_copy}
3731
+ deep_copy = _check_and_convert_deep_copy(params)
3732
+
3733
+ attr_name = ("_principal_disk_bounding_box"
3734
+ "_in_uncropped_image_fractional_coords")
3735
+ attr = getattr(self, attr_name)
3736
+
3737
+ if attr is None:
3738
+ method_name = ("_calc_principal_disk_analysis_results"
3739
+ "_and_cache_select_intermediates")
3740
+ method_alias = getattr(self, method_name)
3741
+ self._principal_disk_analysis_results = method_alias()
3742
+
3743
+ attr = getattr(self, attr_name)
3744
+
3745
+ principal_disk_bounding_box_in_uncropped_image_fractional_coords = \
3746
+ copy.deepcopy(attr) if (deep_copy == True) else attr
3747
+
3748
+ return principal_disk_bounding_box_in_uncropped_image_fractional_coords
3749
+
3750
+
3751
+
3752
+ @property
3753
+ def principal_disk_bounding_box_in_uncropped_image_fractional_coords(self):
3754
+ r"""`tuple` | `None`: The bounding box of the unclipped support of the
3755
+ "principal" CBED disk, should it exist, in fractional coordinates of the
3756
+ uncropped CBED pattern.
3757
+
3758
+ See the documentation for the attribute
3759
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`
3760
+ for additional context.
3761
+
3762
+ Let ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``
3763
+ denote the attribute
3764
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`.
3765
+
3766
+ ``principal_disk_bounding_box_in_uncropped_image_fractional_coords`` is
3767
+ calculated effectively by:
3768
+
3769
+ .. code-block:: python
3770
+
3771
+ import torch
3772
+
3773
+ pts = \
3774
+ principal_disk_boundary_pts_in_uncropped_image_fractional_coords
3775
+
3776
+ if pts is not None:
3777
+ bounding_box = (torch.amin(pts[:, 0]).item(),
3778
+ torch.amax(pts[:, 0]).item(),
3779
+ torch.amin(pts[:, 1]).item(),
3780
+ torch.amax(pts[:, 1]).item())
3781
+ else:
3782
+ bounding_box = None
3783
+
3784
+ principal_disk_bounding_box_in_uncropped_image_fractional_coords = \
3785
+ bounding_box
3786
+
3787
+ Note that
3788
+ ``principal_disk_bounding_box_in_uncropped_image_fractional_coords``
3789
+ should be considered **read-only**.
3790
+
3791
+ """
3792
+ method_name = ("get_principal_disk_bounding_box"
3793
+ "_in_uncropped_image_fractional_coords")
3794
+ method_alias = getattr(self, method_name)
3795
+ result = method_alias(deep_copy=True)
3796
+
3797
+ return result
3798
+
3799
+
3800
+
3801
+ def get_principal_disk_boundary_pts_in_cropped_image_fractional_coords(
3802
+ self, deep_copy=_default_deep_copy):
3803
+ r"""Return the sample of points on the boundary of the "principal" CBED
3804
+ disk, should it exist, in fractional coordinates of the cropped CBED
3805
+ pattern.
3806
+
3807
+ Let ``core_attrs`` denote the attribute
3808
+ :attr:`~fancytypes.Checkable.core_attrs`.
3809
+
3810
+ Parameters
3811
+ ----------
3812
+ deep_copy : `bool`, optional
3813
+ Let
3814
+ ``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
3815
+ denote the attribute
3816
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`.
3817
+
3818
+ If ``deep_copy`` is set to ``True``, then a deep copy of
3819
+ ``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
3820
+ is returned. Otherwise, a reference to
3821
+ ``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
3822
+ is returned.
3823
+
3824
+ Returns
3825
+ -------
3826
+ principal_disk_boundary_pts_in_cropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
3827
+ The attribute
3828
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`.
3829
+
3830
+ """
3831
+ params = {"deep_copy": deep_copy}
3832
+ deep_copy = _check_and_convert_deep_copy(params)
3833
+
3834
+ attr_name = ("_principal_disk_boundary_pts"
3835
+ "_in_cropped_image_fractional_coords")
3836
+ attr = getattr(self, attr_name)
3837
+
3838
+ if attr is None:
3839
+ method_name = ("_calc_principal_disk_analysis_results"
3840
+ "_and_cache_select_intermediates")
3841
+ method_alias = getattr(self, method_name)
3842
+ self._principal_disk_analysis_results = method_alias()
3843
+
3844
+ attr = getattr(self, attr_name)
3845
+
3846
+ principal_disk_boundary_pts_in_cropped_image_fractional_coords = \
3847
+ (attr
3848
+ if ((deep_copy == False) or (attr is None))
3849
+ else attr.detach().clone())
3850
+
3851
+ return principal_disk_boundary_pts_in_cropped_image_fractional_coords
3852
+
3853
+
3854
+
3855
+ @property
3856
+ def principal_disk_boundary_pts_in_cropped_image_fractional_coords(self):
3857
+ r"""`torch.Tensor` | `None`: The sample of points on the boundary of the
3858
+ unclipped support of the "principal" CBED disk, should it exist, in
3859
+ fractional coordinates of the cropped CBED pattern.
3860
+
3861
+ See the documentation for the attribute
3862
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`
3863
+ for additional context.
3864
+
3865
+ Let ``core_attrs``,
3866
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``,
3867
+ and ``signal``, denote the attributes
3868
+ :attr:`~fancytypes.Checkable.core_attrs`
3869
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`,
3870
+ and :attr:`fakecbed.discretized.CroppedCBEDPattern.signal` respectively.
3871
+
3872
+ ``principal_disk_boundary_pts_in_cropped_image_fractional_coords`` is
3873
+ calculated effectively by:
3874
+
3875
+ .. code-block:: python
3876
+
3877
+ import torch
3878
+
3879
+ cbed_pattern = core_attrs["cbed_pattern"]
3880
+ N_x = cbed_pattern.core_attrs["num_pixels_across_pattern"]
3881
+ N_y = N_x
3882
+
3883
+ N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
3884
+
3885
+ boundary_pts_in_uncropped_image_fractional_coords = \
3886
+ principal_disk_boundary_pts_in_uncropped_image_fractional_coords
3887
+
3888
+ if boundary_pts_in_uncropped_image_fractional_coords is not None:
3889
+
3890
+ kwargs = \
3891
+ {"input": boundary_pts_in_uncropped_image_fractional_coords}
3892
+ boundary_pts_in_cropped_image_fractional_coords = \
3893
+ torch.zeros_like(**kwargs)
3894
+
3895
+ boundary_pts_in_cropped_image_fractional_coords[:, 0] = \
3896
+ ((boundary_pts_in_uncropped_image_fractional_coords[:, 0]
3897
+ - signal.axes_manager[1].offset) * (N_x/N_W_h)
3898
+ + (0.5/N_W_h))
3899
+ boundary_pts_in_cropped_image_fractional_coords[:, 1] = \
3900
+ ((boundary_pts_in_uncropped_image_fractional_coords[:, 1]
3901
+ - signal.axes_manager[2].offset) * (N_y/N_W_v)
3902
+ + (1-(1-0.5)/N_W_v))
3903
+ else:
3904
+ boundary_pts_in_cropped_image_fractional_coords = \
3905
+ None
3906
+
3907
+ principal_disk_boundary_pts_in_cropped_image_fractional_coords = \
3908
+ boundary_pts_in_cropped_image_fractional_coords
3909
+
3910
+ Note that
3911
+ ``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
3912
+ should be considered **read-only**.
3913
+
3914
+ """
3915
+ method_name = ("get_principal_disk_boundary_pts"
3916
+ "_in_cropped_image_fractional_coords")
3917
+ method_alias = getattr(self, method_name)
3918
+ result = method_alias(deep_copy=True)
3919
+
3920
+ return result
3921
+
3922
+
3923
+
3924
+ def get_principal_disk_bounding_box_in_cropped_image_fractional_coords(
3925
+ self, deep_copy=_default_deep_copy):
3926
+ r"""Return the bounding box of the "principal" CBED disk, should it
3927
+ exist, in fractional coordinates of the cropped CBED pattern.
3928
+
3929
+ Let ``core_attrs`` denote the attribute
3930
+ :attr:`~fancytypes.Checkable.core_attrs`.
3931
+
3932
+ Parameters
3933
+ ----------
3934
+ deep_copy : `bool`, optional
3935
+ Let
3936
+ ``principal_disk_bounding_box_in_cropped_image_fractional_coords``
3937
+ denote the attribute
3938
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_cropped_image_fractional_coords`.
3939
+
3940
+ If ``deep_copy`` is set to ``True``, then a deep copy of
3941
+ ``principal_disk_bounding_box_in_cropped_image_fractional_coords``
3942
+ is returned. Otherwise, a reference to
3943
+ ``principal_disk_bounding_box_in_cropped_image_fractional_coords``
3944
+ is returned.
3945
+
3946
+ Returns
3947
+ -------
3948
+ principal_disk_bounding_box_in_cropped_image_fractional_coords : `torch.Tensor` (`float`, shape=(core_attrs["disk_boundary_sample_size"], 2)) | `None`
3949
+ The attribute
3950
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_cropped_image_fractional_coords`.
3951
+
3952
+ """
3953
+ params = {"deep_copy": deep_copy}
3954
+ deep_copy = _check_and_convert_deep_copy(params)
3955
+
3956
+ attr_name = ("_principal_disk_bounding_box"
3957
+ "_in_cropped_image_fractional_coords")
3958
+ attr = getattr(self, attr_name)
3959
+
3960
+ if attr is None:
3961
+ method_name = ("_calc_principal_disk_analysis_results"
3962
+ "_and_cache_select_intermediates")
3963
+ method_alias = getattr(self, method_name)
3964
+ self._principal_disk_analysis_results = method_alias()
3965
+
3966
+ attr = getattr(self, attr_name)
3967
+
3968
+ principal_disk_bounding_box_in_cropped_image_fractional_coords = \
3969
+ copy.deepcopy(attr) if (deep_copy == True) else attr
3970
+
3971
+ return principal_disk_bounding_box_in_cropped_image_fractional_coords
3972
+
3973
+
3974
+
3975
+ @property
3976
+ def principal_disk_bounding_box_in_cropped_image_fractional_coords(self):
3977
+ r"""`tuple` | `None`: The bounding box of the unclipped support of the
3978
+ "principal" CBED disk, should it exist, in fractional coordinates of the
3979
+ cropped CBED pattern.
3980
+
3981
+ See the documentation for the attribute
3982
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`
3983
+ for additional context.
3984
+
3985
+ Let ``principal_disk_boundary_pts_in_cropped_image_fractional_coords``
3986
+ denote the attribute
3987
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`.
3988
+
3989
+ ``principal_disk_bounding_box_in_cropped_image_fractional_coords`` is
3990
+ calculated effectively by:
3991
+
3992
+ .. code-block:: python
3993
+
3994
+ import torch
3995
+
3996
+ pts = \
3997
+ principal_disk_boundary_pts_in_cropped_image_fractional_coords
3998
+
3999
+ if pts is not None:
4000
+ bounding_box = (torch.amin(pts[:, 0]).item(),
4001
+ torch.amax(pts[:, 0]).item(),
4002
+ torch.amin(pts[:, 1]).item(),
4003
+ torch.amax(pts[:, 1]).item())
4004
+ else:
4005
+ bounding_box = None
4006
+
4007
+ principal_disk_bounding_box_in_cropped_image_fractional_coords = \
4008
+ bounding_box
4009
+
4010
+ Note that
4011
+ ``principal_disk_bounding_box_in_cropped_image_fractional_coords``
4012
+ should be considered **read-only**.
4013
+
4014
+ """
4015
+ method_name = ("get_principal_disk_bounding_box"
4016
+ "_in_cropped_image_fractional_coords")
4017
+ method_alias = getattr(self, method_name)
4018
+ result = method_alias(deep_copy=True)
4019
+
4020
+ return result
4021
+
4022
+
4023
+
4024
+ def get_disk_supports(self, deep_copy=_default_deep_copy):
4025
+ r"""Return the image stack of the disk supports of the cropped fake CBED
4026
+ pattern.
4027
+
4028
+ Parameters
4029
+ ----------
4030
+ deep_copy : `bool`, optional
4031
+ Let ``disk_supports`` denote the attribute
4032
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`.
4033
+
4034
+ If ``deep_copy`` is set to ``True``, then a deep copy of
4035
+ ``disk_supports`` is returned. Otherwise, a reference to
4036
+ ``disk_supports`` is returned.
4037
+
4038
+ Returns
4039
+ -------
4040
+ disk_supports : `torch.Tensor` (`bool`, ndim=3)
4041
+ The attribute
4042
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`.
4043
+
4044
+ """
4045
+ params = {"deep_copy": deep_copy}
4046
+ deep_copy = _check_and_convert_deep_copy(params)
4047
+
4048
+ if self._disk_supports is None:
4049
+ method_name = "_calc_disk_supports"
4050
+ method_alias = getattr(self, method_name)
4051
+ self._disk_supports = method_alias()
4052
+
4053
+ disk_supports = (self._disk_supports.detach().clone()
4054
+ if (deep_copy == True)
4055
+ else self._disk_supports)
4056
+
4057
+ return disk_supports
4058
+
4059
+
4060
+
4061
+ def _calc_disk_supports(self):
4062
+ N_W_h, N_W_v = self._cropping_window_dims_in_pixels
4063
+ L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
4064
+
4065
+ uncropped_cbed_pattern_disk_supports = \
4066
+ self._cbed_pattern.get_disk_supports(deep_copy=False)
4067
+ uncropped_cbed_pattern_disk_supports = \
4068
+ uncropped_cbed_pattern_disk_supports.cpu().detach().clone()
4069
+
4070
+ signal_to_crop = \
4071
+ self._generate_uncropped_blank_signal()
4072
+ signal_to_crop.data[3:] = \
4073
+ uncropped_cbed_pattern_disk_supports.numpy(force=True)[:]
4074
+
4075
+ optional_params = self._generate_optional_cropping_params()
4076
+
4077
+ kwargs = {"input_signal": signal_to_crop,
4078
+ "optional_params": optional_params}
4079
+ cropped_signal = empix.crop(**kwargs)
4080
+
4081
+ disk_supports = cropped_signal.data[3:]
4082
+ disk_supports_dtype = uncropped_cbed_pattern_disk_supports.dtype
4083
+ disk_supports = torch.from_numpy(disk_supports)
4084
+ disk_supports = disk_supports.to(device=self._device,
4085
+ dtype=disk_supports_dtype)
4086
+ disk_supports[:, :T, :] = 0
4087
+ disk_supports[:, max(N_W_v-B, 0):, :] = 0
4088
+ disk_supports[:, :, :L] = 0
4089
+ disk_supports[:, :, max(N_W_h-R, 0):] = 0
4090
+
4091
+ return disk_supports
4092
+
4093
+
4094
+
4095
+ @property
4096
+ def disk_supports(self):
4097
+ r"""`torch.Tensor`: The image stack of the disk supports of the cropped
4098
+ fake CBED pattern.
4099
+
4100
+ See the documentation for the attribute
4101
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal` for additional
4102
+ context.
4103
+
4104
+ Let ``signal``, ``device``, and ``core_attrs`` denote the attributes
4105
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal`,
4106
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.device`, and
4107
+ :attr:`~fancytypes.Checkable.core_attrs` respectively.
4108
+
4109
+ ``disk_supports`` is calculated effectively by:
4110
+
4111
+ .. code-block:: python
4112
+
4113
+ N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
4114
+ L, R, B, T = core_attrs["mask_frame"]
4115
+
4116
+ disk_supports = signal.data[3:]
4117
+ disk_supports = torch.from_numpy(disk_supports)
4118
+ disk_supports = disk_supports.to(device=device, dtype=bool)
4119
+ disk_supports[:, :T, :] = 0
4120
+ disk_supports[:, max(N_W_v-B, 0):, :] = 0
4121
+ disk_supports[:, :, :L] = 0
4122
+ disk_supports[:, :, max(N_W_h-R, 0):] = 0
4123
+
4124
+ Note that ``disk_supports`` should be considered **read-only**.
4125
+
4126
+ """
4127
+ result = self.get_disk_supports(deep_copy=True)
4128
+
4129
+ return result
4130
+
4131
+
4132
+
4133
+ def get_disk_clipping_registry(self, deep_copy=_default_deep_copy):
4134
+ r"""Return the disk clipping registry of the cropped fake CBED pattern.
4135
+
4136
+ Parameters
4137
+ ----------
4138
+ deep_copy : `bool`, optional
4139
+ Let ``disk_clipping_registry`` denote the attribute
4140
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`.
4141
+
4142
+ If ``deep_copy`` is set to ``True``, then a deep copy of
4143
+ ``disk_clipping_registry`` is returned. Otherwise, a reference to
4144
+ ``disk_clipping_registry`` is returned.
4145
+
4146
+ Returns
4147
+ -------
4148
+ disk_clipping_registry : `torch.Tensor` (`bool`, ndim=1)
4149
+ The attribute
4150
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`.
4151
+
4152
+ """
4153
+ params = {"deep_copy": deep_copy}
4154
+ deep_copy = _check_and_convert_deep_copy(params)
4155
+
4156
+ if self._disk_clipping_registry is None:
4157
+ method_name = ("_calc_disk_clipping_registry"
4158
+ "_and_cache_select_intermediates")
4159
+ method_alias = getattr(self, method_name)
4160
+ self._disk_clipping_registry = method_alias()
4161
+
4162
+ disk_clipping_registry = (self._disk_clipping_registry.detach().clone()
4163
+ if (deep_copy == True)
4164
+ else self._disk_clipping_registry)
4165
+
4166
+ return disk_clipping_registry
4167
+
4168
+
4169
+
4170
+ def _calc_disk_clipping_registry_and_cache_select_intermediates(self):
4171
+ num_disks = self._num_disks
4172
+
4173
+ if num_disks > 0:
4174
+ if self._disk_supports is None:
4175
+ method_name = "_calc_disk_supports"
4176
+ method_alias = getattr(self, method_name)
4177
+ self._disk_supports = method_alias()
4178
+ disk_supports = self._disk_supports
4179
+
4180
+ clip_support = self._calc_clip_support()
4181
+
4182
+ disk_clipping_map = disk_supports*clip_support[None, :, :]
4183
+
4184
+ disk_clipping_registry = ((disk_clipping_map.sum(dim=(1, 2)) != 0)
4185
+ + (disk_supports.sum(dim=(1, 2)) == 0))
4186
+ else:
4187
+ disk_clipping_registry = torch.zeros((num_disks,),
4188
+ device=self._device,
4189
+ dtype=torch.bool)
4190
+
4191
+ return disk_clipping_registry
4192
+
4193
+
4194
+
4195
+ def _calc_clip_support(self):
4196
+ cbed_pattern = self._cbed_pattern
4197
+
4198
+ cbed_pattern_core_attrs = cbed_pattern.get_core_attrs(deep_copy=False)
4199
+
4200
+ illumination_support = \
4201
+ cbed_pattern.get_illumination_support(deep_copy=False)
4202
+ illumination_support = \
4203
+ illumination_support.cpu().detach().clone()
4204
+
4205
+ L, R, B, T = cbed_pattern._mask_frame
4206
+
4207
+ N_x = cbed_pattern_core_attrs["num_pixels_across_pattern"]
4208
+ N_y = N_x
4209
+
4210
+ signal_to_crop = self._generate_uncropped_blank_signal()
4211
+ signal_to_crop.data[0] = illumination_support.numpy(force=True)
4212
+ signal_to_crop.data[0, :T, :] = 0
4213
+ signal_to_crop.data[0, max(N_y-B, 0):, :] = 0
4214
+ signal_to_crop.data[0, :, :L] = 0
4215
+ signal_to_crop.data[0, :, max(N_x-R, 0):] = 0
4216
+
4217
+ optional_params = self._generate_optional_cropping_params()
4218
+
4219
+ kwargs = {"input_signal": signal_to_crop,
4220
+ "optional_params": optional_params}
4221
+ cropped_signal = empix.crop(**kwargs)
4222
+
4223
+ clip_support = torch.from_numpy(1.0-cropped_signal.data[0])
4224
+ clip_support = clip_support.to(device=self._device, dtype=torch.float)
4225
+ for _ in range(2):
4226
+ clip_support = torch.unsqueeze(clip_support, dim=0)
4227
+
4228
+ conv_weights = torch.ones((1, 1, 3, 3), device=self._device)
4229
+
4230
+ N_W_h, N_W_v = self._cropping_window_dims_in_pixels
4231
+ L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
4232
+
4233
+ kwargs = {"input": clip_support,
4234
+ "weight": conv_weights,
4235
+ "padding": "same"}
4236
+ clip_support = (torch.nn.functional.conv2d(**kwargs) != 0)[0, 0]
4237
+ clip_support[:T+1, :] = True
4238
+ clip_support[max(N_W_v-B-1, 0):, :] = True
4239
+ clip_support[:, :L+1] = True
4240
+ clip_support[:, max(N_W_h-R-1, 0):] = True
4241
+
4242
+ return clip_support
4243
+
4244
+
4245
+
4246
+ @property
4247
+ def disk_clipping_registry(self):
4248
+ r"""`torch.Tensor`: The disk clipping registry of the cropped fake CBED
4249
+ pattern.
4250
+
4251
+ See the summary documentation for the classes
4252
+ :class:`fakecbed.discretized.CBEDPattern`, and
4253
+ :class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
4254
+
4255
+ Let ``num_disks``, ``disk_supports``, ``illumination_support``, and
4256
+ ``image`` denote the attributes
4257
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`,
4258
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`,
4259
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.illumination_support`,
4260
+ and :attr:`fakecbed.discretized.CroppedCBEDPattern.image`
4261
+ respectively. Moreover, let ``k`` be a nonnegative integer less than
4262
+ ``num_disks``.
4263
+
4264
+ ``disk_clipping_registry`` is a one-dimensional PyTorch tensor of length
4265
+ equal to ``num_disks``. If ``disk_clipping_registry[k]`` is equal to
4266
+ ``False``, then the image stored in ``disk_supports[k]`` has at least
4267
+ one nonzero pixel, and that the position of every nonzero pixel of said
4268
+ image is at least two pixels away from (i.e. at least pixel-wise
4269
+ next-nearest neighbours to) the position of every zero-valued pixel of
4270
+ the image stored in ``illumination_support``, at least two pixels away
4271
+ from the position of every pixel inside any of the mask frames that
4272
+ appears in the image stored in ``image``, and at least one pixel away
4273
+ from every pixel bordering the image stored in ``image``. Otherwise, if
4274
+ ``disk_clipping_registry[k]`` is equal to ``True``, then the opposite of
4275
+ the above scenario is true.
4276
+
4277
+ Note that there are potentially two mask frames that may appear in the
4278
+ image stored in ``image``: the mask frame applied to the uncropped fake
4279
+ CBED pattern, and the mask frame that is applied after cropping the fake
4280
+ CBED pattern.
4281
+
4282
+ Note that ``disk_clipping_registry`` should be considered **read-only**.
4283
+
4284
+ """
4285
+ result = self.get_disk_clipping_registry(deep_copy=True)
4286
+
4287
+ return result
4288
+
4289
+
4290
+
4291
+ def get_disk_absence_registry(self, deep_copy=_default_deep_copy):
4292
+ r"""Return the disk absence registry of the cropped fake CBED pattern.
4293
+
4294
+ Parameters
4295
+ ----------
4296
+ deep_copy : `bool`, optional
4297
+ Let ``disk_absence_registry`` denote the attribute
4298
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`.
4299
+
4300
+ If ``deep_copy`` is set to ``True``, then a deep copy of
4301
+ ``disk_absence_registry`` is returned. Otherwise, a reference to
4302
+ ``disk_absence_registry`` is returned.
4303
+
4304
+ Returns
4305
+ -------
4306
+ disk_absence_registry : `torch.Tensor` (`bool`, ndim=1)
4307
+ The attribute
4308
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`.
4309
+
4310
+ """
4311
+ params = {"deep_copy": deep_copy}
4312
+ deep_copy = _check_and_convert_deep_copy(params)
4313
+
4314
+ if self._disk_absence_registry is None:
4315
+ method_name = ("_calc_disk_absence_registry"
4316
+ "_and_cache_select_intermediates")
4317
+ method_alias = getattr(self, method_name)
4318
+ self._disk_absence_registry = method_alias()
4319
+
4320
+ disk_absence_registry = (self._disk_absence_registry.detach().clone()
4321
+ if (deep_copy == True)
4322
+ else self._disk_absence_registry)
4323
+
4324
+ return disk_absence_registry
4325
+
4326
+
4327
+
4328
+ def _calc_disk_absence_registry_and_cache_select_intermediates(self):
4329
+ num_disks = self._num_disks
4330
+
4331
+ if num_disks > 0:
4332
+ if self._disk_supports is None:
4333
+ method_name = "_calc_disk_supports"
4334
+ method_alias = getattr(self, method_name)
4335
+ self._disk_supports = method_alias()
4336
+ disk_supports = self._disk_supports
4337
+
4338
+ disk_absence_registry = (disk_supports.sum(dim=(1, 2)) == 0)
4339
+ else:
4340
+ disk_absence_registry = torch.zeros((num_disks,),
4341
+ device=self._device,
4342
+ dtype=torch.bool)
4343
+
4344
+ return disk_absence_registry
4345
+
4346
+
4347
+
4348
+ @property
4349
+ def disk_absence_registry(self):
4350
+ r"""`torch.Tensor`: The disk absence registry of the cropped fake CBED
4351
+ pattern.
4352
+
4353
+ See the summary documentation for the classes
4354
+ :class:`fakecbed.discretized.CBEDPattern`, and
4355
+ :class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
4356
+
4357
+ Let ``num_disks``, and ``disk_supports`` denote the attributes
4358
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`, and
4359
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_supports`
4360
+ respectively. Moreover, let ``k`` be a nonnegative integer less than
4361
+ ``num_disks``.
4362
+
4363
+ ``disk_absence_registry`` is a one-dimensional PyTorch tensor of length
4364
+ equal to ``num_disks``. If ``disk_absence_registry[k]`` is equal to
4365
+ ``False``, then the image stored in ``disk_supports[k]`` has at least
4366
+ one nonzero pixel. Otherwise, if ``disk_absence_registry[k]`` is equal
4367
+ to ``True``, then the opposite of the above scenario is true.
4368
+
4369
+ Note that ``disk_absence_registry`` should be considered **read-only**.
4370
+
4371
+ """
4372
+ result = self.get_disk_absence_registry(deep_copy=True)
4373
+
4374
+ return result
4375
+
4376
+
4377
+
4378
+ @property
4379
+ def principal_disk_is_clipped(self):
4380
+ r"""`bool`: Equals ``True`` if the "principal" CBED disk either does not
4381
+ exists, or it exists and is clipped in image of the cropped fake CBED
4382
+ pattern.
4383
+
4384
+ See the summary documentation for the class
4385
+ :class:`fakecbed.discretized.CroppedCBEDPattern` for additional context,
4386
+ as well as the documentation for the attribute
4387
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`.
4388
+
4389
+ Let ``core_attrs``, ``num_disks``, and ``disk_clipping_registry`` denote
4390
+ the attributes :attr:`~fancytypes.Checkable.core_attrs`,
4391
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`, and
4392
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_clipping_registry`
4393
+ respectively.
4394
+
4395
+ ``principal_disk_is_clipped`` is calculated effectively by:
4396
+
4397
+ .. code-block:: python
4398
+
4399
+ principal_disk_idx = \
4400
+ core_attrs["principal_disk_idx"]
4401
+ principal_disk_is_clipped = \
4402
+ (disk_clipping_registry[principal_disk_idx].item()
4403
+ if (principal_disk_idx < num_disks)
4404
+ else True)
4405
+
4406
+ Note that ``principal_disk_is_clipped`` should be considered
4407
+ **read-only**.
4408
+
4409
+ """
4410
+ if self._principal_disk_is_clipped is None:
4411
+ method_name = ("_calc_principal_disk_is_clipped"
4412
+ "_and_cache_select_intermediates")
4413
+ method_alias = getattr(self, method_name)
4414
+ self._principal_disk_is_clipped = method_alias()
4415
+
4416
+ result = self._principal_disk_is_clipped
4417
+
4418
+ return result
4419
+
4420
+
4421
+
4422
+ def _calc_principal_disk_is_clipped_and_cache_select_intermediates(self):
4423
+ principal_disk_idx = self._principal_disk_idx
4424
+ num_disks = self._num_disks
4425
+
4426
+ if principal_disk_idx < num_disks:
4427
+ if self._disk_clipping_registry is None:
4428
+ method_name = ("_calc_disk_clipping_registry"
4429
+ "_and_cache_select_intermediates")
4430
+ method_alias = getattr(self, method_name)
4431
+ self._disk_clipping_registry = method_alias()
4432
+ disk_clipping_registry = self._disk_clipping_registry
4433
+
4434
+ disk_idx = principal_disk_idx
4435
+ principal_disk_is_clipped = disk_clipping_registry[disk_idx].item()
4436
+ else:
4437
+ principal_disk_is_clipped = True
4438
+
4439
+ return principal_disk_is_clipped
4440
+
4441
+
4442
+
4443
+ @property
4444
+ def principal_disk_is_absent(self):
4445
+ r"""`bool`: Equals ``True`` if the "principal" CBED disk either does not
4446
+ exists, or it exists and is absent from the image of the cropped fake
4447
+ CBED pattern.
4448
+
4449
+ See the summary documentation for the class
4450
+ :class:`fakecbed.discretized.CroppedCBEDPattern` for additional context,
4451
+ as well as the documentation for the attribute
4452
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`.
4453
+
4454
+ Let ``core_attrs``, ``num_disks``, and ``disk_absence_registry`` denote
4455
+ the attributes :attr:`~fancytypes.Checkable.core_attrs`,
4456
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.num_disks`, and
4457
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_absence_registry`
4458
+ respectively.
4459
+
4460
+ ``principal_disk_is_absent`` is calculated effectively by:
4461
+
4462
+ .. code-block:: python
4463
+
4464
+ principal_disk_idx = \
4465
+ core_attrs["principal_disk_idx"]
4466
+ principal_disk_is_absent = \
4467
+ (disk_absence_registry[principal_disk_idx].item()
4468
+ if (principal_disk_idx < num_disks)
4469
+ else True)
4470
+
4471
+ Note that ``principal_disk_is_absent`` should be considered
4472
+ **read-only**.
4473
+
4474
+ """
4475
+ if self._principal_disk_is_absent is None:
4476
+ method_name = ("_calc_principal_disk_is_absent"
4477
+ "_and_cache_select_intermediates")
4478
+ method_alias = getattr(self, method_name)
4479
+ self._principal_disk_is_absent = method_alias()
4480
+
4481
+ result = self._principal_disk_is_absent
4482
+
4483
+ return result
4484
+
4485
+
4486
+
4487
+ def _calc_principal_disk_is_absent_and_cache_select_intermediates(self):
4488
+ principal_disk_idx = self._principal_disk_idx
4489
+ num_disks = self._num_disks
4490
+
4491
+ if principal_disk_idx < num_disks:
4492
+ if self._disk_absence_registry is None:
4493
+ method_name = ("_calc_disk_absence_registry"
4494
+ "_and_cache_select_intermediates")
4495
+ method_alias = getattr(self, method_name)
4496
+ self._disk_absence_registry = method_alias()
4497
+ disk_absence_registry = self._disk_absence_registry
4498
+
4499
+ disk_idx = principal_disk_idx
4500
+ principal_disk_is_absent = disk_absence_registry[disk_idx].item()
4501
+ else:
4502
+ principal_disk_is_absent = True
4503
+
4504
+ return principal_disk_is_absent
4505
+
4506
+
4507
+
4508
+ def get_signal(self, deep_copy=_default_deep_copy):
4509
+ r"""Return the hyperspy signal representation of the cropped fake CBED
4510
+ pattern.
4511
+
4512
+ Parameters
4513
+ ----------
4514
+ deep_copy : `bool`, optional
4515
+ Let ``signal`` denote the attribute
4516
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal`.
4517
+
4518
+ If ``deep_copy`` is set to ``True``, then a deep copy of ``signal``
4519
+ is returned. Otherwise, a reference to ``signal`` is returned.
4520
+
4521
+ Returns
4522
+ -------
4523
+ signal : :class:`hyperspy._signals.signal2d.Signal2D`
4524
+ The attribute
4525
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal`.
4526
+
4527
+ """
4528
+ params = {"deep_copy": deep_copy}
4529
+ deep_copy = _check_and_convert_deep_copy(params)
4530
+
4531
+ if self._signal is None:
4532
+ method_name = "_calc_signal_and_cache_select_intermediates"
4533
+ method_alias = getattr(self, method_name)
4534
+ self._signal = method_alias()
4535
+
4536
+ signal = (copy.deepcopy(self._signal)
4537
+ if (deep_copy == True)
4538
+ else self._signal)
4539
+
4540
+ return signal
4541
+
4542
+
4543
+
4544
+ def _calc_signal_and_cache_select_intermediates(self):
4545
+ method_name = "_calc_signal_metadata_and_cache_select_intermediates"
4546
+ method_alias = getattr(self, method_name)
4547
+ signal_metadata = method_alias()
4548
+
4549
+ optional_params = self._generate_optional_cropping_params()
4550
+
4551
+ kwargs = {"input_signal": \
4552
+ self._cbed_pattern.get_signal(deep_copy=False),
4553
+ "optional_params": \
4554
+ optional_params}
4555
+ signal_data = empix.crop(**kwargs).data
4556
+
4557
+ N_W_h, N_W_v = self._cropping_window_dims_in_pixels
4558
+ L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
4559
+
4560
+ signal_data[:, :T, :] = 0
4561
+ signal_data[:, max(N_W_v-B, 0):, :] = 0
4562
+ signal_data[:, :, :L] = 0
4563
+ signal_data[:, :, max(N_W_h-R, 0):] = 0
4564
+
4565
+ matrix_to_normalize = torch.from_numpy(signal_data[0])
4566
+ kwargs = {"input_matrix": matrix_to_normalize.to(device=self._device)}
4567
+ normalized_matrix = self._cbed_pattern._normalize_matrix(**kwargs)
4568
+
4569
+ signal_data[0] = \
4570
+ normalized_matrix.cpu().detach().clone().numpy(force=True)
4571
+
4572
+ signal = \
4573
+ hyperspy.signals.Signal2D(data=signal_data,
4574
+ metadata=signal_metadata)
4575
+ signal.axes_manager = \
4576
+ self._generate_cropped_blank_signal().axes_manager
4577
+
4578
+ signal.axes_manager[0].name = \
4579
+ r"cropped fake CBED pattern attribute"
4580
+ signal.axes_manager[1].name = \
4581
+ r"fractional horizontal coordinate of uncropped fake CBED pattern"
4582
+ signal.axes_manager[2].name = \
4583
+ r"fractional vertical coordinate of uncropped fake CBED pattern"
4584
+
4585
+ return signal
4586
+
4587
+
4588
+
4589
+ def _calc_signal_metadata_and_cache_select_intermediates(self):
4590
+ cbed_pattern_signal = self._cbed_pattern.get_signal(deep_copy=False)
4591
+
4592
+ title = "Cropped " + cbed_pattern_signal.metadata.General.title
4593
+
4594
+ pre_serialized_core_attrs = self.pre_serialize()
4595
+
4596
+ attr_name_subset = ("_principal_disk_analysis_results",
4597
+ "_disk_clipping_registry",
4598
+ "_disk_absence_registry",
4599
+ "_principal_disk_is_clipped",
4600
+ "_principal_disk_is_absent")
4601
+ for attr_name in attr_name_subset:
4602
+ attr = getattr(self, attr_name)
4603
+ if attr is None:
4604
+ method_name = ("_calc{}_and_cache_select"
4605
+ "_intermediates").format(attr_name)
4606
+ method_alias = getattr(self, method_name)
4607
+ attr = method_alias()
4608
+ setattr(self, attr_name, attr)
4609
+
4610
+ fakecbed_metadata = \
4611
+ cbed_pattern_signal.metadata.FakeCBED.as_dictionary()
4612
+ fakecbed_metadata["pre_serialized_core_attrs"] = \
4613
+ pre_serialized_core_attrs
4614
+
4615
+ attr_name_subset += ("_principal_disk_bounding_box"
4616
+ "_in_uncropped_image_fractional_coords",
4617
+ "_principal_disk_bounding_box"
4618
+ "_in_cropped_image_fractional_coords",
4619
+ "_principal_disk_boundary_pts"
4620
+ "_in_uncropped_image_fractional_coords",
4621
+ "_principal_disk_boundary_pts"
4622
+ "_in_cropped_image_fractional_coords")
4623
+ for attr_name in attr_name_subset[1:]:
4624
+ key = attr_name[1:]
4625
+ attr = getattr(self, attr_name)
4626
+ metadata_item = self._convert_attr_to_metadata_item(attr)
4627
+ fakecbed_metadata[key] = metadata_item
4628
+
4629
+ signal_metadata = {"General": {"title": title},
4630
+ "Signal": {"pixel value units": "dimensionless"},
4631
+ "FakeCBED": fakecbed_metadata}
4632
+
4633
+ return signal_metadata
4634
+
4635
+
4636
+
4637
+ def _convert_attr_to_metadata_item(self, attr):
4638
+ if isinstance(attr, (tuple, bool)):
4639
+ metadata_item = attr
4640
+ else:
4641
+ if len(attr.shape) == 1:
4642
+ metadata_item = tuple(elem
4643
+ for elem
4644
+ in attr.cpu().detach().clone().tolist())
4645
+ else:
4646
+ metadata_item = tuple(tuple(pt)
4647
+ for pt
4648
+ in attr.cpu().detach().clone().tolist())
4649
+
4650
+ return metadata_item
4651
+
4652
+
4653
+
4654
+ @property
4655
+ def signal(self):
4656
+ r"""`hyperspy._signals.signal2d.Signal2D`: The hyperspy signal
4657
+ representation of the cropped fake CBED pattern.
4658
+
4659
+ See the summary documentation for the classes
4660
+ :class:`fakecbed.discretized.CBEDPattern` and
4661
+ :class:`fakecbed.discretized.CroppedCBEDPattern` for additional context.
4662
+
4663
+ Let
4664
+ ``principal_disk_bounding_box_in_uncropped_image_fractional_coords``,
4665
+ ``principal_disk_bounding_box_in_cropped_image_fractional_coords``,
4666
+ ``principal_disk_boundary_pts_in_uncropped_image_fractional_coords``,
4667
+ ``principal_disk_boundary_pts_in_cropped_image_fractional_coords``, and
4668
+ ``core_attrs`` denote the attributes
4669
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_uncropped_image_fractional_coords`,
4670
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_bounding_box_in_cropped_image_fractional_coords`,
4671
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_uncropped_image_fractional_coords`,
4672
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.principal_disk_boundary_pts_in_cropped_image_fractional_coords`,
4673
+ and :attr:`~fancytypes.Checkable.core_attrs` respectively. Furthermore,
4674
+ let ``pre_serialize`` denote the method
4675
+ :meth:`~fancytypes.PreSerializable.pre_serialize`.
4676
+
4677
+ ``signal`` is calculated effectively by:
4678
+
4679
+ .. code-block:: python
4680
+
4681
+ import empix
4682
+ import numpy as np
4683
+
4684
+ cbed_pattern = core_attrs["cbed_pattern"]
4685
+ N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
4686
+ L, R, B, T = core_attrs["mask_frame"]
4687
+
4688
+ cbed_pattern_signal = cbed_pattern.signal
4689
+
4690
+ title = "Cropped " + cbed_pattern_signal.metadata.General.title
4691
+
4692
+ cropping_window_center = \
4693
+ core_attrs["cropping_window_center"]
4694
+ cropping_window_dims_in_pixels = \
4695
+ core_attrs["cropping_window_dims_in_pixels"]
4696
+
4697
+ kwargs = {"center": cropping_window_center,
4698
+ "window_dims": cropping_window_dims_in_pixels,
4699
+ "pad_mode": "zeros",
4700
+ "apply_symmetric_mask": False}
4701
+ optional_params = empix.OptionalCroppingParams(**kwargs)
4702
+
4703
+ kwargs = {"input_signal": cbed_pattern_signal,
4704
+ "optional_params": optional_params}
4705
+ signal = empix.crop(**kwargs)
4706
+
4707
+ signal.data[:, :T, :] = 0
4708
+ signal.data[:, max(N_W_v-B, 0):, :] = 0
4709
+ signal.data[:, :, :L] = 0
4710
+ signal.data[:, :, max(N_W_h-R, 0):] = 0
4711
+
4712
+ # Normalize ``signal.data[0]``.
4713
+ matrix = signal.data[0]
4714
+ if matrix.max()-matrix.min() > 0:
4715
+ normalization_weight = 1 / (matrix.max()-matrix.min())
4716
+ normalization_bias = -normalization_weight*matrix.min()
4717
+ matrix = (matrix*normalization_weight
4718
+ + normalization_bias).clip(min=0, max=1)
4719
+ else:
4720
+ matrix = np.zeros_like(matrix)
4721
+ signal.data[0] = matrix
4722
+
4723
+ kwargs = {"item_path": "General.title", "value": title}
4724
+ signal.metadata.set_item(**kwargs)
4725
+
4726
+ kwargs["item_path"] = "FakeCBED.pre_serialized_core_attrs"
4727
+ kwargs["value"] = pre_serialize()
4728
+ signal.metadata.set_item(**kwargs)
4729
+
4730
+ tensor = \
4731
+ principal_disk_bounding_box_in_uncropped_image_fractional_coords
4732
+ kwargs["item_path"] = \
4733
+ ("FakeCBED.principal_disk_bounding_box"
4734
+ "_in_uncropped_image_fractional_coords")
4735
+ kwargs["value"] = \
4736
+ tuple(tensor.cpu().detach().clone().tolist())
4737
+ _ = \
4738
+ signal.metadata.set_item(**kwargs)
4739
+
4740
+ tensor = \
4741
+ principal_disk_bounding_box_in_cropped_image_fractional_coords
4742
+ kwargs["item_path"] = \
4743
+ ("FakeCBED.principal_disk_bounding_box"
4744
+ "_in_cropped_image_fractional_coords")
4745
+ kwargs["value"] = \
4746
+ tuple(tensor.cpu().detach().clone().tolist())
4747
+ _ = \
4748
+ signal.metadata.set_item(**kwargs)
4749
+
4750
+ tensor = \
4751
+ principal_disk_boundary_pts_in_uncropped_image_fractional_coords
4752
+ kwargs["item_path"] = \
4753
+ ("FakeCBED.principal_disk_boundary_pts"
4754
+ "_in_uncropped_image_fractional_coords")
4755
+ kwargs["value"] = \
4756
+ tuple(tensor.cpu().detach().clone().tolist())
4757
+ _ = \
4758
+ signal.metadata.set_item(**kwargs)
4759
+
4760
+ tensor = \
4761
+ principal_disk_boundary_pts_in_cropped_image_fractional_coords
4762
+ kwargs["item_path"] = \
4763
+ ("FakeCBED.principal_disk_boundary_pts"
4764
+ "_in_cropped_image_fractional_coords")
4765
+ kwargs["value"] = \
4766
+ tuple(tensor.cpu().detach().clone().tolist())
4767
+ _ = \
4768
+ signal.metadata.set_item(**kwargs)
4769
+
4770
+ Note that ``signal`` should be considered **read-only**.
4771
+
4772
+ """
4773
+ result = self.get_signal(deep_copy=True)
4774
+
4775
+ return result
4776
+
4777
+
4778
+
4779
+ @property
4780
+ def image_has_been_overridden(self):
4781
+ r"""`bool`: Equals ``True`` if the image of the fake cropped CBED
4782
+ pattern has been overridden.
4783
+
4784
+ See the summary documentation for the classes
4785
+ :class:`fakecbed.discretized.CBEDPattern` and
4786
+ :class:`fakecbed.discretized.CroppedCBEDPattern` for additional context,
4787
+ as well as the documentation for the attribute
4788
+ :attr:`fakecbed.discretized.CBEDPattern.image_has_been_overridden`.
4789
+
4790
+ Let ``core_attrs`` denote the attribute
4791
+ :attr:`~fancytypes.Checkable.core_attrs`.
4792
+
4793
+ ``image_has_been_overridden`` is calculated effectively by:
4794
+
4795
+ .. code-block:: python
4796
+
4797
+ cbed_pattern = core_attrs["cbed_pattern"]
4798
+ image_has_been_overridden = cbed_pattern.image_has_been_overridden
4799
+
4800
+ Note that ``image_has_been_overridden`` should be considered
4801
+ **read-only**.
4802
+
4803
+ """
4804
+ result = self._image_has_been_overridden
4805
+
4806
+ return result
4807
+
4808
+
4809
+
4810
+ def get_image(self, deep_copy=_default_deep_copy):
4811
+ r"""Return the image of the cropped fake CBED pattern.
4812
+
4813
+ Parameters
4814
+ ----------
4815
+ deep_copy : `bool`, optional
4816
+ Let ``image`` denote the attribute
4817
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.image`.
4818
+
4819
+ If ``deep_copy`` is set to ``True``, then a deep copy of ``image``
4820
+ is returned. Otherwise, a reference to ``image`` is returned.
4821
+
4822
+ Returns
4823
+ -------
4824
+ image : `torch.Tensor` (`float`, ndim=2)
4825
+ The attribute :attr:`fakecbed.discretized.CroppedCBEDPattern.image`.
4826
+
4827
+ """
4828
+ params = {"deep_copy": deep_copy}
4829
+ deep_copy = _check_and_convert_deep_copy(params)
4830
+
4831
+ if self._image is None:
4832
+ method_name = "_calc_image"
4833
+ method_alias = getattr(self, method_name)
4834
+ self._image = method_alias()
4835
+
4836
+ image = (self._image.detach().clone()
4837
+ if (deep_copy == True)
4838
+ else self._image)
4839
+
4840
+ return image
4841
+
4842
+
4843
+
4844
+ def _calc_image(self):
4845
+ uncropped_cbed_pattern_image = \
4846
+ self._cbed_pattern.get_image(deep_copy=False)
4847
+ uncropped_cbed_pattern_image = \
4848
+ uncropped_cbed_pattern_image.cpu().detach().clone()
4849
+
4850
+ signal_to_crop = \
4851
+ self._generate_uncropped_blank_signal()
4852
+ signal_to_crop.data[0] = \
4853
+ uncropped_cbed_pattern_image.numpy(force=True)
4854
+
4855
+ optional_params = self._generate_optional_cropping_params()
4856
+
4857
+ kwargs = {"input_signal": signal_to_crop,
4858
+ "optional_params": optional_params}
4859
+ cropped_signal = empix.crop(**kwargs)
4860
+
4861
+ image = cropped_signal.data[0]
4862
+ image_dtype = uncropped_cbed_pattern_image.dtype
4863
+ image = torch.from_numpy(image)
4864
+ image = image.to(device=self._device, dtype=image_dtype)
4865
+
4866
+ N_W_h, N_W_v = self._cropping_window_dims_in_pixels
4867
+ L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
4868
+
4869
+ image[:T, :] = 0
4870
+ image[max(N_W_v-B, 0):, :] = 0
4871
+ image[:, :L] = 0
4872
+ image[:, max(N_W_h-R, 0):] = 0
4873
+
4874
+ kwargs = {"input_matrix": image}
4875
+ image = self._cbed_pattern._normalize_matrix(**kwargs)
4876
+
4877
+ return image
4878
+
4879
+
4880
+
4881
+ @property
4882
+ def image(self):
4883
+ r"""`torch.Tensor`: The image of the cropped fake CBED pattern.
4884
+
4885
+ See the documentation for the attribute
4886
+ :attr:`fakecbed.discretized.CBEDPattern.signal` for additional
4887
+ context.
4888
+
4889
+ Let ``signal``, ``device``, and ``core_attrs`` denote the attributes
4890
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal`,
4891
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.device`, and
4892
+ :attr:`~fancytypes.Checkable.core_attrs` respectively.
4893
+
4894
+ ``image`` is calculated effectively by:
4895
+
4896
+ .. code-block:: python
4897
+
4898
+ N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
4899
+ L, R, B, T = core_attrs["mask_frame"]
4900
+
4901
+ image = signal.data[0]
4902
+ image_dtype = image.dtype
4903
+ image = torch.from_numpy(image)
4904
+ image = image.to(device=device, dtype=image_dtype)
4905
+ image[:T, :] = 0
4906
+ image[max(N_W_v-B, 0):, :] = 0
4907
+ image[:, :L] = 0
4908
+ image[:, max(N_W_h-R, 0):] = 0
4909
+
4910
+ Note that ``image`` should be considered **read-only**.
4911
+
4912
+ """
4913
+ result = self.get_image(deep_copy=True)
4914
+
4915
+ return result
4916
+
4917
+
4918
+
4919
+ def get_illumination_support(self, deep_copy=_default_deep_copy):
4920
+ r"""Return the illumination support of the cropped fake CBED pattern.
4921
+
4922
+ Parameters
4923
+ ----------
4924
+ deep_copy : `bool`, optional
4925
+ Let ``illumination_support`` denote the attribute
4926
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.illumination_support`.
4927
+
4928
+ If ``deep_copy`` is set to ``True``, then a deep copy of
4929
+ ``illumination_support`` is returned. Otherwise, a reference to
4930
+ ``illumination_support`` is returned.
4931
+
4932
+ Returns
4933
+ -------
4934
+ illumination_support : `torch.Tensor` (`float`, ndim=2)
4935
+ The attribute
4936
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.illumination_support`.
4937
+
4938
+ """
4939
+ params = {"deep_copy": deep_copy}
4940
+ deep_copy = _check_and_convert_deep_copy(params)
4941
+
4942
+ if self._illumination_support is None:
4943
+ method_name = "_calc_illumination_support"
4944
+ method_alias = getattr(self, method_name)
4945
+ self._illumination_support = method_alias()
4946
+
4947
+ illumination_support = (self._illumination_support.detach().clone()
4948
+ if (deep_copy == True)
4949
+ else self._illumination_support)
4950
+
4951
+ return illumination_support
4952
+
4953
+
4954
+
4955
+ def _calc_illumination_support(self):
4956
+ uncropped_cbed_pattern_illumination_support = \
4957
+ self._cbed_pattern.get_illumination_support(deep_copy=False)
4958
+ uncropped_cbed_pattern_illumination_support = \
4959
+ uncropped_cbed_pattern_illumination_support.cpu().detach().clone()
4960
+
4961
+ signal_to_crop = \
4962
+ self._generate_uncropped_blank_signal()
4963
+ signal_to_crop.data[1] = \
4964
+ uncropped_cbed_pattern_illumination_support.numpy(force=True)
4965
+
4966
+ optional_params = self._generate_optional_cropping_params()
4967
+
4968
+ kwargs = {"input_signal": signal_to_crop,
4969
+ "optional_params": optional_params}
4970
+ cropped_signal = empix.crop(**kwargs)
4971
+
4972
+ illumination_support = \
4973
+ cropped_signal.data[1]
4974
+ illumination_support_dtype = \
4975
+ uncropped_cbed_pattern_illumination_support.dtype
4976
+ illumination_support = \
4977
+ torch.from_numpy(illumination_support)
4978
+ illumination_support = \
4979
+ illumination_support.to(device=self._device,
4980
+ dtype=illumination_support_dtype)
4981
+
4982
+ return illumination_support
4983
+
4984
+
4985
+
4986
+ @property
4987
+ def illumination_support(self):
4988
+ r"""`torch.Tensor`: The illumination support of the cropped fake CBED
4989
+ pattern.
4990
+
4991
+ See the documentation for the attribute
4992
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal` for additional
4993
+ context.
4994
+
4995
+ Let ``signal``, and ``device`` denote the attributes
4996
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal`, and
4997
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.device` respectively.
4998
+
4999
+ ``illumination_support`` is calculated effectively by:
5000
+
5001
+ .. code-block:: python
5002
+
5003
+ illumination_support = signal.data[1]
5004
+ illumination_support = torch.from_numpy(illumination_support)
5005
+ illumination_support = illumination_support.to(device=device,
5006
+ dtype=bool)
5007
+
5008
+ Note that ``illumination_support`` should be considered **read-only**.
5009
+
5010
+ """
5011
+ result = self.get_illumination_support(deep_copy=True)
5012
+
5013
+ return result
5014
+
5015
+
5016
+
5017
+ def get_disk_overlap_map(self, deep_copy=_default_deep_copy):
5018
+ r"""Return the image of the disk overlap map of the cropped fake CBED
5019
+ pattern.
5020
+
5021
+ Parameters
5022
+ ----------
5023
+ deep_copy : `bool`, optional
5024
+ Let ``disk_overlap_map`` denote the attribute
5025
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_overlap_map`.
5026
+
5027
+ If ``deep_copy`` is set to ``True``, then a deep copy of
5028
+ ``disk_overlap_map`` is returned. Otherwise, a reference to
5029
+ ``disk_overlap_map`` is returned.
5030
+
5031
+ Returns
5032
+ -------
5033
+ disk_overlap_map : `torch.Tensor` (`int`, ndim=2)
5034
+ The attribute
5035
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.disk_overlap_map`.
5036
+
5037
+ """
5038
+ params = {"deep_copy": deep_copy}
5039
+ deep_copy = _check_and_convert_deep_copy(params)
5040
+
5041
+ if self._disk_overlap_map is None:
5042
+ method_name = ("_calc_disk_overlap_map")
5043
+ method_alias = getattr(self, method_name)
5044
+ self._disk_overlap_map = method_alias()
5045
+
5046
+ disk_overlap_map = (self._disk_overlap_map.detach().clone()
5047
+ if (deep_copy == True)
5048
+ else self._disk_overlap_map)
5049
+
5050
+ return disk_overlap_map
5051
+
5052
+
5053
+
5054
+ def _calc_disk_overlap_map(self):
5055
+ uncropped_cbed_pattern_disk_overlap_map = \
5056
+ self._cbed_pattern.get_disk_overlap_map(deep_copy=False)
5057
+ uncropped_cbed_pattern_disk_overlap_map = \
5058
+ uncropped_cbed_pattern_disk_overlap_map.cpu().detach().clone()
5059
+
5060
+ signal_to_crop = \
5061
+ self._generate_uncropped_blank_signal()
5062
+ signal_to_crop.data[2] = \
5063
+ uncropped_cbed_pattern_disk_overlap_map.numpy(force=True)
5064
+
5065
+ optional_params = self._generate_optional_cropping_params()
5066
+
5067
+ kwargs = {"input_signal": signal_to_crop,
5068
+ "optional_params": optional_params}
5069
+ cropped_signal = empix.crop(**kwargs)
5070
+
5071
+ N_W_h, N_W_v = self._cropping_window_dims_in_pixels
5072
+ L, R, B, T = self._mask_frame # Mask frame of cropped pattern.
5073
+
5074
+ disk_overlap_map = cropped_signal.data[2]
5075
+ disk_overlap_map_dtype = uncropped_cbed_pattern_disk_overlap_map.dtype
5076
+ disk_overlap_map = torch.from_numpy(disk_overlap_map)
5077
+ disk_overlap_map = disk_overlap_map.to(device=self._device,
5078
+ dtype=disk_overlap_map_dtype)
5079
+ disk_overlap_map[:T, :] = 0
5080
+ disk_overlap_map[max(N_W_v-B, 0):, :] = 0
5081
+ disk_overlap_map[:, :L] = 0
5082
+ disk_overlap_map[:, max(N_W_h-R, 0):] = 0
5083
+
5084
+ return disk_overlap_map
5085
+
5086
+
5087
+
5088
+ @property
5089
+ def disk_overlap_map(self):
5090
+ r"""`torch.Tensor`: The image of the disk overlap map of the cropped
5091
+ fake CBED pattern.
5092
+
5093
+ See the documentation for the attribute
5094
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal` for additional
5095
+ context.
5096
+
5097
+ Let ``signal``, ``device``, and ``core_attrs`` denote the attributes
5098
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.signal`,
5099
+ :attr:`fakecbed.discretized.CroppedCBEDPattern.device`, and
5100
+ :attr:`~fancytypes.Checkable.core_attrs` respectively.
5101
+
5102
+ ``disk_overlap_map`` is calculated effectively by:
5103
+
5104
+ .. code-block:: python
5105
+
5106
+ N_W_h, N_W_v = core_attrs["cropping_window_dims_in_pixels"]
5107
+ L, R, B, T = core_attrs["mask_frame"]
5108
+
5109
+ disk_overlap_map = signal.data[2]
5110
+ disk_overlap_map = torch.from_numpy(disk_overlap_map)
5111
+ disk_overlap_map = disk_overlap_map.to(device=device, dtype=int)
5112
+ disk_overlap_map[:T, :] = 0
5113
+ disk_overlap_map[max(N_W_v-B, 0):, :] = 0
5114
+ disk_overlap_map[:, :L] = 0
5115
+ disk_overlap_map[:, max(N_W_h-R, 0):] = 0
5116
+
5117
+ Note that ``disk_overlap_map`` should be considered **read-only**.
5118
+
5119
+ """
5120
+ result = self.get_disk_overlap_map(deep_copy=True)
5121
+
5122
+ return result
5123
+
5124
+
5125
+
5126
+ ###########################
5127
+ ## Define error messages ##
5128
+ ###########################
5129
+
5130
+ _check_and_convert_undistorted_disks_err_msg_1 = \
5131
+ ("The object ``undistorted_disks`` must be a sequence of "
5132
+ "`fakecbed.shapes.NonuniformBoundedShape` objects, where for each element "
5133
+ "``elem`` in ``undistorted_disks``, "
5134
+ "``isinstance(elem.core_attrs['support'], "
5135
+ "(fakecbed.shapes.Circle, fakecbed.shapes.Ellipse))`` evaluates to "
5136
+ "``True``.")
5137
+
5138
+ _check_and_convert_undistorted_misc_shapes_err_msg_1 = \
5139
+ ("The object ``undistorted_misc_shapes`` must be a sequence of objects of "
5140
+ "any of the following types: ("
5141
+ "`fakecbed.shapes.Circle`, "
5142
+ "`fakecbed.shapes.Ellipse`, "
5143
+ "`fakecbed.shapes.Peak`, "
5144
+ "`fakecbed.shapes.Band`, "
5145
+ "`fakecbed.shapes.PlaneWave`, "
5146
+ "`fakecbed.shapes.Arc`, "
5147
+ "`fakecbed.shapes.GenericBlob`, "
5148
+ "`fakecbed.shapes.Orbital`, "
5149
+ "`fakecbed.shapes.Lune`, "
5150
+ "`fakecbed.shapes.NonuniformBoundedShape`).")
5151
+
5152
+ _check_and_convert_distortion_model_err_msg_1 = \
5153
+ ("The dimensions, in units of pixels, of the distortion model sampling "
5154
+ "grid, specified by the object ``distortion_model``, must be divisible "
5155
+ "by the object ``num_pixels_across_pattern``.")
5156
+
5157
+ _check_and_convert_rng_seed_err_msg_1 = \
5158
+ ("The object ``rng_seed`` must be either a nonnegative integer or of the "
5159
+ "type `NoneType`.")
5160
+
5161
+ _check_and_convert_cold_pixels_err_msg_1 = \
5162
+ ("The object ``cold_pixels`` must be a sequence of integer pairs, where "
5163
+ "each integer pair specifies valid pixel coordinates (i.e. row and column "
5164
+ "indices) of a pixel in the discretized fake CBED pattern.")
5165
+
5166
+ _check_and_convert_overriding_image_err_msg_1 = \
5167
+ ("The object ``overriding_image`` must have dimensions, in units of "
5168
+ "pixels, equal to those of the original CBED pattern intensity image "
5169
+ "being overridden, which in this case are ``({}, {})``.")
5170
+
5171
+ _cbed_pattern_err_msg_1 = \
5172
+ ("Failed to generate discretized fake CBED pattern. See traceback for "
5173
+ "details.")
5174
+
5175
+ _check_and_convert_cropping_window_center_err_msg_1 = \
5176
+ ("The object ``cropping_window_center`` must be `NoneType` or a pair of "
5177
+ "real numbers.")
5178
+
5179
+ _check_and_convert_cropping_window_dims_in_pixels_err_msg_1 = \
5180
+ ("The object ``cropping_window_dims_in_pixels`` must be `NoneType` or a "
5181
+ "pair of positive integers.")