pytme 0.2.1__cp311-cp311-macosx_14_0_arm64.whl → 0.2.2__cp311-cp311-macosx_14_0_arm64.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.
- {pytme-0.2.1.data → pytme-0.2.2.data}/scripts/match_template.py +147 -93
- {pytme-0.2.1.data → pytme-0.2.2.data}/scripts/postprocess.py +67 -26
- {pytme-0.2.1.data → pytme-0.2.2.data}/scripts/preprocessor_gui.py +175 -85
- pytme-0.2.2.dist-info/METADATA +91 -0
- pytme-0.2.2.dist-info/RECORD +74 -0
- {pytme-0.2.1.dist-info → pytme-0.2.2.dist-info}/WHEEL +1 -1
- scripts/extract_candidates.py +20 -13
- scripts/match_template.py +147 -93
- scripts/match_template_filters.py +154 -95
- scripts/postprocess.py +67 -26
- scripts/preprocessor_gui.py +175 -85
- scripts/refine_matches.py +265 -61
- tme/__init__.py +0 -1
- tme/__version__.py +1 -1
- tme/analyzer.py +451 -809
- tme/backends/__init__.py +40 -11
- tme/backends/_jax_utils.py +185 -0
- tme/backends/cupy_backend.py +111 -223
- tme/backends/jax_backend.py +214 -150
- tme/backends/matching_backend.py +445 -384
- tme/backends/mlx_backend.py +32 -59
- tme/backends/npfftw_backend.py +239 -507
- tme/backends/pytorch_backend.py +21 -145
- tme/density.py +233 -363
- tme/extensions.cpython-311-darwin.so +0 -0
- tme/matching_data.py +322 -285
- tme/matching_exhaustive.py +172 -1493
- tme/matching_optimization.py +143 -106
- tme/matching_scores.py +884 -0
- tme/matching_utils.py +280 -386
- tme/memory.py +377 -0
- tme/orientations.py +52 -12
- tme/parser.py +3 -4
- tme/preprocessing/_utils.py +61 -32
- tme/preprocessing/compose.py +7 -3
- tme/preprocessing/frequency_filters.py +49 -39
- tme/preprocessing/tilt_series.py +34 -40
- tme/preprocessor.py +560 -526
- tme/structure.py +491 -188
- tme/types.py +5 -3
- pytme-0.2.1.dist-info/METADATA +0 -73
- pytme-0.2.1.dist-info/RECORD +0 -73
- tme/helpers.py +0 -881
- tme/matching_constrained.py +0 -195
- {pytme-0.2.1.data → pytme-0.2.2.data}/scripts/estimate_ram_usage.py +0 -0
- {pytme-0.2.1.data → pytme-0.2.2.data}/scripts/preprocess.py +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.2.dist-info}/LICENSE +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.2.dist-info}/entry_points.txt +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.2.dist-info}/top_level.txt +0 -0
tme/density.py
CHANGED
@@ -18,24 +18,23 @@ import numpy as np
|
|
18
18
|
import skimage.io as skio
|
19
19
|
|
20
20
|
from scipy.ndimage import (
|
21
|
+
zoom,
|
21
22
|
laplace,
|
22
|
-
generic_gradient_magnitude,
|
23
|
-
minimum_filter,
|
24
23
|
sobel,
|
24
|
+
minimum_filter,
|
25
25
|
binary_erosion,
|
26
|
-
|
26
|
+
generic_gradient_magnitude,
|
27
27
|
)
|
28
28
|
from scipy.spatial import ConvexHull
|
29
29
|
|
30
|
+
from .types import NDArray
|
31
|
+
from .backends import NumpyFFTWBackend
|
30
32
|
from .structure import Structure
|
31
33
|
from .matching_utils import (
|
32
|
-
minimum_enclosing_box,
|
33
34
|
array_to_memmap,
|
34
35
|
memmap_to_array,
|
36
|
+
minimum_enclosing_box,
|
35
37
|
)
|
36
|
-
from .types import NDArray
|
37
|
-
from .helpers import is_gzipped
|
38
|
-
from .backends import NumpyFFTWBackend
|
39
38
|
|
40
39
|
|
41
40
|
class Density:
|
@@ -44,44 +43,47 @@ class Density:
|
|
44
43
|
|
45
44
|
Parameters
|
46
45
|
----------
|
47
|
-
data :
|
48
|
-
Array of
|
49
|
-
origin :
|
50
|
-
Origin of the coordinate system
|
51
|
-
sampling_rate :
|
52
|
-
Sampling rate along data axis
|
46
|
+
data : array_like
|
47
|
+
Array of densities.
|
48
|
+
origin : array_like, optional
|
49
|
+
Origin of the coordinate system, zero by default.
|
50
|
+
sampling_rate : array_like, optional
|
51
|
+
Sampling rate along data axis, one by default.
|
53
52
|
metadata : dict, optional
|
54
53
|
Dictionary with metadata information, empty by default.
|
55
54
|
|
56
55
|
Raises
|
57
56
|
------
|
58
57
|
ValueError
|
59
|
-
|
58
|
+
If metadata is not a dictionary.
|
59
|
+
|
60
|
+
If sampling rate / origin is not defined for a single or all axes.
|
60
61
|
|
61
62
|
Examples
|
62
63
|
--------
|
63
|
-
The following achieves the minimal definition of a :py:class:`Density` instance
|
64
|
+
The following achieves the minimal definition of a :py:class:`Density` instance
|
64
65
|
|
65
66
|
>>> import numpy as np
|
66
67
|
>>> from tme import Density
|
67
68
|
>>> data = np.random.rand(50,70,40)
|
68
|
-
>>> Density(data
|
69
|
+
>>> Density(data=data)
|
69
70
|
|
70
|
-
Optional parameters
|
71
|
-
to the
|
72
|
-
|
73
|
-
:py:attr:`Density.origin` is set to zero
|
74
|
-
to
|
71
|
+
Optional parameters ``origin`` correspond to the coordinate system reference,
|
72
|
+
``sampling_rate`` to the spatial length per axis element, and ``metadata`` to
|
73
|
+
a dictionary with supplementary information. By default,
|
74
|
+
:py:attr:`Density.origin` is set to zero, :py:attr:`Density.sampling_rate`
|
75
|
+
to one, and :py:attr:`Density.metadata` is an empty dictionary. If provided,
|
76
|
+
``origin`` and ``sampling_rate`` either need to be a single value
|
75
77
|
|
76
|
-
>>> Density(data
|
78
|
+
>>> Density(data=data, origin=0, sampling_rate=1)
|
77
79
|
|
78
|
-
|
80
|
+
be specified along each data axis
|
79
81
|
|
80
|
-
>>> Density(data
|
82
|
+
>>> Density(data=data, origin=(0, 0, 0), sampling_rate=(1.5, 1.1, 1.2))
|
81
83
|
|
82
|
-
|
84
|
+
or be a combination of both
|
83
85
|
|
84
|
-
>>> Density(data
|
86
|
+
>>> Density(data=data, origin=0, sampling_rate=(1.5, 1.1, 1.2))
|
85
87
|
"""
|
86
88
|
|
87
89
|
def __init__(
|
@@ -111,7 +113,7 @@ class Density:
|
|
111
113
|
self.metadata = metadata
|
112
114
|
|
113
115
|
def __repr__(self):
|
114
|
-
response = "Density object at {}\nOrigin: {},
|
116
|
+
response = "Density object at {}\nOrigin: {}, Sampling Rate: {}, Shape: {}"
|
115
117
|
return response.format(
|
116
118
|
hex(id(self)),
|
117
119
|
tuple(np.round(self.origin, 3)),
|
@@ -124,22 +126,22 @@ class Density:
|
|
124
126
|
cls, filename: str, subset: Tuple[slice] = None, use_memmap: bool = False
|
125
127
|
) -> "Density":
|
126
128
|
"""
|
127
|
-
Reads
|
129
|
+
Reads a file into a :py:class:`Density` instance.
|
128
130
|
|
129
131
|
Parameters
|
130
132
|
----------
|
131
133
|
filename : str
|
132
134
|
Path to a file in CCP4/MRC, EM, HDF5 or a format supported by
|
133
|
-
skimage.io.imread
|
135
|
+
:obj:`skimage.io.imread`. The file can be gzip compressed.
|
134
136
|
subset : tuple of slices, optional
|
135
137
|
Slices representing the desired subset along each dimension.
|
136
138
|
use_memmap : bool, optional
|
137
|
-
|
139
|
+
Memory map the data contained in ``filename`` to save memory.
|
138
140
|
|
139
141
|
Returns
|
140
142
|
-------
|
141
|
-
Density
|
142
|
-
|
143
|
+
:py:class:`Density`
|
144
|
+
Class instance representing the data in ``filename``.
|
143
145
|
|
144
146
|
References
|
145
147
|
----------
|
@@ -149,7 +151,7 @@ class Density:
|
|
149
151
|
|
150
152
|
Examples
|
151
153
|
--------
|
152
|
-
:py:meth:`Density.from_file` reads files in
|
154
|
+
:py:meth:`Density.from_file` reads files in CCP4/MRC, EM, or a format supported
|
153
155
|
by skimage.io.imread and converts them into a :py:class:`Density` instance. The
|
154
156
|
following outlines how to read a file in the CCP4/MRC format [1]_:
|
155
157
|
|
@@ -163,9 +165,9 @@ class Density:
|
|
163
165
|
>>> subset_slices = (slice(0, 50), slice(0, 50), slice(0, 50))
|
164
166
|
>>> Density.from_file("/path/to/file.mrc", subset=subset_slices)
|
165
167
|
|
166
|
-
|
167
|
-
|
168
|
-
|
168
|
+
Memory mapping can be used to read the file from disk without loading
|
169
|
+
it entirely into memory. This is particularly useful for large datasets
|
170
|
+
or when working with limited memory resources:
|
169
171
|
|
170
172
|
>>> Density.from_file("/path/to/large_file.mrc", use_memmap=True)
|
171
173
|
|
@@ -178,18 +180,17 @@ class Density:
|
|
178
180
|
>>> Density.from_file("/path/to/file.em.gz")
|
179
181
|
|
180
182
|
If the file format is not CCP4/MRC or EM, :py:meth:`Density.from_file` attempts
|
181
|
-
to use skimage.io.imread to read the file [3]_
|
183
|
+
to use :obj:`skimage.io.imread` to read the file [3]_, which does not extract
|
182
184
|
origin or sampling_rate information from the file:
|
183
185
|
|
184
186
|
>>> Density.from_file("/path/to/other_format.tif")
|
185
187
|
|
186
188
|
Notes
|
187
189
|
-----
|
188
|
-
If ``filename`` ends
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
extract origin or sampling_rate information from the file.
|
190
|
+
If ``filename`` ends ".em" or ".h5" it will be parsed as EM or HDF5 file.
|
191
|
+
Otherwise, the default reader is CCP4/MRC and on failure
|
192
|
+
:obj:`skimage.io.imread` is used regardless of extension. The later does
|
193
|
+
not extract origin or sampling_rate information from the file.
|
193
194
|
|
194
195
|
See Also
|
195
196
|
--------
|
@@ -314,7 +315,7 @@ class Density:
|
|
314
315
|
use_memmap = False
|
315
316
|
|
316
317
|
if subset is not None:
|
317
|
-
subset_shape =
|
318
|
+
subset_shape = tuple(x.stop - x.start for x in subset)
|
318
319
|
if np.allclose(subset_shape, data_shape):
|
319
320
|
return cls._load_mrc(
|
320
321
|
filename=filename, subset=None, use_memmap=use_memmap
|
@@ -337,7 +338,6 @@ class Density:
|
|
337
338
|
data = mrc.data
|
338
339
|
|
339
340
|
if not np.all(crs_index == (0, 1, 2)):
|
340
|
-
data.setflags(write=True)
|
341
341
|
data = np.transpose(data, crs_index)
|
342
342
|
start = np.take(start, crs_index)
|
343
343
|
|
@@ -642,7 +642,7 @@ class Density:
|
|
642
642
|
sampling_rate: NDArray = np.ones(1),
|
643
643
|
origin: Tuple[float] = None,
|
644
644
|
weight_type: str = "atomic_weight",
|
645
|
-
|
645
|
+
weight_type_args: Dict = {},
|
646
646
|
chain: str = None,
|
647
647
|
filter_by_elements: Set = None,
|
648
648
|
filter_by_residues: Set = None,
|
@@ -672,6 +672,8 @@ class Density:
|
|
672
672
|
weight_type : str, optional
|
673
673
|
Which weight should be given to individual atoms. For valid values
|
674
674
|
see :py:meth:`tme.structure.Structure.to_volume`.
|
675
|
+
weight_type_args : dict, optional
|
676
|
+
Additional arguments for atom weight computation.
|
675
677
|
chain : str, optional
|
676
678
|
The chain that should be extracted from the structure. If multiple chains
|
677
679
|
should be selected, they needto be a comma separated string,
|
@@ -680,8 +682,6 @@ class Density:
|
|
680
682
|
Set of atomic elements to keep. Default is all atoms.
|
681
683
|
filter_by_residues : set, optional
|
682
684
|
Set of residues to keep. Default is all residues.
|
683
|
-
scattering_args : dict, optional
|
684
|
-
Additional arguments for scattering factor computation.
|
685
685
|
|
686
686
|
Returns
|
687
687
|
-------
|
@@ -738,14 +738,14 @@ class Density:
|
|
738
738
|
>>> )
|
739
739
|
|
740
740
|
:py:meth:`Density.from_structure` supports a variety of methods to convert
|
741
|
-
atoms into densities. In
|
742
|
-
'van_der_waals_radius' its possible to use experimentally determined
|
743
|
-
factors from various sources:
|
741
|
+
atoms into densities. In addition to 'atomic_weight', 'atomic_number',
|
742
|
+
and 'van_der_waals_radius', its possible to use experimentally determined
|
743
|
+
scattering factors from various sources:
|
744
744
|
|
745
745
|
>>> density = Density.from_structure(
|
746
746
|
>>> filename_or_structure = path_to_structure,
|
747
747
|
>>> weight_type = "scattering_factors",
|
748
|
-
>>>
|
748
|
+
>>> weight_type_args={"source": "dt1969"}
|
749
749
|
>>> )
|
750
750
|
|
751
751
|
or a lowpass filtered representation introduced in [1]_:
|
@@ -753,7 +753,7 @@ class Density:
|
|
753
753
|
>>> density = Density.from_structure(
|
754
754
|
>>> filename_or_structure = path_to_structure,
|
755
755
|
>>> weight_type = "lowpass_scattering_factors",
|
756
|
-
>>>
|
756
|
+
>>> weight_type_args={"source": "dt1969"}
|
757
757
|
>>> )
|
758
758
|
|
759
759
|
See Also
|
@@ -775,27 +775,27 @@ class Density:
|
|
775
775
|
origin=origin,
|
776
776
|
chain=chain,
|
777
777
|
weight_type=weight_type,
|
778
|
-
|
778
|
+
weight_type_args=weight_type_args,
|
779
779
|
)
|
780
780
|
|
781
781
|
return cls(
|
782
782
|
data=volume,
|
783
783
|
origin=origin,
|
784
784
|
sampling_rate=sampling_rate,
|
785
|
-
metadata=structure.
|
785
|
+
metadata=structure.metadata.copy(),
|
786
786
|
)
|
787
787
|
|
788
788
|
def to_file(self, filename: str, gzip: bool = False) -> None:
|
789
789
|
"""
|
790
|
-
Writes
|
790
|
+
Writes class instance to disk.
|
791
791
|
|
792
792
|
Parameters
|
793
793
|
----------
|
794
794
|
filename : str
|
795
795
|
Path to write to.
|
796
796
|
gzip : bool, optional
|
797
|
-
|
798
|
-
|
797
|
+
Gzip compress the output and add corresponding suffix to filename
|
798
|
+
if not present. False by default.
|
799
799
|
|
800
800
|
References
|
801
801
|
----------
|
@@ -811,7 +811,7 @@ class Density:
|
|
811
811
|
>>> import numpy as np
|
812
812
|
>>> from tme import Density
|
813
813
|
>>> data = np.random.rand(50,50,50)
|
814
|
-
>>> dens = Density(data
|
814
|
+
>>> dens = Density(data=data, origin=(0, 0, 0), sampling_rate=(1, 1, 1))
|
815
815
|
>>> dens.to_file("example.mrc")
|
816
816
|
|
817
817
|
The output file can also be directly ``gzip`` compressed. The corresponding
|
@@ -826,14 +826,14 @@ class Density:
|
|
826
826
|
In addition, a variety of image file formats are supported [3]_:
|
827
827
|
|
828
828
|
>>> data = np.random.rand(50,50)
|
829
|
-
>>> dens = Density(data
|
829
|
+
>>> dens = Density(data=data, origin=(0, 0), sampling_rate=(1, 1))
|
830
830
|
>>> dens.to_file("example.tiff")
|
831
831
|
|
832
832
|
Notes
|
833
833
|
-----
|
834
|
-
If ``filename``
|
835
|
-
|
836
|
-
|
834
|
+
If ``filename`` endswith ".em" or ".h5" a EM file or HDF5 file will be created.
|
835
|
+
The default output format is CCP4/MRC and on failure, :obj:`skimage.io.imsave`
|
836
|
+
is used.
|
837
837
|
|
838
838
|
See Also
|
839
839
|
--------
|
@@ -854,7 +854,7 @@ class Density:
|
|
854
854
|
|
855
855
|
def _save_mrc(self, filename: str, gzip: bool = False) -> None:
|
856
856
|
"""
|
857
|
-
Writes
|
857
|
+
Writes class instance to disk as mrc file.
|
858
858
|
|
859
859
|
Parameters
|
860
860
|
----------
|
@@ -974,7 +974,7 @@ class Density:
|
|
974
974
|
self.metadata["std"] = self.metadata.get("std", 0)
|
975
975
|
self.metadata["min"] = self.metadata.get("min", 0)
|
976
976
|
self.metadata["max"] = self.metadata.get("max", 0)
|
977
|
-
if
|
977
|
+
if not isinstance(self.data, np.memmap):
|
978
978
|
self.metadata["mean"] = self.data.mean()
|
979
979
|
self.metadata["std"] = self.data.std()
|
980
980
|
self.metadata["min"] = self.data.min()
|
@@ -986,11 +986,15 @@ class Density:
|
|
986
986
|
@property
|
987
987
|
def empty(self) -> "Density":
|
988
988
|
"""
|
989
|
-
Returns a copy of the
|
990
|
-
:py:attr:`Density.data` set to zero. :py:attr:`Density.
|
991
|
-
:py:attr:`Density.
|
992
|
-
:py:attr:`Density.
|
993
|
-
|
989
|
+
Returns a copy of the class instance with all elements in
|
990
|
+
:py:attr:`Density.data` set to zero. :py:attr:`Density.metadata` will be
|
991
|
+
initialized accordingly. :py:attr:`Density.origin` and
|
992
|
+
:py:attr:`Density.sampling_rate` are copied.
|
993
|
+
|
994
|
+
Returns
|
995
|
+
-------
|
996
|
+
:py:class:`Density`
|
997
|
+
Empty class instance.
|
994
998
|
|
995
999
|
Examples
|
996
1000
|
--------
|
@@ -1010,7 +1014,12 @@ class Density:
|
|
1010
1014
|
|
1011
1015
|
def copy(self) -> "Density":
|
1012
1016
|
"""
|
1013
|
-
|
1017
|
+
Create a copy of the class instance.
|
1018
|
+
|
1019
|
+
Returns
|
1020
|
+
-------
|
1021
|
+
:py:class:`Density`
|
1022
|
+
A copy of the class instance.
|
1014
1023
|
|
1015
1024
|
Examples
|
1016
1025
|
--------
|
@@ -1029,8 +1038,7 @@ class Density:
|
|
1029
1038
|
|
1030
1039
|
def to_memmap(self) -> None:
|
1031
1040
|
"""
|
1032
|
-
Converts
|
1033
|
-
a :obj:`numpy.memmap` instance.
|
1041
|
+
Converts :py:attr:`Density.data` to a :obj:`numpy.memmap`.
|
1034
1042
|
|
1035
1043
|
Examples
|
1036
1044
|
--------
|
@@ -1055,7 +1063,7 @@ class Density:
|
|
1055
1063
|
--------
|
1056
1064
|
:py:meth:`Density.to_numpy`
|
1057
1065
|
"""
|
1058
|
-
if
|
1066
|
+
if isinstance(self.data, np.memmap):
|
1059
1067
|
return None
|
1060
1068
|
|
1061
1069
|
filename = array_to_memmap(arr=self.data)
|
@@ -1066,8 +1074,7 @@ class Density:
|
|
1066
1074
|
|
1067
1075
|
def to_numpy(self) -> None:
|
1068
1076
|
"""
|
1069
|
-
Converts
|
1070
|
-
an in-memory :obj:`numpy.ndarray`.
|
1077
|
+
Converts :py:attr:`Density.data` to an in-memory :obj:`numpy.ndarray`.
|
1071
1078
|
|
1072
1079
|
Examples
|
1073
1080
|
--------
|
@@ -1085,8 +1092,7 @@ class Density:
|
|
1085
1092
|
@property
|
1086
1093
|
def shape(self) -> Tuple[int]:
|
1087
1094
|
"""
|
1088
|
-
Returns the dimensions of
|
1089
|
-
attribute.
|
1095
|
+
Returns the dimensions of :py:attr:`Density.data`.
|
1090
1096
|
|
1091
1097
|
Returns
|
1092
1098
|
-------
|
@@ -1095,8 +1101,6 @@ class Density:
|
|
1095
1101
|
|
1096
1102
|
Examples
|
1097
1103
|
--------
|
1098
|
-
The following outlines the usage of :py:attr:`Density.shape`:
|
1099
|
-
|
1100
1104
|
>>> import numpy as np
|
1101
1105
|
>>> from tme import Density
|
1102
1106
|
>>> dens = Density(np.array([0, 1, 1, 1, 0]))
|
@@ -1108,13 +1112,12 @@ class Density:
|
|
1108
1112
|
@property
|
1109
1113
|
def data(self) -> NDArray:
|
1110
1114
|
"""
|
1111
|
-
Returns the value of
|
1112
|
-
attribute.
|
1115
|
+
Returns the value of :py:attr:`Density.data`.
|
1113
1116
|
|
1114
1117
|
Returns
|
1115
1118
|
-------
|
1116
1119
|
NDArray
|
1117
|
-
Value of the
|
1120
|
+
Value of the instance's :py:attr:`Density.data` attribute.
|
1118
1121
|
|
1119
1122
|
Examples
|
1120
1123
|
--------
|
@@ -1132,20 +1135,20 @@ class Density:
|
|
1132
1135
|
@data.setter
|
1133
1136
|
def data(self, data: NDArray) -> None:
|
1134
1137
|
"""
|
1135
|
-
Sets the value of the
|
1138
|
+
Sets the value of the instance's :py:attr:`Density.data` attribute.
|
1136
1139
|
"""
|
1137
1140
|
self._data = data
|
1138
1141
|
|
1139
1142
|
@property
|
1140
1143
|
def origin(self) -> NDArray:
|
1141
1144
|
"""
|
1142
|
-
Returns the value of the
|
1145
|
+
Returns the value of the instance's :py:attr:`Density.origin`
|
1143
1146
|
attribute.
|
1144
1147
|
|
1145
1148
|
Returns
|
1146
1149
|
-------
|
1147
1150
|
NDArray
|
1148
|
-
Value of the
|
1151
|
+
Value of the instance's :py:attr:`Density.origin` attribute.
|
1149
1152
|
|
1150
1153
|
Examples
|
1151
1154
|
--------
|
@@ -1171,8 +1174,7 @@ class Density:
|
|
1171
1174
|
@property
|
1172
1175
|
def sampling_rate(self) -> NDArray:
|
1173
1176
|
"""
|
1174
|
-
Returns the value of the
|
1175
|
-
attribute.
|
1177
|
+
Returns the value of the instance's :py:attr:`Density.sampling_rate` attribute.
|
1176
1178
|
|
1177
1179
|
Returns
|
1178
1180
|
-------
|
@@ -1193,7 +1195,7 @@ class Density:
|
|
1193
1195
|
@property
|
1194
1196
|
def metadata(self) -> Dict:
|
1195
1197
|
"""
|
1196
|
-
Returns the
|
1198
|
+
Returns the instance's :py:attr:`Density.metadata` attribute.
|
1197
1199
|
|
1198
1200
|
Returns
|
1199
1201
|
-------
|
@@ -1235,7 +1237,7 @@ class Density:
|
|
1235
1237
|
Pads the internal data array according to box.
|
1236
1238
|
|
1237
1239
|
Negative slices indices will result in a left-hand padding, while
|
1238
|
-
slice indices larger than the box_size property of the
|
1240
|
+
slice indices larger than the box_size property of the class
|
1239
1241
|
instance will result in a right-hand padding.
|
1240
1242
|
|
1241
1243
|
Parameters
|
@@ -1264,17 +1266,15 @@ class Density:
|
|
1264
1266
|
|
1265
1267
|
def adjust_box(self, box: Tuple[slice], pad_kwargs: Dict = {}) -> None:
|
1266
1268
|
"""
|
1267
|
-
Adjusts
|
1269
|
+
Adjusts :py:attr:`Density.data` and :py:attr:`Density.origin`
|
1268
1270
|
according to the provided box.
|
1269
1271
|
|
1270
1272
|
Parameters
|
1271
1273
|
----------
|
1272
1274
|
box : tuple of slices
|
1273
|
-
|
1274
|
-
should be sliced. See :py:meth:`Density.trim_box` on how to produce
|
1275
|
-
such an object.
|
1275
|
+
Description of how each axis of :py:attr:`Density.data` should be sliced.
|
1276
1276
|
pad_kwargs: dict, optional
|
1277
|
-
Parameter dictionary passed to numpy
|
1277
|
+
Parameter dictionary passed to :obj:`numpy.pad`.
|
1278
1278
|
|
1279
1279
|
See Also
|
1280
1280
|
--------
|
@@ -1454,8 +1454,8 @@ class Density:
|
|
1454
1454
|
Raises
|
1455
1455
|
------
|
1456
1456
|
ValueError
|
1457
|
-
If the length of
|
1458
|
-
|
1457
|
+
If the length of ``new_shape`` does not match the dimensionality of
|
1458
|
+
:py:attr:`Density.data`.
|
1459
1459
|
|
1460
1460
|
Examples
|
1461
1461
|
--------
|
@@ -1469,13 +1469,6 @@ class Density:
|
|
1469
1469
|
>>> dens.data
|
1470
1470
|
array([0, 1, 1, 1, 0])
|
1471
1471
|
|
1472
|
-
It's also possible to pass a user-defined ``padding_value``:
|
1473
|
-
|
1474
|
-
>>> dens = Density(np.array([1,1,1]))
|
1475
|
-
>>> dens.pad(new_shape = (5,), center = True, padding_value = -1)
|
1476
|
-
>>> dens.data
|
1477
|
-
array([-1, 1, 1, 1, -1])
|
1478
|
-
|
1479
1472
|
If ``center`` is set to False, the padding values will be appended:
|
1480
1473
|
|
1481
1474
|
>>> dens = Density(np.array([1,1,1]))
|
@@ -1483,6 +1476,12 @@ class Density:
|
|
1483
1476
|
>>> dens.data
|
1484
1477
|
array([1, 1, 1, 0, 0])
|
1485
1478
|
|
1479
|
+
It's also possible to pass a user-defined ``padding_value``:
|
1480
|
+
|
1481
|
+
>>> dens = Density(np.array([1,1,1]))
|
1482
|
+
>>> dens.pad(new_shape = (5,), center = True, padding_value = -1)
|
1483
|
+
>>> dens.data
|
1484
|
+
array([-1, 1, 1, 1, -1])
|
1486
1485
|
"""
|
1487
1486
|
if len(new_shape) != self.data.ndim:
|
1488
1487
|
raise ValueError(
|
@@ -1522,7 +1521,7 @@ class Density:
|
|
1522
1521
|
Returns
|
1523
1522
|
-------
|
1524
1523
|
:py:class:`Density`
|
1525
|
-
A centered copy of the
|
1524
|
+
A centered copy of the class instance.
|
1526
1525
|
NDArray
|
1527
1526
|
The offset between array center and center of mass.
|
1528
1527
|
|
@@ -1531,7 +1530,6 @@ class Density:
|
|
1531
1530
|
:py:meth:`Density.trim_box`
|
1532
1531
|
:py:meth:`Density.minimum_enclosing_box`
|
1533
1532
|
|
1534
|
-
|
1535
1533
|
Examples
|
1536
1534
|
--------
|
1537
1535
|
:py:meth:`Density.centered` returns a tuple containing a centered version
|
@@ -1591,114 +1589,6 @@ class Density:
|
|
1591
1589
|
shift = np.subtract(center, self.center_of_mass(ret.data, cutoff))
|
1592
1590
|
return ret, shift
|
1593
1591
|
|
1594
|
-
@classmethod
|
1595
|
-
def rotate_array(
|
1596
|
-
cls,
|
1597
|
-
arr: NDArray,
|
1598
|
-
rotation_matrix: NDArray,
|
1599
|
-
arr_mask: NDArray = None,
|
1600
|
-
translation: NDArray = None,
|
1601
|
-
use_geometric_center: bool = False,
|
1602
|
-
out: NDArray = None,
|
1603
|
-
out_mask: NDArray = None,
|
1604
|
-
order: int = 3,
|
1605
|
-
) -> None:
|
1606
|
-
"""
|
1607
|
-
Rotates coordinates of arr according to rotation_matrix.
|
1608
|
-
|
1609
|
-
If no output array is provided, this method will compute an array with
|
1610
|
-
sufficient space to hold all elements. If both `arr` and `arr_mask`
|
1611
|
-
are provided, `arr_mask` will be centered according to arr.
|
1612
|
-
|
1613
|
-
Parameters
|
1614
|
-
----------
|
1615
|
-
arr : NDArray
|
1616
|
-
The input array to be rotated.
|
1617
|
-
arr_mask : NDArray, optional
|
1618
|
-
The mask of `arr` that will be equivalently rotated.
|
1619
|
-
rotation_matrix : NDArray
|
1620
|
-
The rotation matrix to apply [d x d].
|
1621
|
-
translation : NDArray
|
1622
|
-
The translation to apply [d].
|
1623
|
-
use_geometric_center : bool, optional
|
1624
|
-
Whether the rotation should be centered around the geometric
|
1625
|
-
or mass center. Default is mass center.
|
1626
|
-
out : NDArray, optional
|
1627
|
-
The output array to write the rotation of `arr` to.
|
1628
|
-
out_mask : NDArray, optional
|
1629
|
-
The output array to write the rotation of `arr_mask` to.
|
1630
|
-
order : int, optional
|
1631
|
-
Spline interpolation order. Has to be in the range 0-5.
|
1632
|
-
"""
|
1633
|
-
|
1634
|
-
return NumpyFFTWBackend().rotate_array(
|
1635
|
-
arr=arr,
|
1636
|
-
rotation_matrix=rotation_matrix,
|
1637
|
-
arr_mask=arr_mask,
|
1638
|
-
translation=translation,
|
1639
|
-
use_geometric_center=use_geometric_center,
|
1640
|
-
out=out,
|
1641
|
-
out_mask=out_mask,
|
1642
|
-
order=order,
|
1643
|
-
)
|
1644
|
-
|
1645
|
-
@staticmethod
|
1646
|
-
def rotate_array_coordinates(
|
1647
|
-
arr: NDArray,
|
1648
|
-
coordinates: NDArray,
|
1649
|
-
rotation_matrix: NDArray,
|
1650
|
-
translation: NDArray = None,
|
1651
|
-
out: NDArray = None,
|
1652
|
-
use_geometric_center: bool = True,
|
1653
|
-
arr_mask: NDArray = None,
|
1654
|
-
mask_coordinates: NDArray = None,
|
1655
|
-
out_mask: NDArray = None,
|
1656
|
-
) -> None:
|
1657
|
-
"""
|
1658
|
-
Rotates coordinates of arr according to rotation_matrix.
|
1659
|
-
|
1660
|
-
If no output array is provided, this method will compute an array with
|
1661
|
-
sufficient space to hold all elements. If both `arr` and `arr_mask`
|
1662
|
-
are provided, `arr_mask` will be centered according to arr.
|
1663
|
-
|
1664
|
-
No centering will be performed if the rotation matrix is the identity matrix.
|
1665
|
-
|
1666
|
-
Parameters
|
1667
|
-
----------
|
1668
|
-
arr : NDArray
|
1669
|
-
The input array to be rotated.
|
1670
|
-
coordinates : NDArray
|
1671
|
-
The pointcloud [d x N] containing elements of `arr` that should be rotated.
|
1672
|
-
See :py:meth:`Density.to_pointcloud` on how to obtain the coordinates.
|
1673
|
-
rotation_matrix : NDArray
|
1674
|
-
The rotation matrix to apply [d x d].
|
1675
|
-
rotation_matrix : NDArray
|
1676
|
-
The translation to apply [d].
|
1677
|
-
out : NDArray, optional
|
1678
|
-
The output array to write the rotation of `arr` to.
|
1679
|
-
use_geometric_center : bool, optional
|
1680
|
-
Whether the rotation should be centered around the geometric
|
1681
|
-
or mass center.
|
1682
|
-
arr_mask : NDArray, optional
|
1683
|
-
The mask of `arr` that will be equivalently rotated.
|
1684
|
-
mask_coordinates : NDArray, optional
|
1685
|
-
Equivalent to `coordinates`, but containing elements of `arr_mask`
|
1686
|
-
that should be rotated.
|
1687
|
-
out_mask : NDArray, optional
|
1688
|
-
The output array to write the rotation of `arr_mask` to.
|
1689
|
-
"""
|
1690
|
-
return NumpyFFTWBackend().rotate_array_coordinates(
|
1691
|
-
arr=arr,
|
1692
|
-
coordinates=coordinates,
|
1693
|
-
rotation_matrix=rotation_matrix,
|
1694
|
-
translation=translation,
|
1695
|
-
out=out,
|
1696
|
-
use_geometric_center=use_geometric_center,
|
1697
|
-
arr_mask=arr_mask,
|
1698
|
-
mask_coordinates=mask_coordinates,
|
1699
|
-
out_mask=out_mask,
|
1700
|
-
)
|
1701
|
-
|
1702
1592
|
def rigid_transform(
|
1703
1593
|
self,
|
1704
1594
|
rotation_matrix: NDArray,
|
@@ -1707,7 +1597,7 @@ class Density:
|
|
1707
1597
|
use_geometric_center: bool = False,
|
1708
1598
|
) -> "Density":
|
1709
1599
|
"""
|
1710
|
-
Performs a rigid transform of the
|
1600
|
+
Performs a rigid transform of the class instance.
|
1711
1601
|
|
1712
1602
|
Parameters
|
1713
1603
|
----------
|
@@ -1756,89 +1646,134 @@ class Density:
|
|
1756
1646
|
--------
|
1757
1647
|
:py:meth:`Density.centered`, :py:meth:`Density.minimum_enclosing_box`
|
1758
1648
|
"""
|
1759
|
-
|
1760
|
-
|
1761
|
-
self.rotate_array(
|
1649
|
+
ret = self.empty
|
1650
|
+
NumpyFFTWBackend().rigid_transform(
|
1762
1651
|
arr=self.data,
|
1763
1652
|
rotation_matrix=rotation_matrix,
|
1764
1653
|
translation=translation,
|
1765
|
-
order=order,
|
1766
1654
|
use_geometric_center=use_geometric_center,
|
1767
|
-
out=
|
1655
|
+
out=ret.data,
|
1656
|
+
order=order,
|
1768
1657
|
)
|
1769
|
-
eps = np.finfo(transformed_map.data.dtype).eps
|
1770
|
-
transformed_map.data[transformed_map.data < eps] = 0
|
1771
|
-
return transformed_map
|
1772
1658
|
|
1773
|
-
|
1659
|
+
eps = np.finfo(ret.data.dtype).eps
|
1660
|
+
ret.data[np.abs(ret.data) < eps] = 0
|
1661
|
+
return ret
|
1662
|
+
|
1663
|
+
def resample(
|
1664
|
+
self, new_sampling_rate: Tuple[float], method: str = "spline", order: int = 1
|
1665
|
+
) -> "Density":
|
1774
1666
|
"""
|
1775
|
-
|
1667
|
+
Resamples :py:attr:`Density.data` to ``new_sampling_rate``.
|
1776
1668
|
|
1777
1669
|
Parameters
|
1778
1670
|
----------
|
1779
|
-
|
1780
|
-
|
1671
|
+
new_sampling_rate : tuple of floats or float
|
1672
|
+
Sampling rate to resample to for a single or all axes.
|
1673
|
+
method: str, optional
|
1674
|
+
Resampling method to use, defaults to `spline`. Availabe options are:
|
1675
|
+
|
1676
|
+
+---------+----------------------------------------------------------+
|
1677
|
+
| spline | Smooth spline interpolation via :obj:`scipy.ndimage.zoom`|
|
1678
|
+
+---------+----------------------------------------------------------+
|
1679
|
+
| fourier | Frequency preserving Fourier cropping |
|
1680
|
+
+---------+----------------------------------------------------------+
|
1681
|
+
|
1682
|
+
order : int, optional
|
1683
|
+
Order of spline used for interpolation, by default 1. Ignored when
|
1684
|
+
``method`` is `fourier`.
|
1781
1685
|
|
1782
1686
|
Raises
|
1783
1687
|
------
|
1784
1688
|
ValueError
|
1785
|
-
If
|
1689
|
+
If ``method`` is not supported.
|
1786
1690
|
|
1787
1691
|
Returns
|
1788
1692
|
-------
|
1789
|
-
Density
|
1790
|
-
A
|
1791
|
-
"""
|
1792
|
-
if not np.allclose(self.sampling_rate, other_map.sampling_rate):
|
1793
|
-
raise ValueError("sampling_rate of both maps have to match.")
|
1693
|
+
:py:class:`Density`
|
1694
|
+
A resampled copy of the class instance.
|
1794
1695
|
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1696
|
+
Examples
|
1697
|
+
--------
|
1698
|
+
The following makes use of :py:meth:`tme.matching_utils.create_mask`
|
1699
|
+
to define a :py:class:`Density` instance containing a 2D circle with
|
1700
|
+
a sampling rate of 2
|
1799
1701
|
|
1800
|
-
|
1801
|
-
|
1702
|
+
>>> from tme import Density
|
1703
|
+
>>> from tme.matching_utils import create_mask
|
1704
|
+
>>> mask = create_mask(
|
1705
|
+
>>> mask_type="ellipse",
|
1706
|
+
>>> shape=(11,11),
|
1707
|
+
>>> center=(5,5),
|
1708
|
+
>>> radius=3
|
1709
|
+
>>> )
|
1710
|
+
>>> dens = Density(mask, sampling_rate=2)
|
1711
|
+
>>> dens
|
1712
|
+
Origin: (0.0, 0.0), sampling_rate: (2, 2), Shape: (11, 11)
|
1802
1713
|
|
1803
|
-
|
1714
|
+
Using :py:meth:`Density.resample` we can modulate the sampling rate
|
1715
|
+
using spline interpolation of desired order
|
1804
1716
|
|
1805
|
-
|
1806
|
-
|
1807
|
-
return ret
|
1717
|
+
>>> dens.resample(new_sampling_rate= 4, method="spline", order=3)
|
1718
|
+
Origin: (0.0, 0.0), sampling_rate: (4, 4), Shape: (6, 6)
|
1808
1719
|
|
1809
|
-
|
1810
|
-
|
1811
|
-
Resamples the current class instance to ``new_sampling_rate`` using
|
1812
|
-
spline interpolation of order ``order``.
|
1720
|
+
Or Fourier cropping which results in a less smooth output, but more faithfully
|
1721
|
+
captures the contained frequency information
|
1813
1722
|
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1723
|
+
>>> dens.resample(new_sampling_rate=4, method="fourier")
|
1724
|
+
Origin: (0.0, 0.0), sampling_rate: (4, 4), Shape: (6, 6)
|
1725
|
+
|
1726
|
+
``new_sampling_rate`` can also be specified per axis
|
1727
|
+
|
1728
|
+
>>> dens.resample(new_sampling_rate=(4,1), method="spline", order=3)
|
1729
|
+
Origin: (0.0, 0.0), sampling_rate: (4, 1), Shape: (6, 22)
|
1820
1730
|
|
1821
|
-
Returns
|
1822
|
-
-------
|
1823
|
-
Density
|
1824
|
-
A resampled instance of `Density` class.
|
1825
1731
|
"""
|
1826
|
-
|
1732
|
+
_supported_methods = ("spline", "fourier")
|
1733
|
+
if method not in _supported_methods:
|
1734
|
+
raise ValueError(
|
1735
|
+
f"Expected method to be one of {_supported_methods}, got '{method}'."
|
1736
|
+
)
|
1737
|
+
new_sampling_rate = np.array(new_sampling_rate)
|
1827
1738
|
new_sampling_rate = np.repeat(
|
1828
|
-
new_sampling_rate,
|
1739
|
+
new_sampling_rate, self.data.ndim // new_sampling_rate.size
|
1829
1740
|
)
|
1830
|
-
scale_factor = np.divide(map_copy.sampling_rate, new_sampling_rate)
|
1831
1741
|
|
1832
|
-
|
1833
|
-
|
1742
|
+
ret = self.copy()
|
1743
|
+
scale_factor = np.divide(ret.sampling_rate, new_sampling_rate)
|
1744
|
+
if method == "spline":
|
1745
|
+
ret.data = zoom(ret.data, scale_factor, order=order)
|
1746
|
+
elif method == "fourier":
|
1747
|
+
ret_shape = np.round(np.multiply(scale_factor, ret.shape)).astype(int)
|
1748
|
+
|
1749
|
+
axis = range(len(ret_shape))
|
1750
|
+
mask = np.zeros(self.shape, dtype=bool)
|
1751
|
+
mask[tuple(slice(0, x) for x in ret_shape)] = 1
|
1752
|
+
mask = np.roll(
|
1753
|
+
mask, shift=-np.floor(np.divide(ret_shape, 2)).astype(int), axis=axis
|
1754
|
+
)
|
1755
|
+
mask_ret = np.zeros(ret_shape, dtype=bool)
|
1756
|
+
mask_ret[tuple(slice(0, x) for x in self.shape)] = 1
|
1757
|
+
mask_ret = np.roll(
|
1758
|
+
mask_ret,
|
1759
|
+
shift=-np.floor(np.divide(self.shape, 2)).astype(int),
|
1760
|
+
axis=axis,
|
1761
|
+
)
|
1834
1762
|
|
1835
|
-
|
1763
|
+
arr_ft = np.fft.fftn(self.data)
|
1764
|
+
arr_ft *= np.prod(ret_shape) / np.prod(self.shape)
|
1765
|
+
ret_ft = np.zeros(ret_shape, dtype=arr_ft.dtype)
|
1766
|
+
ret_ft[mask_ret] = arr_ft[mask]
|
1767
|
+
ret.data = np.real(np.fft.ifftn(ret_ft))
|
1768
|
+
|
1769
|
+
ret.sampling_rate = new_sampling_rate
|
1770
|
+
return ret
|
1836
1771
|
|
1837
1772
|
def density_boundary(
|
1838
1773
|
self, weight: float, fraction_surface: float = 0.1, volume_factor: float = 1.21
|
1839
1774
|
) -> Tuple[float]:
|
1840
1775
|
"""
|
1841
|
-
Computes the density boundary of the
|
1776
|
+
Computes the density boundary of the class instance. The density
|
1842
1777
|
boundary in this setting is defined as minimal and maximal density value
|
1843
1778
|
enclosing a certain ``weight``.
|
1844
1779
|
|
@@ -1894,31 +1829,31 @@ class Density:
|
|
1894
1829
|
self, density_boundaries: Tuple[float], method: str = "ConvexHull"
|
1895
1830
|
) -> NDArray:
|
1896
1831
|
"""
|
1897
|
-
Calculates the surface coordinates of the
|
1832
|
+
Calculates the surface coordinates of the class instance using
|
1898
1833
|
different boundary and surface detection methods. This method is relevant
|
1899
|
-
for determining coordinates used in template matching,
|
1900
|
-
see :py:class:`tme.
|
1834
|
+
for determining coordinates used in non-exhaustive template matching,
|
1835
|
+
see :py:class:`tme.matching_optimization.optimize_match`.
|
1901
1836
|
|
1902
1837
|
Parameters
|
1903
1838
|
----------
|
1904
1839
|
density_boundaries : tuple
|
1905
|
-
|
1906
|
-
|
1840
|
+
Lower and upper bound of density values to be considered
|
1841
|
+
(can be obtained from :py:meth:`Density.density_boundary`).
|
1907
1842
|
method : str, optional
|
1908
|
-
|
1843
|
+
Method to use for surface coordinate computation
|
1909
1844
|
|
1910
1845
|
+--------------+-----------------------------------------------------+
|
1911
|
-
|
|
1846
|
+
| ConvexHull | Use the lower bound density convex hull vertices. |
|
1912
1847
|
+--------------+-----------------------------------------------------+
|
1913
|
-
|
|
1848
|
+
| Weight | Use all coordinates within ``density_boundaries``. |
|
1914
1849
|
+--------------+-----------------------------------------------------+
|
1915
|
-
|
|
1850
|
+
| Sobel | Set densities below the lower bound density to zero |
|
1916
1851
|
| | apply a sobel filter and return density coordinates |
|
1917
1852
|
| | larger than 0.5 times the maximum filter value. |
|
1918
1853
|
+--------------+-----------------------------------------------------+
|
1919
|
-
|
|
1854
|
+
| Laplace | Like 'Sobel', but with a Laplace filter. |
|
1920
1855
|
+--------------+-----------------------------------------------------+
|
1921
|
-
|
|
1856
|
+
| Minimum | Like 'Sobel' and 'Laplace' but with a spherical |
|
1922
1857
|
| | minimum filter on the lower density bound. |
|
1923
1858
|
+--------------+-----------------------------------------------------+
|
1924
1859
|
|
@@ -1930,15 +1865,11 @@ class Density:
|
|
1930
1865
|
Returns
|
1931
1866
|
-------
|
1932
1867
|
NDArray
|
1933
|
-
An array of surface coordinates with shape (
|
1868
|
+
An array of surface coordinates with shape (points, dimensions).
|
1934
1869
|
|
1935
1870
|
References
|
1936
1871
|
----------
|
1937
|
-
.. [1] Cragnolini T,
|
1938
|
-
Vasishtan D, Topf M (2021a) TEMPy2: A Python library with
|
1939
|
-
improved 3D electron microscopy density-fitting and validation
|
1940
|
-
workflows. Acta Crystallogr Sect D Struct Biol 77:41–47.
|
1941
|
-
https://doi.org/10.1107/S2059798320014928
|
1872
|
+
.. [1] Cragnolini T, et al. (2021) Acta Crys Sect D Struct Biol
|
1942
1873
|
|
1943
1874
|
See Also
|
1944
1875
|
--------
|
@@ -1948,12 +1879,12 @@ class Density:
|
|
1948
1879
|
:py:class:`tme.matching_optimization.Envelope`
|
1949
1880
|
:py:class:`tme.matching_optimization.Chamfer`
|
1950
1881
|
"""
|
1951
|
-
|
1882
|
+
_available_methods = ["ConvexHull", "Weight", "Sobel", "Laplace", "Minimum"]
|
1952
1883
|
|
1953
|
-
if method not in
|
1884
|
+
if method not in _available_methods:
|
1954
1885
|
raise ValueError(
|
1955
1886
|
"Argument method has to be one of the following: %s"
|
1956
|
-
% ", ".join(
|
1887
|
+
% ", ".join(_available_methods)
|
1957
1888
|
)
|
1958
1889
|
|
1959
1890
|
lower_bound, upper_bound = density_boundaries
|
@@ -2001,7 +1932,7 @@ class Density:
|
|
2001
1932
|
def normal_vectors(self, coordinates: NDArray) -> NDArray:
|
2002
1933
|
"""
|
2003
1934
|
Calculates the normal vectors for the given coordinates on the densities
|
2004
|
-
of the
|
1935
|
+
of the class instance. If the normal vector to a given coordinate
|
2005
1936
|
can not be computed, the zero vector is returned instead. The output of this
|
2006
1937
|
function can e.g. be used in
|
2007
1938
|
:py:class:`tme.matching_optimization.NormalVectorScore`.
|
@@ -2051,12 +1982,7 @@ class Density:
|
|
2051
1982
|
in_box = np.logical_and(
|
2052
1983
|
coordinates < np.array(self.shape), coordinates >= 0
|
2053
1984
|
).min(axis=1)
|
2054
|
-
|
2055
|
-
out_of_box = np.invert(in_box)
|
2056
|
-
if out_of_box.sum() > 0:
|
2057
|
-
print(coordinates[out_of_box, :])
|
2058
|
-
raise ValueError("Coordinates outside of self.data detected.")
|
2059
|
-
|
1985
|
+
coordinates = coordinates[in_box, :]
|
2060
1986
|
for index in range(coordinates.shape[0]):
|
2061
1987
|
point = coordinates[index, :]
|
2062
1988
|
start = np.maximum(point - 1, 0)
|
@@ -2076,22 +2002,16 @@ class Density:
|
|
2076
2002
|
|
2077
2003
|
def core_mask(self) -> NDArray:
|
2078
2004
|
"""
|
2079
|
-
Calculates
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
in a mask with same shape as the internal data array. Therefore,
|
2085
|
-
data elements in the output array with a value of n remained non-zero for
|
2086
|
-
n rounds of binary erosion. The higher the value, the more likely a data element
|
2087
|
-
is part of the core of the density map.
|
2005
|
+
Calculates a weighted core mask by performing iterative binary erosion on
|
2006
|
+
:py:attr:`Density.data`. In each iteration, all mask elements corresponding
|
2007
|
+
to a non-zero data elemnt are incremented by one. Therefore, a mask element
|
2008
|
+
with value N corresponds to a data value that remained non-zero for N iterations.
|
2009
|
+
Mask elements with high values are likely part of the core density [1]_.
|
2088
2010
|
|
2089
2011
|
Returns
|
2090
2012
|
-------
|
2091
2013
|
NDArray
|
2092
|
-
|
2093
|
-
indicate how many rounds of binary erosion were necessary to nullify
|
2094
|
-
a given data element.
|
2014
|
+
Core-weighted mask with shape of :py:attr:`Density.data`.
|
2095
2015
|
|
2096
2016
|
References
|
2097
2017
|
----------
|
@@ -2126,19 +2046,7 @@ class Density:
|
|
2126
2046
|
NDArray
|
2127
2047
|
Center of mass with shape (arr.ndim).
|
2128
2048
|
"""
|
2129
|
-
|
2130
|
-
arr = np.where(arr > cutoff, arr, 0)
|
2131
|
-
denominator = np.sum(arr)
|
2132
|
-
grids = np.ogrid[tuple(slice(0, i) for i in arr.shape)]
|
2133
|
-
|
2134
|
-
center_of_mass = np.array(
|
2135
|
-
[
|
2136
|
-
np.sum(np.multiply(arr, grids[dim].astype(float))) / denominator
|
2137
|
-
for dim in range(arr.ndim)
|
2138
|
-
]
|
2139
|
-
)
|
2140
|
-
|
2141
|
-
return center_of_mass
|
2049
|
+
return NumpyFFTWBackend().center_of_mass(arr, cutoff)
|
2142
2050
|
|
2143
2051
|
@classmethod
|
2144
2052
|
def match_densities(
|
@@ -2187,21 +2095,20 @@ class Density:
|
|
2187
2095
|
-----
|
2188
2096
|
No densities below cutoff_template are present in the returned Density object.
|
2189
2097
|
"""
|
2190
|
-
from .
|
2098
|
+
from .matching_utils import normalize_template
|
2191
2099
|
from .matching_optimization import optimize_match, create_score_object
|
2192
2100
|
|
2193
2101
|
template_mask = template.empty
|
2194
|
-
template_mask.data
|
2102
|
+
template_mask.data.fill(1)
|
2195
2103
|
|
2196
|
-
|
2104
|
+
normalize_template(
|
2197
2105
|
template=template.data,
|
2198
2106
|
mask=template_mask.data,
|
2199
|
-
|
2107
|
+
n_observations=template_mask.data.sum(),
|
2200
2108
|
)
|
2201
2109
|
|
2202
2110
|
target_sampling_rate = np.array(target.sampling_rate)
|
2203
2111
|
template_sampling_rate = np.array(template.sampling_rate)
|
2204
|
-
|
2205
2112
|
target_sampling_rate = np.repeat(
|
2206
2113
|
target_sampling_rate, target.data.ndim // target_sampling_rate.size
|
2207
2114
|
)
|
@@ -2340,49 +2247,6 @@ class Density:
|
|
2340
2247
|
|
2341
2248
|
return out, final_translation, rotation_matrix
|
2342
2249
|
|
2343
|
-
@staticmethod
|
2344
|
-
def align_coordinate_systems(target: "Density", template: "Density") -> "Density":
|
2345
|
-
"""
|
2346
|
-
Aligns the coordinate system of `target` and `template`.
|
2347
|
-
|
2348
|
-
Parameters
|
2349
|
-
----------
|
2350
|
-
target : Density
|
2351
|
-
The target density whose coordinate system should remain unchanged.
|
2352
|
-
template : Density
|
2353
|
-
The template density that will be aligned to match the target's
|
2354
|
-
coordinate system.
|
2355
|
-
|
2356
|
-
Raises
|
2357
|
-
------
|
2358
|
-
ValueError
|
2359
|
-
If the `sampling_rate` of `target` and `template` do not match.
|
2360
|
-
|
2361
|
-
Returns
|
2362
|
-
-------
|
2363
|
-
Density
|
2364
|
-
A copy of `template` aligned to the coordinate system of `target`.
|
2365
|
-
The `box_size` and `origin` will match that of `target`.
|
2366
|
-
|
2367
|
-
See Also
|
2368
|
-
--------
|
2369
|
-
:py:meth:`Density.match_densities` : To match aligned template to target.
|
2370
|
-
"""
|
2371
|
-
if not np.allclose(target.sampling_rate, template.sampling_rate):
|
2372
|
-
raise ValueError("sampling_rate of both maps have to match.")
|
2373
|
-
|
2374
|
-
template = template.copy()
|
2375
|
-
template.pad(target.shape, center=True)
|
2376
|
-
|
2377
|
-
origin_difference = np.divide(
|
2378
|
-
np.subtract(template.origin, target.origin), target.sampling_rate
|
2379
|
-
)
|
2380
|
-
template = template.rigid_transform(
|
2381
|
-
rotation_matrix=np.eye(template.data.ndim), translation=origin_difference
|
2382
|
-
)
|
2383
|
-
template.origin = target.origin.copy()
|
2384
|
-
return template
|
2385
|
-
|
2386
2250
|
@staticmethod
|
2387
2251
|
def fourier_shell_correlation(density1: "Density", density2: "Density") -> NDArray:
|
2388
2252
|
"""
|
@@ -2439,3 +2303,9 @@ class Density:
|
|
2439
2303
|
qidx = np.where(qbins < qx.max())
|
2440
2304
|
|
2441
2305
|
return np.vstack((qbins[qidx], FSC[qidx])).T
|
2306
|
+
|
2307
|
+
|
2308
|
+
def is_gzipped(filename: str) -> bool:
|
2309
|
+
"""Check if a file is a gzip file by reading its magic number."""
|
2310
|
+
with open(filename, "rb") as f:
|
2311
|
+
return f.read(2) == b"\x1f\x8b"
|