junifer 0.0.4.dev653__py3-none-any.whl → 0.0.4.dev691__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.
- junifer/_version.py +2 -2
- junifer/api/queue_context/htcondor_adapter.py +2 -2
- junifer/api/queue_context/tests/test_htcondor_adapter.py +2 -2
- junifer/api/tests/test_api_utils.py +5 -1
- junifer/preprocess/__init__.py +1 -0
- junifer/preprocess/bold_warper.py +4 -0
- junifer/preprocess/warping/__init__.py +6 -0
- junifer/preprocess/warping/_ants_warper.py +167 -0
- junifer/preprocess/warping/_fsl_warper.py +109 -0
- junifer/preprocess/warping/space_warper.py +203 -0
- junifer/preprocess/warping/tests/test_space_warper.py +198 -0
- junifer/utils/logging.py +5 -1
- {junifer-0.0.4.dev653.dist-info → junifer-0.0.4.dev691.dist-info}/METADATA +4 -2
- {junifer-0.0.4.dev653.dist-info → junifer-0.0.4.dev691.dist-info}/RECORD +19 -14
- {junifer-0.0.4.dev653.dist-info → junifer-0.0.4.dev691.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.4.dev653.dist-info → junifer-0.0.4.dev691.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.4.dev653.dist-info → junifer-0.0.4.dev691.dist-info}/WHEEL +0 -0
- {junifer-0.0.4.dev653.dist-info → junifer-0.0.4.dev691.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.4.dev653.dist-info → junifer-0.0.4.dev691.dist-info}/top_level.txt +0 -0
junifer/_version.py
CHANGED
@@ -12,5 +12,5 @@ __version__: str
|
|
12
12
|
__version_tuple__: VERSION_TUPLE
|
13
13
|
version_tuple: VERSION_TUPLE
|
14
14
|
|
15
|
-
__version__ = version = '0.0.4.
|
16
|
-
__version_tuple__ = version_tuple = (0, 0, 4, '
|
15
|
+
__version__ = version = '0.0.4.dev691'
|
16
|
+
__version_tuple__ = version_tuple = (0, 0, 4, 'dev691')
|
@@ -274,8 +274,8 @@ class HTCondorAdapter(QueueContextAdapter):
|
|
274
274
|
)
|
275
275
|
fixed += (
|
276
276
|
f"JOB run{idx} {self._submit_run_path}\n"
|
277
|
-
f
|
278
|
-
f
|
277
|
+
f'VARS run{idx} element="{str_element}" ' # needs to be
|
278
|
+
f'log_element="{log_element}"\n\n' # double quoted
|
279
279
|
)
|
280
280
|
var = ""
|
281
281
|
if self._collect == "yes":
|
@@ -139,13 +139,13 @@ def test_HTCondorAdapter_run_collect(
|
|
139
139
|
(
|
140
140
|
[("sub01", "ses01")],
|
141
141
|
"yes",
|
142
|
-
|
142
|
+
'element="sub01,ses01" log_element="sub01-ses01"',
|
143
143
|
),
|
144
144
|
(["sub01"], "on_success_only", "JOB collect"),
|
145
145
|
(
|
146
146
|
[("sub01", "ses01")],
|
147
147
|
"on_success_only",
|
148
|
-
|
148
|
+
'element="sub01,ses01" log_element="sub01-ses01"',
|
149
149
|
),
|
150
150
|
],
|
151
151
|
)
|
@@ -45,9 +45,13 @@ def test_get_dependency_information_short() -> None:
|
|
45
45
|
"httpx",
|
46
46
|
"tqdm",
|
47
47
|
"templateflow",
|
48
|
+
"looseversion",
|
48
49
|
]
|
49
|
-
|
50
|
+
|
51
|
+
python_minor_version = int(pl.python_version_tuple()[1])
|
52
|
+
if python_minor_version < 10:
|
50
53
|
dependency_list.append("importlib_metadata")
|
54
|
+
|
51
55
|
assert frozenset(dependency_information.keys()) == frozenset(
|
52
56
|
dependency_list
|
53
57
|
)
|
junifer/preprocess/__init__.py
CHANGED
@@ -30,6 +30,10 @@ from .fsl.apply_warper import _ApplyWarper
|
|
30
30
|
class BOLDWarper(BasePreprocessor):
|
31
31
|
"""Class for warping BOLD NIfTI images.
|
32
32
|
|
33
|
+
.. deprecated:: 0.0.3
|
34
|
+
`BOLDWarper` will be removed in v0.0.4, it is replaced by
|
35
|
+
`SpaceWarper` because the latter works also with T1w data.
|
36
|
+
|
33
37
|
Parameters
|
34
38
|
----------
|
35
39
|
using : {"fsl", "ants"}
|
@@ -0,0 +1,167 @@
|
|
1
|
+
"""Provide class for space warping via ANTs antsApplyTransforms."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from typing import (
|
7
|
+
Any,
|
8
|
+
ClassVar,
|
9
|
+
Dict,
|
10
|
+
List,
|
11
|
+
Set,
|
12
|
+
Union,
|
13
|
+
)
|
14
|
+
|
15
|
+
import nibabel as nib
|
16
|
+
import numpy as np
|
17
|
+
|
18
|
+
from ...data import get_template, get_xfm
|
19
|
+
from ...pipeline import WorkDirManager
|
20
|
+
from ...utils import logger, run_ext_cmd
|
21
|
+
|
22
|
+
|
23
|
+
class ANTsWarper:
|
24
|
+
"""Class for space warping via ANTs antsApplyTransforms.
|
25
|
+
|
26
|
+
This class uses ANTs' ``ResampleImage`` for resampling (if required) and
|
27
|
+
``antsApplyTransforms`` for transformation.
|
28
|
+
|
29
|
+
"""
|
30
|
+
|
31
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
|
32
|
+
{
|
33
|
+
"name": "ants",
|
34
|
+
"commands": ["ResampleImage", "antsApplyTransforms"],
|
35
|
+
},
|
36
|
+
]
|
37
|
+
|
38
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nibabel"}
|
39
|
+
|
40
|
+
def preprocess(
|
41
|
+
self,
|
42
|
+
input: Dict[str, Any],
|
43
|
+
extra_input: Dict[str, Any],
|
44
|
+
reference: str,
|
45
|
+
) -> Dict[str, Any]:
|
46
|
+
"""Preprocess using ANTs.
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
input : dict
|
51
|
+
A single input from the Junifer Data object in which to preprocess.
|
52
|
+
extra_input : dict
|
53
|
+
The other fields in the Junifer Data object. Should have ``T1w``
|
54
|
+
and ``Warp`` data types.
|
55
|
+
reference : str
|
56
|
+
The data type or template space to use as reference for warping.
|
57
|
+
|
58
|
+
Returns
|
59
|
+
-------
|
60
|
+
dict
|
61
|
+
The ``input`` dictionary with modified ``data`` and ``space`` key
|
62
|
+
values and new ``reference_path`` key whose value points to the
|
63
|
+
reference file used for warping.
|
64
|
+
|
65
|
+
"""
|
66
|
+
# Create element-specific tempdir for storing post-warping assets
|
67
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
68
|
+
prefix="ants_warper"
|
69
|
+
)
|
70
|
+
|
71
|
+
# Native space warping
|
72
|
+
if reference == "T1w":
|
73
|
+
logger.debug("Using ANTs for space warping")
|
74
|
+
|
75
|
+
# Get the min of the voxel sizes from input and use it as the
|
76
|
+
# resolution
|
77
|
+
resolution = np.min(input["data"].header.get_zooms()[:3])
|
78
|
+
|
79
|
+
# Create a tempfile for resampled reference output
|
80
|
+
resample_image_out_path = (
|
81
|
+
element_tempdir / "resampled_reference.nii.gz"
|
82
|
+
)
|
83
|
+
# Set ResampleImage command
|
84
|
+
resample_image_cmd = [
|
85
|
+
"ResampleImage",
|
86
|
+
"3", # image dimension
|
87
|
+
f"{extra_input['T1w']['path'].resolve()}",
|
88
|
+
f"{resample_image_out_path.resolve()}",
|
89
|
+
f"{resolution}x{resolution}x{resolution}",
|
90
|
+
"0", # option for spacing and not size
|
91
|
+
"3 3", # Lanczos windowed sinc
|
92
|
+
]
|
93
|
+
# Call ResampleImage
|
94
|
+
run_ext_cmd(name="ResampleImage", cmd=resample_image_cmd)
|
95
|
+
|
96
|
+
# Create a tempfile for warped output
|
97
|
+
apply_transforms_out_path = element_tempdir / "output.nii.gz"
|
98
|
+
# Set antsApplyTransforms command
|
99
|
+
apply_transforms_cmd = [
|
100
|
+
"antsApplyTransforms",
|
101
|
+
"-d 3",
|
102
|
+
"-e 3",
|
103
|
+
"-n LanczosWindowedSinc",
|
104
|
+
f"-i {input['path'].resolve()}",
|
105
|
+
# use resampled reference
|
106
|
+
f"-r {resample_image_out_path.resolve()}",
|
107
|
+
f"-t {extra_input['Warp']['path'].resolve()}",
|
108
|
+
f"-o {apply_transforms_out_path.resolve()}",
|
109
|
+
]
|
110
|
+
# Call antsApplyTransforms
|
111
|
+
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
112
|
+
|
113
|
+
# Load nifti
|
114
|
+
input["data"] = nib.load(apply_transforms_out_path)
|
115
|
+
# Save resampled reference path
|
116
|
+
input["reference_path"] = resample_image_out_path
|
117
|
+
# Use reference input's space as warped input's space
|
118
|
+
input["space"] = extra_input["T1w"]["space"]
|
119
|
+
|
120
|
+
# Template space warping
|
121
|
+
else:
|
122
|
+
logger.debug(
|
123
|
+
f"Using ANTs to warp data from {input['space']} to {reference}"
|
124
|
+
)
|
125
|
+
|
126
|
+
# Get xfm file
|
127
|
+
xfm_file_path = get_xfm(src=input["space"], dst=reference)
|
128
|
+
# Get template space image
|
129
|
+
template_space_img = get_template(
|
130
|
+
space=reference,
|
131
|
+
target_data=input,
|
132
|
+
extra_input=None,
|
133
|
+
)
|
134
|
+
|
135
|
+
# Create component-scoped tempdir
|
136
|
+
tempdir = WorkDirManager().get_tempdir(prefix="ants_warper")
|
137
|
+
# Save template
|
138
|
+
template_space_img_path = tempdir / f"{reference}_T1w.nii.gz"
|
139
|
+
nib.save(template_space_img, template_space_img_path)
|
140
|
+
|
141
|
+
# Create a tempfile for warped output
|
142
|
+
warped_output_path = element_tempdir / (
|
143
|
+
f"data_warped_from_{input['space']}_to_" f"{reference}.nii.gz"
|
144
|
+
)
|
145
|
+
|
146
|
+
# Set antsApplyTransforms command
|
147
|
+
apply_transforms_cmd = [
|
148
|
+
"antsApplyTransforms",
|
149
|
+
"-d 3",
|
150
|
+
"-e 3",
|
151
|
+
"-n LanczosWindowedSinc",
|
152
|
+
f"-i {input['path'].resolve()}",
|
153
|
+
f"-r {template_space_img_path.resolve()}",
|
154
|
+
f"-t {xfm_file_path.resolve()}",
|
155
|
+
f"-o {warped_output_path.resolve()}",
|
156
|
+
]
|
157
|
+
# Call antsApplyTransforms
|
158
|
+
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
159
|
+
|
160
|
+
# Delete tempdir
|
161
|
+
WorkDirManager().delete_tempdir(tempdir)
|
162
|
+
|
163
|
+
# Modify target data
|
164
|
+
input["data"] = nib.load(warped_output_path)
|
165
|
+
input["space"] = reference
|
166
|
+
|
167
|
+
return input
|
@@ -0,0 +1,109 @@
|
|
1
|
+
"""Provide class for space warping via FSL FLIRT."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from typing import (
|
7
|
+
Any,
|
8
|
+
ClassVar,
|
9
|
+
Dict,
|
10
|
+
List,
|
11
|
+
Set,
|
12
|
+
Union,
|
13
|
+
)
|
14
|
+
|
15
|
+
import nibabel as nib
|
16
|
+
import numpy as np
|
17
|
+
|
18
|
+
from ...pipeline import WorkDirManager
|
19
|
+
from ...utils import logger, run_ext_cmd
|
20
|
+
|
21
|
+
|
22
|
+
class FSLWarper:
|
23
|
+
"""Class for space warping via FSL FLIRT.
|
24
|
+
|
25
|
+
This class uses FSL FLIRT's ``flirt`` for resampling and ``applywarp`` for
|
26
|
+
transformation.
|
27
|
+
|
28
|
+
"""
|
29
|
+
|
30
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
|
31
|
+
{
|
32
|
+
"name": "fsl",
|
33
|
+
"commands": ["flirt", "applywarp"],
|
34
|
+
},
|
35
|
+
]
|
36
|
+
|
37
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nibabel"}
|
38
|
+
|
39
|
+
def preprocess(
|
40
|
+
self,
|
41
|
+
input: Dict[str, Any],
|
42
|
+
extra_input: Dict[str, Any],
|
43
|
+
) -> Dict[str, Any]:
|
44
|
+
"""Preprocess using FSL.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
input : dict
|
49
|
+
A single input from the Junifer Data object in which to preprocess.
|
50
|
+
extra_input : dict
|
51
|
+
The other fields in the Junifer Data object. Should have ``T1w``
|
52
|
+
and ``Warp`` data types.
|
53
|
+
|
54
|
+
Returns
|
55
|
+
-------
|
56
|
+
dict
|
57
|
+
The ``input`` dictionary with modified ``data`` and ``space`` key
|
58
|
+
values and new ``reference_path`` key whose value points to the
|
59
|
+
reference file used for warping.
|
60
|
+
|
61
|
+
"""
|
62
|
+
logger.debug("Using FSL for space warping")
|
63
|
+
|
64
|
+
# Get the min of the voxel sizes from input and use it as the
|
65
|
+
# resolution
|
66
|
+
resolution = np.min(input["data"].header.get_zooms()[:3])
|
67
|
+
|
68
|
+
# Create element-specific tempdir for storing post-warping assets
|
69
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
70
|
+
prefix="fsl_warper"
|
71
|
+
)
|
72
|
+
|
73
|
+
# Create a tempfile for resampled reference output
|
74
|
+
flirt_out_path = element_tempdir / "resampled_reference.nii.gz"
|
75
|
+
# Set flirt command
|
76
|
+
flirt_cmd = [
|
77
|
+
"flirt",
|
78
|
+
"-interp spline",
|
79
|
+
f"-in {extra_input['T1w']['path'].resolve()}",
|
80
|
+
f"-ref {extra_input['T1w']['path'].resolve()}",
|
81
|
+
f"-applyisoxfm {resolution}",
|
82
|
+
f"-out {flirt_out_path.resolve()}",
|
83
|
+
]
|
84
|
+
# Call flirt
|
85
|
+
run_ext_cmd(name="flirt", cmd=flirt_cmd)
|
86
|
+
|
87
|
+
# Create a tempfile for warped output
|
88
|
+
applywarp_out_path = element_tempdir / "output.nii.gz"
|
89
|
+
# Set applywarp command
|
90
|
+
applywarp_cmd = [
|
91
|
+
"applywarp",
|
92
|
+
"--interp=spline",
|
93
|
+
f"-i {input['path'].resolve()}",
|
94
|
+
f"-r {flirt_out_path.resolve()}", # use resampled reference
|
95
|
+
f"-w {extra_input['Warp']['path'].resolve()}",
|
96
|
+
f"-o {applywarp_out_path.resolve()}",
|
97
|
+
]
|
98
|
+
# Call applywarp
|
99
|
+
run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
|
100
|
+
|
101
|
+
# Load nifti
|
102
|
+
input["data"] = nib.load(applywarp_out_path)
|
103
|
+
# Save resampled reference path
|
104
|
+
input["reference_path"] = flirt_out_path
|
105
|
+
|
106
|
+
# Use reference input's space as warped input's space
|
107
|
+
input["space"] = extra_input["T1w"]["space"]
|
108
|
+
|
109
|
+
return input
|
@@ -0,0 +1,203 @@
|
|
1
|
+
"""Provide class for warping data to other template spaces."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type, Union
|
7
|
+
|
8
|
+
from templateflow import api as tflow
|
9
|
+
|
10
|
+
from ...api.decorators import register_preprocessor
|
11
|
+
from ...utils import logger, raise_error
|
12
|
+
from ..base import BasePreprocessor
|
13
|
+
from ._ants_warper import ANTsWarper
|
14
|
+
from ._fsl_warper import FSLWarper
|
15
|
+
|
16
|
+
|
17
|
+
__all__ = ["SpaceWarper"]
|
18
|
+
|
19
|
+
|
20
|
+
@register_preprocessor
|
21
|
+
class SpaceWarper(BasePreprocessor):
|
22
|
+
"""Class for warping data to other template spaces.
|
23
|
+
|
24
|
+
Parameters
|
25
|
+
----------
|
26
|
+
using : {"fsl", "ants"}
|
27
|
+
Implementation to use for warping:
|
28
|
+
|
29
|
+
* "fsl" : Use FSL's ``applywarp``
|
30
|
+
* "ants" : Use ANTs' ``antsApplyTransforms``
|
31
|
+
|
32
|
+
reference : str
|
33
|
+
The data type to use as reference for warping, can be either a data
|
34
|
+
type like ``"T1w"`` or a template space like ``"MNI152NLin2009cAsym"``.
|
35
|
+
Use ``"T1w"`` for native space warping and named templates for
|
36
|
+
template space warping.
|
37
|
+
on : {"T1w", "BOLD", "VBM_GM", "VBM_WM", "fALFF", "GCOR", "LCOR"} or list \
|
38
|
+
of the options
|
39
|
+
The data type to warp.
|
40
|
+
|
41
|
+
Raises
|
42
|
+
------
|
43
|
+
ValueError
|
44
|
+
If ``using`` is invalid or
|
45
|
+
if ``reference`` is invalid.
|
46
|
+
|
47
|
+
"""
|
48
|
+
|
49
|
+
_CONDITIONAL_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, Type]]]] = [
|
50
|
+
{
|
51
|
+
"using": "fsl",
|
52
|
+
"depends_on": FSLWarper,
|
53
|
+
},
|
54
|
+
{
|
55
|
+
"using": "ants",
|
56
|
+
"depends_on": ANTsWarper,
|
57
|
+
},
|
58
|
+
]
|
59
|
+
|
60
|
+
def __init__(
|
61
|
+
self, using: str, reference: str, on: Union[List[str], str]
|
62
|
+
) -> None:
|
63
|
+
"""Initialize the class."""
|
64
|
+
# Validate `using` parameter
|
65
|
+
valid_using = [dep["using"] for dep in self._CONDITIONAL_DEPENDENCIES]
|
66
|
+
if using not in valid_using:
|
67
|
+
raise_error(
|
68
|
+
f"Invalid value for `using`, should be one of: {valid_using}"
|
69
|
+
)
|
70
|
+
self.using = using
|
71
|
+
self.reference = reference
|
72
|
+
# Set required data types based on reference and
|
73
|
+
# initialize superclass
|
74
|
+
if self.reference == "T1w":
|
75
|
+
required_data_types = [self.reference, "Warp"]
|
76
|
+
# Listify on
|
77
|
+
if not isinstance(on, list):
|
78
|
+
on = [on]
|
79
|
+
# Extend required data types
|
80
|
+
required_data_types.extend(on)
|
81
|
+
|
82
|
+
super().__init__(
|
83
|
+
on=on,
|
84
|
+
required_data_types=required_data_types,
|
85
|
+
)
|
86
|
+
elif self.reference in tflow.templates():
|
87
|
+
super().__init__(on=on)
|
88
|
+
else:
|
89
|
+
raise_error(f"Unknown reference: {self.reference}")
|
90
|
+
|
91
|
+
def get_valid_inputs(self) -> List[str]:
|
92
|
+
"""Get valid data types for input.
|
93
|
+
|
94
|
+
Returns
|
95
|
+
-------
|
96
|
+
list of str
|
97
|
+
The list of data types that can be used as input for this
|
98
|
+
preprocessor.
|
99
|
+
|
100
|
+
"""
|
101
|
+
return ["T1w", "BOLD", "VBM_GM", "VBM_WM", "fALFF", "GCOR", "LCOR"]
|
102
|
+
|
103
|
+
def get_output_type(self, input_type: str) -> str:
|
104
|
+
"""Get output type.
|
105
|
+
|
106
|
+
Parameters
|
107
|
+
----------
|
108
|
+
input_type : str
|
109
|
+
The data type input to the preprocessor.
|
110
|
+
|
111
|
+
Returns
|
112
|
+
-------
|
113
|
+
str
|
114
|
+
The data type output by the preprocessor.
|
115
|
+
|
116
|
+
"""
|
117
|
+
# Does not add any new keys
|
118
|
+
return input_type
|
119
|
+
|
120
|
+
def preprocess(
|
121
|
+
self,
|
122
|
+
input: Dict[str, Any],
|
123
|
+
extra_input: Optional[Dict[str, Any]] = None,
|
124
|
+
) -> Tuple[Dict[str, Any], Optional[Dict[str, Dict[str, Any]]]]:
|
125
|
+
"""Preprocess.
|
126
|
+
|
127
|
+
Parameters
|
128
|
+
----------
|
129
|
+
input : dict
|
130
|
+
The input from the Junifer Data object.
|
131
|
+
extra_input : dict, optional
|
132
|
+
The other fields in the Junifer Data object.
|
133
|
+
|
134
|
+
Returns
|
135
|
+
-------
|
136
|
+
dict
|
137
|
+
The computed result as dictionary.
|
138
|
+
None
|
139
|
+
Extra "helper" data types as dictionary to add to the Junifer Data
|
140
|
+
object.
|
141
|
+
|
142
|
+
Raises
|
143
|
+
------
|
144
|
+
ValueError
|
145
|
+
If ``extra_input`` is None when transforming to native space
|
146
|
+
i.e., using ``"T1w"`` as reference.
|
147
|
+
RuntimeError
|
148
|
+
If the data is in the correct space and does not require
|
149
|
+
warping or
|
150
|
+
if FSL is used for template space warping.
|
151
|
+
|
152
|
+
"""
|
153
|
+
logger.info(f"Warping to {self.reference} space using SpaceWarper")
|
154
|
+
# Transform to native space
|
155
|
+
if self.using in ["fsl", "ants"] and self.reference == "T1w":
|
156
|
+
# Check for extra inputs
|
157
|
+
if extra_input is None:
|
158
|
+
raise_error(
|
159
|
+
"No extra input provided, requires `Warp` and "
|
160
|
+
f"`{self.reference}` data types in particular."
|
161
|
+
)
|
162
|
+
# Conditional preprocessor
|
163
|
+
if self.using == "fsl":
|
164
|
+
input = FSLWarper().preprocess(
|
165
|
+
input=input,
|
166
|
+
extra_input=extra_input,
|
167
|
+
)
|
168
|
+
elif self.using == "ants":
|
169
|
+
input = ANTsWarper().preprocess(
|
170
|
+
input=input,
|
171
|
+
extra_input=extra_input,
|
172
|
+
reference=self.reference,
|
173
|
+
)
|
174
|
+
# Transform to template space with ANTs possible
|
175
|
+
elif self.using == "ants" and self.reference != "T1w":
|
176
|
+
# Check pre-requirements for space manipulation
|
177
|
+
if self.reference == input["space"]:
|
178
|
+
raise_error(
|
179
|
+
(
|
180
|
+
f"The target data is in {self.reference} space "
|
181
|
+
"and thus warping will not be performed, hence you "
|
182
|
+
"should remove the SpaceWarper from the preprocess "
|
183
|
+
"step."
|
184
|
+
),
|
185
|
+
klass=RuntimeError,
|
186
|
+
)
|
187
|
+
|
188
|
+
input = ANTsWarper().preprocess(
|
189
|
+
input=input,
|
190
|
+
extra_input={},
|
191
|
+
reference=self.reference,
|
192
|
+
)
|
193
|
+
# Transform to template space with FSL not possible
|
194
|
+
elif self.using == "fsl" and self.reference != "T1w":
|
195
|
+
raise_error(
|
196
|
+
(
|
197
|
+
f"Warping to {self.reference} space not possible with "
|
198
|
+
"FSL, use ANTs instead."
|
199
|
+
),
|
200
|
+
klass=RuntimeError,
|
201
|
+
)
|
202
|
+
|
203
|
+
return input, None
|
@@ -0,0 +1,198 @@
|
|
1
|
+
"""Provide tests for SpaceWarper."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
import socket
|
7
|
+
from typing import TYPE_CHECKING, Tuple, Type
|
8
|
+
|
9
|
+
import pytest
|
10
|
+
from numpy.testing import assert_array_equal, assert_raises
|
11
|
+
|
12
|
+
from junifer.datagrabber import DataladHCP1200, DMCC13Benchmark
|
13
|
+
from junifer.datareader import DefaultDataReader
|
14
|
+
from junifer.pipeline.utils import _check_ants, _check_fsl
|
15
|
+
from junifer.preprocess import SpaceWarper
|
16
|
+
from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
|
17
|
+
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from junifer.datagrabber import BaseDataGrabber
|
21
|
+
|
22
|
+
|
23
|
+
@pytest.mark.parametrize(
|
24
|
+
"using, reference, error_type, error_msg",
|
25
|
+
[
|
26
|
+
("jam", "T1w", ValueError, "`using`"),
|
27
|
+
("ants", "juice", ValueError, "reference"),
|
28
|
+
("ants", "MNI152NLin2009cAsym", RuntimeError, "remove"),
|
29
|
+
("fsl", "MNI152NLin2009cAsym", RuntimeError, "ANTs"),
|
30
|
+
],
|
31
|
+
)
|
32
|
+
def test_SpaceWarper_errors(
|
33
|
+
using: str,
|
34
|
+
reference: str,
|
35
|
+
error_type: Type[Exception],
|
36
|
+
error_msg: str,
|
37
|
+
) -> None:
|
38
|
+
"""Test SpaceWarper errors.
|
39
|
+
|
40
|
+
Parameters
|
41
|
+
----------
|
42
|
+
using : str
|
43
|
+
The parametrized implementation method.
|
44
|
+
reference : str
|
45
|
+
The parametrized reference to use.
|
46
|
+
error_type : Exception-like object
|
47
|
+
The parametrized exception to check.
|
48
|
+
error_msg : str
|
49
|
+
The parametrized exception message to check.
|
50
|
+
|
51
|
+
"""
|
52
|
+
with PartlyCloudyTestingDataGrabber() as dg:
|
53
|
+
# Read data
|
54
|
+
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
55
|
+
# Preprocess data
|
56
|
+
with pytest.raises(error_type, match=error_msg):
|
57
|
+
SpaceWarper(
|
58
|
+
using=using,
|
59
|
+
reference=reference,
|
60
|
+
on="BOLD",
|
61
|
+
).preprocess(
|
62
|
+
input=element_data["BOLD"],
|
63
|
+
extra_input=element_data,
|
64
|
+
)
|
65
|
+
|
66
|
+
|
67
|
+
@pytest.mark.parametrize(
|
68
|
+
"datagrabber, element, using",
|
69
|
+
[
|
70
|
+
[
|
71
|
+
DMCC13Benchmark(
|
72
|
+
types=["BOLD", "T1w", "Warp"],
|
73
|
+
sessions=["wave1bas"],
|
74
|
+
tasks=["Rest"],
|
75
|
+
phase_encodings=["AP"],
|
76
|
+
runs=["1"],
|
77
|
+
native_t1w=True,
|
78
|
+
),
|
79
|
+
("f9057kp", "wave1bas", "Rest", "AP", "1"),
|
80
|
+
"ants",
|
81
|
+
],
|
82
|
+
[
|
83
|
+
DataladHCP1200(
|
84
|
+
tasks=["REST1"],
|
85
|
+
phase_encodings=["LR"],
|
86
|
+
ica_fix=True,
|
87
|
+
),
|
88
|
+
("100206", "REST1", "LR"),
|
89
|
+
"fsl",
|
90
|
+
],
|
91
|
+
],
|
92
|
+
)
|
93
|
+
@pytest.mark.skipif(_check_fsl() is False, reason="requires FSL to be in PATH")
|
94
|
+
@pytest.mark.skipif(
|
95
|
+
_check_ants() is False, reason="requires ANTs to be in PATH"
|
96
|
+
)
|
97
|
+
@pytest.mark.skipif(
|
98
|
+
socket.gethostname() != "juseless",
|
99
|
+
reason="only for juseless",
|
100
|
+
)
|
101
|
+
def test_SpaceWarper_native(
|
102
|
+
datagrabber: "BaseDataGrabber", element: Tuple[str, ...], using: str
|
103
|
+
) -> None:
|
104
|
+
"""Test SpaceWarper for native space warping.
|
105
|
+
|
106
|
+
Parameters
|
107
|
+
----------
|
108
|
+
datagrabber : DataGrabber-like object
|
109
|
+
The parametrized DataGrabber objects.
|
110
|
+
element : tuple of str
|
111
|
+
The parametrized elements.
|
112
|
+
using : str
|
113
|
+
The parametrized implementation method.
|
114
|
+
|
115
|
+
"""
|
116
|
+
with datagrabber as dg:
|
117
|
+
# Read data
|
118
|
+
element_data = DefaultDataReader().fit_transform(dg[element])
|
119
|
+
# Preprocess data
|
120
|
+
output, _ = SpaceWarper(
|
121
|
+
using=using,
|
122
|
+
reference="T1w",
|
123
|
+
on="BOLD",
|
124
|
+
).preprocess(
|
125
|
+
input=element_data["BOLD"],
|
126
|
+
extra_input=element_data,
|
127
|
+
)
|
128
|
+
# Check
|
129
|
+
assert isinstance(output, dict)
|
130
|
+
|
131
|
+
|
132
|
+
@pytest.mark.parametrize(
|
133
|
+
"datagrabber, element, space",
|
134
|
+
[
|
135
|
+
[
|
136
|
+
DMCC13Benchmark(
|
137
|
+
types=["T1w"],
|
138
|
+
sessions=["wave1bas"],
|
139
|
+
tasks=["Rest"],
|
140
|
+
phase_encodings=["AP"],
|
141
|
+
runs=["1"],
|
142
|
+
native_t1w=False,
|
143
|
+
),
|
144
|
+
("f9057kp", "wave1bas", "Rest", "AP", "1"),
|
145
|
+
"MNI152NLin2009aAsym",
|
146
|
+
],
|
147
|
+
[
|
148
|
+
DMCC13Benchmark(
|
149
|
+
types=["T1w"],
|
150
|
+
sessions=["wave1bas"],
|
151
|
+
tasks=["Rest"],
|
152
|
+
phase_encodings=["AP"],
|
153
|
+
runs=["1"],
|
154
|
+
native_t1w=False,
|
155
|
+
),
|
156
|
+
("f9057kp", "wave1bas", "Rest", "AP", "1"),
|
157
|
+
"MNI152NLin6Asym",
|
158
|
+
],
|
159
|
+
],
|
160
|
+
)
|
161
|
+
@pytest.mark.skipif(
|
162
|
+
_check_ants() is False, reason="requires ANTs to be in PATH"
|
163
|
+
)
|
164
|
+
def test_SpaceWarper_multi_mni(
|
165
|
+
datagrabber: "BaseDataGrabber",
|
166
|
+
element: Tuple[str, ...],
|
167
|
+
space: str,
|
168
|
+
) -> None:
|
169
|
+
"""Test SpaceWarper for MNI space warping.
|
170
|
+
|
171
|
+
Parameters
|
172
|
+
----------
|
173
|
+
datagrabber : DataGrabber-like object
|
174
|
+
The parametrized DataGrabber objects.
|
175
|
+
element : tuple of str
|
176
|
+
The parametrized elements.
|
177
|
+
space : str
|
178
|
+
The parametrized template space to transform to.
|
179
|
+
|
180
|
+
"""
|
181
|
+
with datagrabber as dg:
|
182
|
+
# Read data
|
183
|
+
element_data = DefaultDataReader().fit_transform(dg[element])
|
184
|
+
pre_xfm_data = element_data["T1w"]["data"].get_fdata().copy()
|
185
|
+
# Preprocess data
|
186
|
+
output, _ = SpaceWarper(
|
187
|
+
using="ants",
|
188
|
+
reference=space,
|
189
|
+
on=["T1w"],
|
190
|
+
).preprocess(
|
191
|
+
input=element_data["T1w"],
|
192
|
+
extra_input=element_data,
|
193
|
+
)
|
194
|
+
# Checks
|
195
|
+
assert isinstance(output, dict)
|
196
|
+
assert output["space"] == space
|
197
|
+
with assert_raises(AssertionError):
|
198
|
+
assert_array_equal(pre_xfm_data, output["data"])
|
junifer/utils/logging.py
CHANGED
@@ -4,9 +4,13 @@
|
|
4
4
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
|
+
try:
|
8
|
+
from distutils.version import LooseVersion
|
9
|
+
except ImportError: # pragma: no cover
|
10
|
+
from looseversion import LooseVersion
|
11
|
+
|
7
12
|
import logging
|
8
13
|
import sys
|
9
|
-
from distutils.version import LooseVersion
|
10
14
|
from pathlib import Path
|
11
15
|
from subprocess import PIPE, Popen, TimeoutExpired
|
12
16
|
from typing import Dict, NoReturn, Optional, Type, Union
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: junifer
|
3
|
-
Version: 0.0.4.
|
3
|
+
Version: 0.0.4.dev691
|
4
4
|
Summary: JUelich NeuroImaging FEature extractoR
|
5
5
|
Author-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
|
6
6
|
Maintainer-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
|
@@ -23,6 +23,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.9
|
24
24
|
Classifier: Programming Language :: Python :: 3.10
|
25
25
|
Classifier: Programming Language :: Python :: 3.11
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
26
27
|
Requires-Python: >=3.8
|
27
28
|
Description-Content-Type: text/markdown
|
28
29
|
License-File: LICENSE.md
|
@@ -36,11 +37,12 @@ Requires-Dist: nibabel <5.11,>=3.2.0
|
|
36
37
|
Requires-Dist: nilearn <=0.10.2,>=0.9.0
|
37
38
|
Requires-Dist: sqlalchemy <=2.1.0,>=1.4.27
|
38
39
|
Requires-Dist: ruamel.yaml <0.18,>=0.17
|
39
|
-
Requires-Dist: h5py
|
40
|
+
Requires-Dist: h5py >=3.10
|
40
41
|
Requires-Dist: httpx[http2] ==0.26.0
|
41
42
|
Requires-Dist: tqdm ==4.66.1
|
42
43
|
Requires-Dist: templateflow >=23.0.0
|
43
44
|
Requires-Dist: importlib-metadata ; python_version < "3.10"
|
45
|
+
Requires-Dist: looseversion ==1.3.0 ; python_version >= "3.12"
|
44
46
|
Provides-Extra: all
|
45
47
|
Requires-Dist: bctpy ==0.6.0 ; extra == 'all'
|
46
48
|
Requires-Dist: neurokit2 >=0.1.7 ; extra == 'all'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
junifer/__init__.py,sha256=x1UR2jUcrUdm2HNl-3Qvyi4UUrU6ms5qm2qcmNY7zZk,391
|
2
|
-
junifer/_version.py,sha256=
|
2
|
+
junifer/_version.py,sha256=2WPaxhKlhwhDOGRx-9LuqipNV4sZTUszLPQ6IRWVxY0,428
|
3
3
|
junifer/stats.py,sha256=sU5IZ2qFZWbzgSutQS_z42miIVItpSGmQYBn6KkD5fA,6162
|
4
4
|
junifer/api/__init__.py,sha256=YILu9M7SC0Ri4CVd90fELH2OnK_gvCYAXCoqBNCFE8E,257
|
5
5
|
junifer/api/cli.py,sha256=auw38tIH7ipTnaADM7on0or7zauY-BFII8nd-eRUEJs,13664
|
@@ -8,9 +8,9 @@ junifer/api/functions.py,sha256=Nvkby-Nlef_mMizdENC_Bodx8_MS5yoJ0nLsU7aGMgE,1125
|
|
8
8
|
junifer/api/parser.py,sha256=a3SSC2xO-PF1pqXZXFq8Sh9aVd-dmHolJbCkGyOUTAM,4416
|
9
9
|
junifer/api/utils.py,sha256=dyjTdPMwX9qeCrn8SQT2Pjshfnu-y1FEyujV7lCzvm0,3333
|
10
10
|
junifer/api/queue_context/__init__.py,sha256=lrDX54HCWt7-hvJ2XTP3dADQhWFFZZR9GfZh2cgCyAI,223
|
11
|
-
junifer/api/queue_context/htcondor_adapter.py,sha256=
|
11
|
+
junifer/api/queue_context/htcondor_adapter.py,sha256=XEzwMCFwGCoSReJKm3TU9poVN5-7lRZ1KUJw6_8v0b0,12681
|
12
12
|
junifer/api/queue_context/queue_context_adapter.py,sha256=a6UE8xavDfuaZbkWYsayVs6l-rwIrbpFSpqSyHsEeYY,1577
|
13
|
-
junifer/api/queue_context/tests/test_htcondor_adapter.py,sha256=
|
13
|
+
junifer/api/queue_context/tests/test_htcondor_adapter.py,sha256=XmtdqoUmYTdkwGqRvIX52_n-vaVnPfvz81K4iSc6P0c,7255
|
14
14
|
junifer/api/res/run_conda.sh,sha256=eskIn-fotITOlh--IUQKXh-SBOZkDkK8QvDcdQsyJ0M,509
|
15
15
|
junifer/api/res/run_venv.sh,sha256=OF-LOdOdpjCU0JX6Dz1boG4ErPutTiVVD_N7j520Fvk,499
|
16
16
|
junifer/api/res/afni/3dAFNItoNIFTI,sha256=Fsy5S5rwIBb1MepLfl_5FQhE7gv6NDwNyAR_k036NmM,51
|
@@ -27,7 +27,7 @@ junifer/api/res/fsl/flirt,sha256=tSjiUco8ui8AbHD7mTzChEwbR0Rf_4iJTgzYTPF_WuQ,42
|
|
27
27
|
junifer/api/res/fsl/img2imgcoord,sha256=Zmaw3oJYrEltcXiPyEubXry9ppAq3SND52tdDWGgeZk,49
|
28
28
|
junifer/api/res/fsl/run_fsl_docker.sh,sha256=mRLtZo0OgDwleoee2MG6rYI37HVuGNk9zOADwcl97RA,1122
|
29
29
|
junifer/api/res/fsl/std2imgcoord,sha256=-X5wRH6XMl0yqnTACJX6MFhO8DFOEWg42MHRxGvimXg,49
|
30
|
-
junifer/api/tests/test_api_utils.py,sha256=
|
30
|
+
junifer/api/tests/test_api_utils.py,sha256=zDRQiqFOaoO02FpzJCxEbZvsP4u4__Yr25e5k5MJwuI,2713
|
31
31
|
junifer/api/tests/test_cli.py,sha256=hou8Jn31v0SfjAwf5zIqQEbA1nxvL6uvVZUjnghAG3k,8553
|
32
32
|
junifer/api/tests/test_functions.py,sha256=WvedMpHZ9ckbvkuOgCxnyZLrKdi5bR4hGJyO6ywkbIA,15531
|
33
33
|
junifer/api/tests/test_parser.py,sha256=eUz2JPVb0_cxvoeI1O_C5PMNs5v_lDzGsN6fV1VW5Eg,6109
|
@@ -197,9 +197,9 @@ junifer/pipeline/tests/test_pipeline_step_mixin.py,sha256=_ykZzyNzREXy-r_yv1gY_j
|
|
197
197
|
junifer/pipeline/tests/test_registry.py,sha256=rYN0pO3gSueQ6XsasEM9VU5BkLyaNl8WaCZiaO8Rno0,4105
|
198
198
|
junifer/pipeline/tests/test_update_meta_mixin.py,sha256=UeWwpUi-Q5WVd36Fgfn_utXplSVXMSjLcdO2mR2xLTk,1355
|
199
199
|
junifer/pipeline/tests/test_workdir_manager.py,sha256=E1WY4C-GDsx2c49sePqr1WR_Y3nZ1kiRn4Veu506uTs,2801
|
200
|
-
junifer/preprocess/__init__.py,sha256
|
200
|
+
junifer/preprocess/__init__.py,sha256=fPZPFvx-BIUswBP4bx5HdBG1vLFkUMI4mnn6li339wU,375
|
201
201
|
junifer/preprocess/base.py,sha256=Bn1VdonQ1f_DDPwFMpdaeyfLfNSnROulr-U8HuGQ81A,6697
|
202
|
-
junifer/preprocess/bold_warper.py,sha256=
|
202
|
+
junifer/preprocess/bold_warper.py,sha256=pEQ1GaWTV2Ili9WyqJgtq0PGHm4hNztXyY9ixoLNZnw,9060
|
203
203
|
junifer/preprocess/ants/__init__.py,sha256=Uobmbhh6_gOowkF2hQNSQOh3AYeaXzarBXEcLJzhERE,112
|
204
204
|
junifer/preprocess/ants/ants_apply_transforms_warper.py,sha256=1qkTq4NoW-c8CDEvh8j4uuN_HtneXSpG0mqRc6_qrNk,5556
|
205
205
|
junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py,sha256=jJMZvX_3VDpXaxLDRNtVzlqcRsHjDTsUa_VywJ8rniE,1666
|
@@ -211,6 +211,11 @@ junifer/preprocess/fsl/apply_warper.py,sha256=k6ZzoDhXgsqcJZYYdx45Y3rN9xJERc0295
|
|
211
211
|
junifer/preprocess/fsl/tests/test_apply_warper.py,sha256=eCrSPYIGTKFDiBtseZFkghjhU7j7np59TZeGdKHkhMs,1324
|
212
212
|
junifer/preprocess/tests/test_bold_warper.py,sha256=sPS8Xd9Ix5TIvxiTCkfRDcA_zxk-2UUteIt68Rv6JGw,4600
|
213
213
|
junifer/preprocess/tests/test_preprocess_base.py,sha256=-0rpe8QjqYES36H6MHuDs3cv_6upHBdVHnFMgQsmEX4,2571
|
214
|
+
junifer/preprocess/warping/__init__.py,sha256=zW4DVt_RPJWT0-AsylGmh9wgFBDPkU-hx4VzV_qPayU,154
|
215
|
+
junifer/preprocess/warping/_ants_warper.py,sha256=Y1UzZ5jy1TvlLEkaQKW7jCNvEHufZMdQFbg2JpY3UaM,5690
|
216
|
+
junifer/preprocess/warping/_fsl_warper.py,sha256=eELmS44LYYANQaWR3VDKv8iwpEC2qnF9kbTYRanR2mE,3204
|
217
|
+
junifer/preprocess/warping/space_warper.py,sha256=honyMD9e2kK72oBWb4loA6lKZFs5kIjr1AYB4HU3eDQ,6435
|
218
|
+
junifer/preprocess/warping/tests/test_space_warper.py,sha256=ph92dIDOr9ih9tkqT0yo5tnQL3UkOgQRXG3WzP5QLSE,5586
|
214
219
|
junifer/storage/__init__.py,sha256=QlzBw9UrRhmg_f7zQVas9h313u3sfZIBicA3_0Skm4M,337
|
215
220
|
junifer/storage/base.py,sha256=UxDvj81gSmqqHspbSs1X_i9HvW5wXysDippI7HWM7aM,9654
|
216
221
|
junifer/storage/hdf5.py,sha256=oxdPuCG0hxzSDNH0uHnYFwVr_wp0g9yvgZf8bv3PkJM,35631
|
@@ -236,14 +241,14 @@ junifer/tests/test_stats.py,sha256=3vPMgYYpWxk8ECDFOMm3-dFBlh4XxjL83SwRBSBAHok,4
|
|
236
241
|
junifer/utils/__init__.py,sha256=sFpsDKbPPtoU3Db3OSO9Vo45u4yE7UfocNaBtD-qvdk,310
|
237
242
|
junifer/utils/fs.py,sha256=Jd9AoV2fIF7pT7KhXsn8T1O1fJ1_SFZgaFuOBAM7DG8,460
|
238
243
|
junifer/utils/helpers.py,sha256=Hp3yGZa9x659BCqFTd4keEU1gbHXNAbJ1_lHG1M6lwI,1338
|
239
|
-
junifer/utils/logging.py,sha256=
|
244
|
+
junifer/utils/logging.py,sha256=furcU3XIUpUvnpe4PEwzWWIWgmH4j2ZA4MQdvSGWjj0,9216
|
240
245
|
junifer/utils/tests/test_fs.py,sha256=WQS7cKlKEZ742CIuiOYYpueeAhY9PqlastfDVpVVtvE,923
|
241
246
|
junifer/utils/tests/test_helpers.py,sha256=k5qqfxK8dFyuewTJyR1Qn6-nFaYNuVr0ysc18bfPjyU,929
|
242
247
|
junifer/utils/tests/test_logging.py,sha256=l8oo-AiBV7H6_IzlsNcj__cLeZBUvgIGoaMszD9VaJg,7754
|
243
|
-
junifer-0.0.4.
|
244
|
-
junifer-0.0.4.
|
245
|
-
junifer-0.0.4.
|
246
|
-
junifer-0.0.4.
|
247
|
-
junifer-0.0.4.
|
248
|
-
junifer-0.0.4.
|
249
|
-
junifer-0.0.4.
|
248
|
+
junifer-0.0.4.dev691.dist-info/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
|
249
|
+
junifer-0.0.4.dev691.dist-info/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
|
250
|
+
junifer-0.0.4.dev691.dist-info/METADATA,sha256=7usklSXm5dzCWLbRWRjXlHGaFEMdDuUjstrEtyOxwj8,8235
|
251
|
+
junifer-0.0.4.dev691.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
252
|
+
junifer-0.0.4.dev691.dist-info/entry_points.txt,sha256=DxFvKq0pOqRunAK0FxwJcoDfV1-dZvsFDpD5HRqSDhw,48
|
253
|
+
junifer-0.0.4.dev691.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
|
254
|
+
junifer-0.0.4.dev691.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|