pytme 0.2.1__cp311-cp311-macosx_14_0_arm64.whl → 0.2.3__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.3.data}/scripts/match_template.py +219 -216
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/postprocess.py +86 -54
- pytme-0.2.3.data/scripts/preprocess.py +132 -0
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/preprocessor_gui.py +181 -94
- pytme-0.2.3.dist-info/METADATA +92 -0
- pytme-0.2.3.dist-info/RECORD +75 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/WHEEL +1 -1
- pytme-0.2.1.data/scripts/preprocess.py → scripts/eval.py +1 -1
- scripts/extract_candidates.py +20 -13
- scripts/match_template.py +219 -216
- scripts/match_template_filters.py +154 -95
- scripts/postprocess.py +86 -54
- scripts/preprocess.py +95 -56
- scripts/preprocessor_gui.py +181 -94
- scripts/refine_matches.py +265 -61
- tme/__init__.py +0 -1
- tme/__version__.py +1 -1
- tme/analyzer.py +458 -813
- tme/backends/__init__.py +40 -11
- tme/backends/_jax_utils.py +187 -0
- tme/backends/cupy_backend.py +109 -226
- tme/backends/jax_backend.py +230 -152
- tme/backends/matching_backend.py +445 -384
- tme/backends/mlx_backend.py +32 -59
- tme/backends/npfftw_backend.py +240 -507
- tme/backends/pytorch_backend.py +30 -151
- tme/density.py +248 -371
- tme/extensions.cpython-311-darwin.so +0 -0
- tme/matching_data.py +328 -284
- tme/matching_exhaustive.py +195 -1499
- tme/matching_optimization.py +143 -106
- tme/matching_scores.py +887 -0
- tme/matching_utils.py +287 -388
- tme/memory.py +377 -0
- tme/orientations.py +78 -21
- tme/parser.py +3 -4
- tme/preprocessing/_utils.py +61 -32
- tme/preprocessing/composable_filter.py +7 -4
- tme/preprocessing/compose.py +7 -3
- tme/preprocessing/frequency_filters.py +49 -39
- tme/preprocessing/tilt_series.py +44 -72
- 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.3.data}/scripts/estimate_ram_usage.py +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/LICENSE +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/entry_points.txt +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.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,11 +113,11 @@ 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
|
-
tuple(
|
118
|
-
tuple(
|
119
|
+
tuple(round(float(x), 3) for x in self.origin),
|
120
|
+
tuple(round(float(x), 3) for x in self.sampling_rate),
|
119
121
|
self.shape,
|
120
122
|
)
|
121
123
|
|
@@ -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
|
--------
|
@@ -305,6 +306,10 @@ class Density:
|
|
305
306
|
"std": float(mrc.header.rms),
|
306
307
|
}
|
307
308
|
|
309
|
+
non_standard_crs = not np.all(crs_index == (0, 1, 2))
|
310
|
+
if non_standard_crs:
|
311
|
+
warnings.warn("Non standard MAPC, MAPR, MAPS, adapting data and origin.")
|
312
|
+
|
308
313
|
if is_gzipped(filename):
|
309
314
|
if use_memmap:
|
310
315
|
warnings.warn(
|
@@ -314,7 +319,11 @@ class Density:
|
|
314
319
|
use_memmap = False
|
315
320
|
|
316
321
|
if subset is not None:
|
317
|
-
|
322
|
+
subset = tuple(
|
323
|
+
subset[i] if i < len(subset) else slice(0, data_shape[i])
|
324
|
+
for i in crs_index
|
325
|
+
)
|
326
|
+
subset_shape = tuple(x.stop - x.start for x in subset)
|
318
327
|
if np.allclose(subset_shape, data_shape):
|
319
328
|
return cls._load_mrc(
|
320
329
|
filename=filename, subset=None, use_memmap=use_memmap
|
@@ -327,19 +336,16 @@ class Density:
|
|
327
336
|
dtype=data_type,
|
328
337
|
header_size=1024 + extended_header,
|
329
338
|
)
|
330
|
-
|
331
|
-
|
332
|
-
if not use_memmap:
|
339
|
+
elif subset is None and not use_memmap:
|
333
340
|
with mrcfile.open(filename, header_only=False) as mrc:
|
334
341
|
data = mrc.data.astype(np.float32, copy=False)
|
335
342
|
else:
|
336
343
|
with mrcfile.mrcmemmap.MrcMemmap(filename, header_only=False) as mrc:
|
337
344
|
data = mrc.data
|
338
345
|
|
339
|
-
if
|
340
|
-
data.setflags(write=True)
|
346
|
+
if non_standard_crs:
|
341
347
|
data = np.transpose(data, crs_index)
|
342
|
-
|
348
|
+
origin = np.take(origin, crs_index)
|
343
349
|
|
344
350
|
return data, origin, sampling_rate, metadata
|
345
351
|
|
@@ -642,7 +648,7 @@ class Density:
|
|
642
648
|
sampling_rate: NDArray = np.ones(1),
|
643
649
|
origin: Tuple[float] = None,
|
644
650
|
weight_type: str = "atomic_weight",
|
645
|
-
|
651
|
+
weight_type_args: Dict = {},
|
646
652
|
chain: str = None,
|
647
653
|
filter_by_elements: Set = None,
|
648
654
|
filter_by_residues: Set = None,
|
@@ -672,6 +678,8 @@ class Density:
|
|
672
678
|
weight_type : str, optional
|
673
679
|
Which weight should be given to individual atoms. For valid values
|
674
680
|
see :py:meth:`tme.structure.Structure.to_volume`.
|
681
|
+
weight_type_args : dict, optional
|
682
|
+
Additional arguments for atom weight computation.
|
675
683
|
chain : str, optional
|
676
684
|
The chain that should be extracted from the structure. If multiple chains
|
677
685
|
should be selected, they needto be a comma separated string,
|
@@ -680,8 +688,6 @@ class Density:
|
|
680
688
|
Set of atomic elements to keep. Default is all atoms.
|
681
689
|
filter_by_residues : set, optional
|
682
690
|
Set of residues to keep. Default is all residues.
|
683
|
-
scattering_args : dict, optional
|
684
|
-
Additional arguments for scattering factor computation.
|
685
691
|
|
686
692
|
Returns
|
687
693
|
-------
|
@@ -738,14 +744,14 @@ class Density:
|
|
738
744
|
>>> )
|
739
745
|
|
740
746
|
: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:
|
747
|
+
atoms into densities. In addition to 'atomic_weight', 'atomic_number',
|
748
|
+
and 'van_der_waals_radius', its possible to use experimentally determined
|
749
|
+
scattering factors from various sources:
|
744
750
|
|
745
751
|
>>> density = Density.from_structure(
|
746
752
|
>>> filename_or_structure = path_to_structure,
|
747
753
|
>>> weight_type = "scattering_factors",
|
748
|
-
>>>
|
754
|
+
>>> weight_type_args={"source": "dt1969"}
|
749
755
|
>>> )
|
750
756
|
|
751
757
|
or a lowpass filtered representation introduced in [1]_:
|
@@ -753,7 +759,7 @@ class Density:
|
|
753
759
|
>>> density = Density.from_structure(
|
754
760
|
>>> filename_or_structure = path_to_structure,
|
755
761
|
>>> weight_type = "lowpass_scattering_factors",
|
756
|
-
>>>
|
762
|
+
>>> weight_type_args={"source": "dt1969"}
|
757
763
|
>>> )
|
758
764
|
|
759
765
|
See Also
|
@@ -775,27 +781,27 @@ class Density:
|
|
775
781
|
origin=origin,
|
776
782
|
chain=chain,
|
777
783
|
weight_type=weight_type,
|
778
|
-
|
784
|
+
weight_type_args=weight_type_args,
|
779
785
|
)
|
780
786
|
|
781
787
|
return cls(
|
782
788
|
data=volume,
|
783
789
|
origin=origin,
|
784
790
|
sampling_rate=sampling_rate,
|
785
|
-
metadata=structure.
|
791
|
+
metadata=structure.metadata.copy(),
|
786
792
|
)
|
787
793
|
|
788
794
|
def to_file(self, filename: str, gzip: bool = False) -> None:
|
789
795
|
"""
|
790
|
-
Writes
|
796
|
+
Writes class instance to disk.
|
791
797
|
|
792
798
|
Parameters
|
793
799
|
----------
|
794
800
|
filename : str
|
795
801
|
Path to write to.
|
796
802
|
gzip : bool, optional
|
797
|
-
|
798
|
-
|
803
|
+
Gzip compress the output and add corresponding suffix to filename
|
804
|
+
if not present. False by default.
|
799
805
|
|
800
806
|
References
|
801
807
|
----------
|
@@ -811,7 +817,7 @@ class Density:
|
|
811
817
|
>>> import numpy as np
|
812
818
|
>>> from tme import Density
|
813
819
|
>>> data = np.random.rand(50,50,50)
|
814
|
-
>>> dens = Density(data
|
820
|
+
>>> dens = Density(data=data, origin=(0, 0, 0), sampling_rate=(1, 1, 1))
|
815
821
|
>>> dens.to_file("example.mrc")
|
816
822
|
|
817
823
|
The output file can also be directly ``gzip`` compressed. The corresponding
|
@@ -826,14 +832,14 @@ class Density:
|
|
826
832
|
In addition, a variety of image file formats are supported [3]_:
|
827
833
|
|
828
834
|
>>> data = np.random.rand(50,50)
|
829
|
-
>>> dens = Density(data
|
835
|
+
>>> dens = Density(data=data, origin=(0, 0), sampling_rate=(1, 1))
|
830
836
|
>>> dens.to_file("example.tiff")
|
831
837
|
|
832
838
|
Notes
|
833
839
|
-----
|
834
|
-
If ``filename``
|
835
|
-
|
836
|
-
|
840
|
+
If ``filename`` endswith ".em" or ".h5" a EM file or HDF5 file will be created.
|
841
|
+
The default output format is CCP4/MRC and on failure, :obj:`skimage.io.imsave`
|
842
|
+
is used.
|
837
843
|
|
838
844
|
See Also
|
839
845
|
--------
|
@@ -854,7 +860,7 @@ class Density:
|
|
854
860
|
|
855
861
|
def _save_mrc(self, filename: str, gzip: bool = False) -> None:
|
856
862
|
"""
|
857
|
-
Writes
|
863
|
+
Writes class instance to disk as mrc file.
|
858
864
|
|
859
865
|
Parameters
|
860
866
|
----------
|
@@ -873,6 +879,7 @@ class Density:
|
|
873
879
|
mrc.header.nzstart, mrc.header.nystart, mrc.header.nxstart = np.rint(
|
874
880
|
np.divide(self.origin, self.sampling_rate)
|
875
881
|
)
|
882
|
+
mrc.header.origin = tuple(x for x in self.origin)
|
876
883
|
# mrcfile library expects origin to be in xyz format
|
877
884
|
mrc.header.mapc, mrc.header.mapr, mrc.header.maps = (1, 2, 3)
|
878
885
|
mrc.header["origin"] = tuple(self.origin[::-1])
|
@@ -974,7 +981,7 @@ class Density:
|
|
974
981
|
self.metadata["std"] = self.metadata.get("std", 0)
|
975
982
|
self.metadata["min"] = self.metadata.get("min", 0)
|
976
983
|
self.metadata["max"] = self.metadata.get("max", 0)
|
977
|
-
if
|
984
|
+
if not isinstance(self.data, np.memmap):
|
978
985
|
self.metadata["mean"] = self.data.mean()
|
979
986
|
self.metadata["std"] = self.data.std()
|
980
987
|
self.metadata["min"] = self.data.min()
|
@@ -986,11 +993,15 @@ class Density:
|
|
986
993
|
@property
|
987
994
|
def empty(self) -> "Density":
|
988
995
|
"""
|
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
|
-
|
996
|
+
Returns a copy of the class instance with all elements in
|
997
|
+
:py:attr:`Density.data` set to zero. :py:attr:`Density.metadata` will be
|
998
|
+
initialized accordingly. :py:attr:`Density.origin` and
|
999
|
+
:py:attr:`Density.sampling_rate` are copied.
|
1000
|
+
|
1001
|
+
Returns
|
1002
|
+
-------
|
1003
|
+
:py:class:`Density`
|
1004
|
+
Empty class instance.
|
994
1005
|
|
995
1006
|
Examples
|
996
1007
|
--------
|
@@ -1010,7 +1021,12 @@ class Density:
|
|
1010
1021
|
|
1011
1022
|
def copy(self) -> "Density":
|
1012
1023
|
"""
|
1013
|
-
|
1024
|
+
Create a copy of the class instance.
|
1025
|
+
|
1026
|
+
Returns
|
1027
|
+
-------
|
1028
|
+
:py:class:`Density`
|
1029
|
+
A copy of the class instance.
|
1014
1030
|
|
1015
1031
|
Examples
|
1016
1032
|
--------
|
@@ -1029,8 +1045,7 @@ class Density:
|
|
1029
1045
|
|
1030
1046
|
def to_memmap(self) -> None:
|
1031
1047
|
"""
|
1032
|
-
Converts
|
1033
|
-
a :obj:`numpy.memmap` instance.
|
1048
|
+
Converts :py:attr:`Density.data` to a :obj:`numpy.memmap`.
|
1034
1049
|
|
1035
1050
|
Examples
|
1036
1051
|
--------
|
@@ -1055,7 +1070,7 @@ class Density:
|
|
1055
1070
|
--------
|
1056
1071
|
:py:meth:`Density.to_numpy`
|
1057
1072
|
"""
|
1058
|
-
if
|
1073
|
+
if isinstance(self.data, np.memmap):
|
1059
1074
|
return None
|
1060
1075
|
|
1061
1076
|
filename = array_to_memmap(arr=self.data)
|
@@ -1066,8 +1081,7 @@ class Density:
|
|
1066
1081
|
|
1067
1082
|
def to_numpy(self) -> None:
|
1068
1083
|
"""
|
1069
|
-
Converts
|
1070
|
-
an in-memory :obj:`numpy.ndarray`.
|
1084
|
+
Converts :py:attr:`Density.data` to an in-memory :obj:`numpy.ndarray`.
|
1071
1085
|
|
1072
1086
|
Examples
|
1073
1087
|
--------
|
@@ -1085,8 +1099,7 @@ class Density:
|
|
1085
1099
|
@property
|
1086
1100
|
def shape(self) -> Tuple[int]:
|
1087
1101
|
"""
|
1088
|
-
Returns the dimensions of
|
1089
|
-
attribute.
|
1102
|
+
Returns the dimensions of :py:attr:`Density.data`.
|
1090
1103
|
|
1091
1104
|
Returns
|
1092
1105
|
-------
|
@@ -1095,8 +1108,6 @@ class Density:
|
|
1095
1108
|
|
1096
1109
|
Examples
|
1097
1110
|
--------
|
1098
|
-
The following outlines the usage of :py:attr:`Density.shape`:
|
1099
|
-
|
1100
1111
|
>>> import numpy as np
|
1101
1112
|
>>> from tme import Density
|
1102
1113
|
>>> dens = Density(np.array([0, 1, 1, 1, 0]))
|
@@ -1108,13 +1119,12 @@ class Density:
|
|
1108
1119
|
@property
|
1109
1120
|
def data(self) -> NDArray:
|
1110
1121
|
"""
|
1111
|
-
Returns the value of
|
1112
|
-
attribute.
|
1122
|
+
Returns the value of :py:attr:`Density.data`.
|
1113
1123
|
|
1114
1124
|
Returns
|
1115
1125
|
-------
|
1116
1126
|
NDArray
|
1117
|
-
Value of the
|
1127
|
+
Value of the instance's :py:attr:`Density.data` attribute.
|
1118
1128
|
|
1119
1129
|
Examples
|
1120
1130
|
--------
|
@@ -1132,20 +1142,20 @@ class Density:
|
|
1132
1142
|
@data.setter
|
1133
1143
|
def data(self, data: NDArray) -> None:
|
1134
1144
|
"""
|
1135
|
-
Sets the value of the
|
1145
|
+
Sets the value of the instance's :py:attr:`Density.data` attribute.
|
1136
1146
|
"""
|
1137
1147
|
self._data = data
|
1138
1148
|
|
1139
1149
|
@property
|
1140
1150
|
def origin(self) -> NDArray:
|
1141
1151
|
"""
|
1142
|
-
Returns the value of the
|
1152
|
+
Returns the value of the instance's :py:attr:`Density.origin`
|
1143
1153
|
attribute.
|
1144
1154
|
|
1145
1155
|
Returns
|
1146
1156
|
-------
|
1147
1157
|
NDArray
|
1148
|
-
Value of the
|
1158
|
+
Value of the instance's :py:attr:`Density.origin` attribute.
|
1149
1159
|
|
1150
1160
|
Examples
|
1151
1161
|
--------
|
@@ -1171,8 +1181,7 @@ class Density:
|
|
1171
1181
|
@property
|
1172
1182
|
def sampling_rate(self) -> NDArray:
|
1173
1183
|
"""
|
1174
|
-
Returns the value of the
|
1175
|
-
attribute.
|
1184
|
+
Returns the value of the instance's :py:attr:`Density.sampling_rate` attribute.
|
1176
1185
|
|
1177
1186
|
Returns
|
1178
1187
|
-------
|
@@ -1193,7 +1202,7 @@ class Density:
|
|
1193
1202
|
@property
|
1194
1203
|
def metadata(self) -> Dict:
|
1195
1204
|
"""
|
1196
|
-
Returns the
|
1205
|
+
Returns the instance's :py:attr:`Density.metadata` attribute.
|
1197
1206
|
|
1198
1207
|
Returns
|
1199
1208
|
-------
|
@@ -1235,7 +1244,7 @@ class Density:
|
|
1235
1244
|
Pads the internal data array according to box.
|
1236
1245
|
|
1237
1246
|
Negative slices indices will result in a left-hand padding, while
|
1238
|
-
slice indices larger than the box_size property of the
|
1247
|
+
slice indices larger than the box_size property of the class
|
1239
1248
|
instance will result in a right-hand padding.
|
1240
1249
|
|
1241
1250
|
Parameters
|
@@ -1264,17 +1273,15 @@ class Density:
|
|
1264
1273
|
|
1265
1274
|
def adjust_box(self, box: Tuple[slice], pad_kwargs: Dict = {}) -> None:
|
1266
1275
|
"""
|
1267
|
-
Adjusts
|
1276
|
+
Adjusts :py:attr:`Density.data` and :py:attr:`Density.origin`
|
1268
1277
|
according to the provided box.
|
1269
1278
|
|
1270
1279
|
Parameters
|
1271
1280
|
----------
|
1272
1281
|
box : tuple of slices
|
1273
|
-
|
1274
|
-
should be sliced. See :py:meth:`Density.trim_box` on how to produce
|
1275
|
-
such an object.
|
1282
|
+
Description of how each axis of :py:attr:`Density.data` should be sliced.
|
1276
1283
|
pad_kwargs: dict, optional
|
1277
|
-
Parameter dictionary passed to numpy
|
1284
|
+
Parameter dictionary passed to :obj:`numpy.pad`.
|
1278
1285
|
|
1279
1286
|
See Also
|
1280
1287
|
--------
|
@@ -1454,8 +1461,8 @@ class Density:
|
|
1454
1461
|
Raises
|
1455
1462
|
------
|
1456
1463
|
ValueError
|
1457
|
-
If the length of
|
1458
|
-
|
1464
|
+
If the length of ``new_shape`` does not match the dimensionality of
|
1465
|
+
:py:attr:`Density.data`.
|
1459
1466
|
|
1460
1467
|
Examples
|
1461
1468
|
--------
|
@@ -1469,13 +1476,6 @@ class Density:
|
|
1469
1476
|
>>> dens.data
|
1470
1477
|
array([0, 1, 1, 1, 0])
|
1471
1478
|
|
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
1479
|
If ``center`` is set to False, the padding values will be appended:
|
1480
1480
|
|
1481
1481
|
>>> dens = Density(np.array([1,1,1]))
|
@@ -1483,6 +1483,12 @@ class Density:
|
|
1483
1483
|
>>> dens.data
|
1484
1484
|
array([1, 1, 1, 0, 0])
|
1485
1485
|
|
1486
|
+
It's also possible to pass a user-defined ``padding_value``:
|
1487
|
+
|
1488
|
+
>>> dens = Density(np.array([1,1,1]))
|
1489
|
+
>>> dens.pad(new_shape = (5,), center = True, padding_value = -1)
|
1490
|
+
>>> dens.data
|
1491
|
+
array([-1, 1, 1, 1, -1])
|
1486
1492
|
"""
|
1487
1493
|
if len(new_shape) != self.data.ndim:
|
1488
1494
|
raise ValueError(
|
@@ -1522,7 +1528,7 @@ class Density:
|
|
1522
1528
|
Returns
|
1523
1529
|
-------
|
1524
1530
|
:py:class:`Density`
|
1525
|
-
A centered copy of the
|
1531
|
+
A centered copy of the class instance.
|
1526
1532
|
NDArray
|
1527
1533
|
The offset between array center and center of mass.
|
1528
1534
|
|
@@ -1531,7 +1537,6 @@ class Density:
|
|
1531
1537
|
:py:meth:`Density.trim_box`
|
1532
1538
|
:py:meth:`Density.minimum_enclosing_box`
|
1533
1539
|
|
1534
|
-
|
1535
1540
|
Examples
|
1536
1541
|
--------
|
1537
1542
|
:py:meth:`Density.centered` returns a tuple containing a centered version
|
@@ -1591,123 +1596,15 @@ class Density:
|
|
1591
1596
|
shift = np.subtract(center, self.center_of_mass(ret.data, cutoff))
|
1592
1597
|
return ret, shift
|
1593
1598
|
|
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
1599
|
def rigid_transform(
|
1703
1600
|
self,
|
1704
1601
|
rotation_matrix: NDArray,
|
1705
1602
|
translation: NDArray = None,
|
1706
1603
|
order: int = 3,
|
1707
|
-
use_geometric_center: bool =
|
1604
|
+
use_geometric_center: bool = True,
|
1708
1605
|
) -> "Density":
|
1709
1606
|
"""
|
1710
|
-
Performs a rigid transform of the
|
1607
|
+
Performs a rigid transform of the class instance.
|
1711
1608
|
|
1712
1609
|
Parameters
|
1713
1610
|
----------
|
@@ -1756,89 +1653,134 @@ class Density:
|
|
1756
1653
|
--------
|
1757
1654
|
:py:meth:`Density.centered`, :py:meth:`Density.minimum_enclosing_box`
|
1758
1655
|
"""
|
1759
|
-
|
1760
|
-
|
1761
|
-
self.rotate_array(
|
1656
|
+
ret = self.empty
|
1657
|
+
NumpyFFTWBackend().rigid_transform(
|
1762
1658
|
arr=self.data,
|
1763
1659
|
rotation_matrix=rotation_matrix,
|
1764
1660
|
translation=translation,
|
1765
|
-
order=order,
|
1766
1661
|
use_geometric_center=use_geometric_center,
|
1767
|
-
out=
|
1662
|
+
out=ret.data,
|
1663
|
+
order=order,
|
1768
1664
|
)
|
1769
|
-
eps = np.finfo(transformed_map.data.dtype).eps
|
1770
|
-
transformed_map.data[transformed_map.data < eps] = 0
|
1771
|
-
return transformed_map
|
1772
1665
|
|
1773
|
-
|
1666
|
+
eps = np.finfo(ret.data.dtype).eps
|
1667
|
+
ret.data[np.abs(ret.data) < eps] = 0
|
1668
|
+
return ret
|
1669
|
+
|
1670
|
+
def resample(
|
1671
|
+
self, new_sampling_rate: Tuple[float], method: str = "spline", order: int = 1
|
1672
|
+
) -> "Density":
|
1774
1673
|
"""
|
1775
|
-
|
1674
|
+
Resamples :py:attr:`Density.data` to ``new_sampling_rate``.
|
1776
1675
|
|
1777
1676
|
Parameters
|
1778
1677
|
----------
|
1779
|
-
|
1780
|
-
|
1678
|
+
new_sampling_rate : tuple of floats or float
|
1679
|
+
Sampling rate to resample to for a single or all axes.
|
1680
|
+
method: str, optional
|
1681
|
+
Resampling method to use, defaults to `spline`. Availabe options are:
|
1682
|
+
|
1683
|
+
+---------+----------------------------------------------------------+
|
1684
|
+
| spline | Smooth spline interpolation via :obj:`scipy.ndimage.zoom`|
|
1685
|
+
+---------+----------------------------------------------------------+
|
1686
|
+
| fourier | Frequency preserving Fourier cropping |
|
1687
|
+
+---------+----------------------------------------------------------+
|
1688
|
+
|
1689
|
+
order : int, optional
|
1690
|
+
Order of spline used for interpolation, by default 1. Ignored when
|
1691
|
+
``method`` is `fourier`.
|
1781
1692
|
|
1782
1693
|
Raises
|
1783
1694
|
------
|
1784
1695
|
ValueError
|
1785
|
-
If
|
1696
|
+
If ``method`` is not supported.
|
1786
1697
|
|
1787
1698
|
Returns
|
1788
1699
|
-------
|
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.")
|
1700
|
+
:py:class:`Density`
|
1701
|
+
A resampled copy of the class instance.
|
1794
1702
|
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1703
|
+
Examples
|
1704
|
+
--------
|
1705
|
+
The following makes use of :py:meth:`tme.matching_utils.create_mask`
|
1706
|
+
to define a :py:class:`Density` instance containing a 2D circle with
|
1707
|
+
a sampling rate of 2
|
1799
1708
|
|
1800
|
-
|
1801
|
-
|
1709
|
+
>>> from tme import Density
|
1710
|
+
>>> from tme.matching_utils import create_mask
|
1711
|
+
>>> mask = create_mask(
|
1712
|
+
>>> mask_type="ellipse",
|
1713
|
+
>>> shape=(11,11),
|
1714
|
+
>>> center=(5,5),
|
1715
|
+
>>> radius=3
|
1716
|
+
>>> )
|
1717
|
+
>>> dens = Density(mask, sampling_rate=2)
|
1718
|
+
>>> dens
|
1719
|
+
Origin: (0.0, 0.0), sampling_rate: (2, 2), Shape: (11, 11)
|
1802
1720
|
|
1803
|
-
|
1721
|
+
Using :py:meth:`Density.resample` we can modulate the sampling rate
|
1722
|
+
using spline interpolation of desired order
|
1804
1723
|
|
1805
|
-
|
1806
|
-
|
1807
|
-
return ret
|
1724
|
+
>>> dens.resample(new_sampling_rate= 4, method="spline", order=3)
|
1725
|
+
Origin: (0.0, 0.0), sampling_rate: (4, 4), Shape: (6, 6)
|
1808
1726
|
|
1809
|
-
|
1810
|
-
|
1811
|
-
Resamples the current class instance to ``new_sampling_rate`` using
|
1812
|
-
spline interpolation of order ``order``.
|
1727
|
+
Or Fourier cropping which results in a less smooth output, but more faithfully
|
1728
|
+
captures the contained frequency information
|
1813
1729
|
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1730
|
+
>>> dens.resample(new_sampling_rate=4, method="fourier")
|
1731
|
+
Origin: (0.0, 0.0), sampling_rate: (4, 4), Shape: (6, 6)
|
1732
|
+
|
1733
|
+
``new_sampling_rate`` can also be specified per axis
|
1734
|
+
|
1735
|
+
>>> dens.resample(new_sampling_rate=(4,1), method="spline", order=3)
|
1736
|
+
Origin: (0.0, 0.0), sampling_rate: (4, 1), Shape: (6, 22)
|
1820
1737
|
|
1821
|
-
Returns
|
1822
|
-
-------
|
1823
|
-
Density
|
1824
|
-
A resampled instance of `Density` class.
|
1825
1738
|
"""
|
1826
|
-
|
1739
|
+
_supported_methods = ("spline", "fourier")
|
1740
|
+
if method not in _supported_methods:
|
1741
|
+
raise ValueError(
|
1742
|
+
f"Expected method to be one of {_supported_methods}, got '{method}'."
|
1743
|
+
)
|
1744
|
+
new_sampling_rate = np.array(new_sampling_rate)
|
1827
1745
|
new_sampling_rate = np.repeat(
|
1828
|
-
new_sampling_rate,
|
1746
|
+
new_sampling_rate, self.data.ndim // new_sampling_rate.size
|
1829
1747
|
)
|
1830
|
-
scale_factor = np.divide(map_copy.sampling_rate, new_sampling_rate)
|
1831
1748
|
|
1832
|
-
|
1833
|
-
|
1749
|
+
ret = self.copy()
|
1750
|
+
scale_factor = np.divide(ret.sampling_rate, new_sampling_rate)
|
1751
|
+
if method == "spline":
|
1752
|
+
ret.data = zoom(ret.data, scale_factor, order=order)
|
1753
|
+
elif method == "fourier":
|
1754
|
+
ret_shape = np.round(np.multiply(scale_factor, ret.shape)).astype(int)
|
1755
|
+
|
1756
|
+
axis = range(len(ret_shape))
|
1757
|
+
mask = np.zeros(self.shape, dtype=bool)
|
1758
|
+
mask[tuple(slice(0, x) for x in ret_shape)] = 1
|
1759
|
+
mask = np.roll(
|
1760
|
+
mask, shift=-np.floor(np.divide(ret_shape, 2)).astype(int), axis=axis
|
1761
|
+
)
|
1762
|
+
mask_ret = np.zeros(ret_shape, dtype=bool)
|
1763
|
+
mask_ret[tuple(slice(0, x) for x in self.shape)] = 1
|
1764
|
+
mask_ret = np.roll(
|
1765
|
+
mask_ret,
|
1766
|
+
shift=-np.floor(np.divide(self.shape, 2)).astype(int),
|
1767
|
+
axis=axis,
|
1768
|
+
)
|
1769
|
+
|
1770
|
+
arr_ft = np.fft.fftn(self.data)
|
1771
|
+
arr_ft *= np.prod(ret_shape) / np.prod(self.shape)
|
1772
|
+
ret_ft = np.zeros(ret_shape, dtype=arr_ft.dtype)
|
1773
|
+
ret_ft[mask_ret] = arr_ft[mask]
|
1774
|
+
ret.data = np.real(np.fft.ifftn(ret_ft))
|
1834
1775
|
|
1835
|
-
|
1776
|
+
ret.sampling_rate = new_sampling_rate
|
1777
|
+
return ret
|
1836
1778
|
|
1837
1779
|
def density_boundary(
|
1838
1780
|
self, weight: float, fraction_surface: float = 0.1, volume_factor: float = 1.21
|
1839
1781
|
) -> Tuple[float]:
|
1840
1782
|
"""
|
1841
|
-
Computes the density boundary of the
|
1783
|
+
Computes the density boundary of the class instance. The density
|
1842
1784
|
boundary in this setting is defined as minimal and maximal density value
|
1843
1785
|
enclosing a certain ``weight``.
|
1844
1786
|
|
@@ -1894,31 +1836,31 @@ class Density:
|
|
1894
1836
|
self, density_boundaries: Tuple[float], method: str = "ConvexHull"
|
1895
1837
|
) -> NDArray:
|
1896
1838
|
"""
|
1897
|
-
Calculates the surface coordinates of the
|
1839
|
+
Calculates the surface coordinates of the class instance using
|
1898
1840
|
different boundary and surface detection methods. This method is relevant
|
1899
|
-
for determining coordinates used in template matching,
|
1900
|
-
see :py:class:`tme.
|
1841
|
+
for determining coordinates used in non-exhaustive template matching,
|
1842
|
+
see :py:class:`tme.matching_optimization.optimize_match`.
|
1901
1843
|
|
1902
1844
|
Parameters
|
1903
1845
|
----------
|
1904
1846
|
density_boundaries : tuple
|
1905
|
-
|
1906
|
-
|
1847
|
+
Lower and upper bound of density values to be considered
|
1848
|
+
(can be obtained from :py:meth:`Density.density_boundary`).
|
1907
1849
|
method : str, optional
|
1908
|
-
|
1850
|
+
Method to use for surface coordinate computation
|
1909
1851
|
|
1910
1852
|
+--------------+-----------------------------------------------------+
|
1911
|
-
|
|
1853
|
+
| ConvexHull | Use the lower bound density convex hull vertices. |
|
1912
1854
|
+--------------+-----------------------------------------------------+
|
1913
|
-
|
|
1855
|
+
| Weight | Use all coordinates within ``density_boundaries``. |
|
1914
1856
|
+--------------+-----------------------------------------------------+
|
1915
|
-
|
|
1857
|
+
| Sobel | Set densities below the lower bound density to zero |
|
1916
1858
|
| | apply a sobel filter and return density coordinates |
|
1917
1859
|
| | larger than 0.5 times the maximum filter value. |
|
1918
1860
|
+--------------+-----------------------------------------------------+
|
1919
|
-
|
|
1861
|
+
| Laplace | Like 'Sobel', but with a Laplace filter. |
|
1920
1862
|
+--------------+-----------------------------------------------------+
|
1921
|
-
|
|
1863
|
+
| Minimum | Like 'Sobel' and 'Laplace' but with a spherical |
|
1922
1864
|
| | minimum filter on the lower density bound. |
|
1923
1865
|
+--------------+-----------------------------------------------------+
|
1924
1866
|
|
@@ -1930,15 +1872,11 @@ class Density:
|
|
1930
1872
|
Returns
|
1931
1873
|
-------
|
1932
1874
|
NDArray
|
1933
|
-
An array of surface coordinates with shape (
|
1875
|
+
An array of surface coordinates with shape (points, dimensions).
|
1934
1876
|
|
1935
1877
|
References
|
1936
1878
|
----------
|
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
|
1879
|
+
.. [1] Cragnolini T, et al. (2021) Acta Crys Sect D Struct Biol
|
1942
1880
|
|
1943
1881
|
See Also
|
1944
1882
|
--------
|
@@ -1948,12 +1886,12 @@ class Density:
|
|
1948
1886
|
:py:class:`tme.matching_optimization.Envelope`
|
1949
1887
|
:py:class:`tme.matching_optimization.Chamfer`
|
1950
1888
|
"""
|
1951
|
-
|
1889
|
+
_available_methods = ["ConvexHull", "Weight", "Sobel", "Laplace", "Minimum"]
|
1952
1890
|
|
1953
|
-
if method not in
|
1891
|
+
if method not in _available_methods:
|
1954
1892
|
raise ValueError(
|
1955
1893
|
"Argument method has to be one of the following: %s"
|
1956
|
-
% ", ".join(
|
1894
|
+
% ", ".join(_available_methods)
|
1957
1895
|
)
|
1958
1896
|
|
1959
1897
|
lower_bound, upper_bound = density_boundaries
|
@@ -2001,7 +1939,7 @@ class Density:
|
|
2001
1939
|
def normal_vectors(self, coordinates: NDArray) -> NDArray:
|
2002
1940
|
"""
|
2003
1941
|
Calculates the normal vectors for the given coordinates on the densities
|
2004
|
-
of the
|
1942
|
+
of the class instance. If the normal vector to a given coordinate
|
2005
1943
|
can not be computed, the zero vector is returned instead. The output of this
|
2006
1944
|
function can e.g. be used in
|
2007
1945
|
:py:class:`tme.matching_optimization.NormalVectorScore`.
|
@@ -2051,12 +1989,7 @@ class Density:
|
|
2051
1989
|
in_box = np.logical_and(
|
2052
1990
|
coordinates < np.array(self.shape), coordinates >= 0
|
2053
1991
|
).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
|
-
|
1992
|
+
coordinates = coordinates[in_box, :]
|
2060
1993
|
for index in range(coordinates.shape[0]):
|
2061
1994
|
point = coordinates[index, :]
|
2062
1995
|
start = np.maximum(point - 1, 0)
|
@@ -2076,22 +2009,16 @@ class Density:
|
|
2076
2009
|
|
2077
2010
|
def core_mask(self) -> NDArray:
|
2078
2011
|
"""
|
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.
|
2012
|
+
Calculates a weighted core mask by performing iterative binary erosion on
|
2013
|
+
:py:attr:`Density.data`. In each iteration, all mask elements corresponding
|
2014
|
+
to a non-zero data elemnt are incremented by one. Therefore, a mask element
|
2015
|
+
with value N corresponds to a data value that remained non-zero for N iterations.
|
2016
|
+
Mask elements with high values are likely part of the core density [1]_.
|
2088
2017
|
|
2089
2018
|
Returns
|
2090
2019
|
-------
|
2091
2020
|
NDArray
|
2092
|
-
|
2093
|
-
indicate how many rounds of binary erosion were necessary to nullify
|
2094
|
-
a given data element.
|
2021
|
+
Core-weighted mask with shape of :py:attr:`Density.data`.
|
2095
2022
|
|
2096
2023
|
References
|
2097
2024
|
----------
|
@@ -2126,19 +2053,7 @@ class Density:
|
|
2126
2053
|
NDArray
|
2127
2054
|
Center of mass with shape (arr.ndim).
|
2128
2055
|
"""
|
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
|
2056
|
+
return NumpyFFTWBackend().center_of_mass(arr, cutoff)
|
2142
2057
|
|
2143
2058
|
@classmethod
|
2144
2059
|
def match_densities(
|
@@ -2187,21 +2102,20 @@ class Density:
|
|
2187
2102
|
-----
|
2188
2103
|
No densities below cutoff_template are present in the returned Density object.
|
2189
2104
|
"""
|
2190
|
-
from .
|
2105
|
+
from .matching_utils import normalize_template
|
2191
2106
|
from .matching_optimization import optimize_match, create_score_object
|
2192
2107
|
|
2193
2108
|
template_mask = template.empty
|
2194
|
-
template_mask.data
|
2109
|
+
template_mask.data.fill(1)
|
2195
2110
|
|
2196
|
-
|
2111
|
+
normalize_template(
|
2197
2112
|
template=template.data,
|
2198
2113
|
mask=template_mask.data,
|
2199
|
-
|
2114
|
+
n_observations=template_mask.data.sum(),
|
2200
2115
|
)
|
2201
2116
|
|
2202
2117
|
target_sampling_rate = np.array(target.sampling_rate)
|
2203
2118
|
template_sampling_rate = np.array(template.sampling_rate)
|
2204
|
-
|
2205
2119
|
target_sampling_rate = np.repeat(
|
2206
2120
|
target_sampling_rate, target.data.ndim // target_sampling_rate.size
|
2207
2121
|
)
|
@@ -2340,49 +2254,6 @@ class Density:
|
|
2340
2254
|
|
2341
2255
|
return out, final_translation, rotation_matrix
|
2342
2256
|
|
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
2257
|
@staticmethod
|
2387
2258
|
def fourier_shell_correlation(density1: "Density", density2: "Density") -> NDArray:
|
2388
2259
|
"""
|
@@ -2439,3 +2310,9 @@ class Density:
|
|
2439
2310
|
qidx = np.where(qbins < qx.max())
|
2440
2311
|
|
2441
2312
|
return np.vstack((qbins[qidx], FSC[qidx])).T
|
2313
|
+
|
2314
|
+
|
2315
|
+
def is_gzipped(filename: str) -> bool:
|
2316
|
+
"""Check if a file is a gzip file by reading its magic number."""
|
2317
|
+
with open(filename, "rb") as f:
|
2318
|
+
return f.read(2) == b"\x1f\x8b"
|