datamint 1.5.5__py3-none-any.whl → 1.6.2__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.
Potentially problematic release.
This version of datamint might be problematic. Click here for more details.
- datamint/apihandler/annotation_api_handler.py +241 -153
- datamint/apihandler/dto/annotation_dto.py +1 -1
- datamint/apihandler/root_api_handler.py +4 -2
- datamint/client_cmd_tools/datamint_upload.py +101 -42
- datamint/dataset/base_dataset.py +2 -2
- {datamint-1.5.5.dist-info → datamint-1.6.2.dist-info}/METADATA +2 -1
- {datamint-1.5.5.dist-info → datamint-1.6.2.dist-info}/RECORD +9 -11
- datamint/utils/dicom_utils.py +0 -707
- datamint/utils/io_utils.py +0 -187
- {datamint-1.5.5.dist-info → datamint-1.6.2.dist-info}/WHEEL +0 -0
- {datamint-1.5.5.dist-info → datamint-1.6.2.dist-info}/entry_points.txt +0 -0
|
@@ -5,7 +5,7 @@ from humanize import naturalsize
|
|
|
5
5
|
import logging
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
import sys
|
|
8
|
-
from
|
|
8
|
+
from medimgkit.dicom_utils import is_dicom
|
|
9
9
|
import fnmatch
|
|
10
10
|
from typing import Generator, Optional, Any
|
|
11
11
|
from collections import defaultdict
|
|
@@ -15,6 +15,7 @@ from datamint.client_cmd_tools.datamint_config import ask_api_key
|
|
|
15
15
|
from datamint.utils.logging_utils import load_cmdline_logging_config
|
|
16
16
|
import yaml
|
|
17
17
|
from collections.abc import Iterable
|
|
18
|
+
import pandas as pd
|
|
18
19
|
|
|
19
20
|
# Create two loggings: one for the user and one for the developer
|
|
20
21
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -23,6 +24,38 @@ _USER_LOGGER = logging.getLogger('user_logger')
|
|
|
23
24
|
MAX_RECURSION_LIMIT = 1000
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
def _read_segmentation_names(segmentation_names_path: str | Path) -> dict:
|
|
28
|
+
"""
|
|
29
|
+
Read a segmentation names file (yaml or csv) and return its content as a dictionary.
|
|
30
|
+
If the file is a YAML file, it should contain two keys: "segmentation_names" and "class_names".
|
|
31
|
+
If the file is a CSV file, it should contain the following columns:
|
|
32
|
+
index, r, g, b, ..., name
|
|
33
|
+
"""
|
|
34
|
+
segmentation_names_path = Path(segmentation_names_path)
|
|
35
|
+
if segmentation_names_path.suffix in ['.yaml', '.yml']:
|
|
36
|
+
with open(segmentation_names_path, 'r') as f:
|
|
37
|
+
metadata = yaml.safe_load(f)
|
|
38
|
+
elif segmentation_names_path.suffix in ['.csv', '.tsv']:
|
|
39
|
+
df = pd.read_csv(segmentation_names_path,
|
|
40
|
+
header=None,
|
|
41
|
+
index_col=0,
|
|
42
|
+
sep=None, # use sep=None to automatically detect the separator
|
|
43
|
+
engine='python'
|
|
44
|
+
)
|
|
45
|
+
df = df.rename(columns={1: 'r', 2: 'g', 3: 'b', df.columns[-1]: 'name'})
|
|
46
|
+
# df = df.set_index(['r', 'g', 'b'])
|
|
47
|
+
metadata = {'class_names': df['name'].to_dict()}
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError(f"Unsupported file format: {segmentation_names_path.suffix}")
|
|
50
|
+
|
|
51
|
+
if 'segmentation_names' in metadata:
|
|
52
|
+
segnames = sorted(metadata['segmentation_names'],
|
|
53
|
+
key=lambda x: len(x))
|
|
54
|
+
metadata['segmentation_names'] = segnames
|
|
55
|
+
|
|
56
|
+
return metadata
|
|
57
|
+
|
|
58
|
+
|
|
26
59
|
def _is_valid_path_argparse(x):
|
|
27
60
|
"""
|
|
28
61
|
argparse type that checks if the path exists
|
|
@@ -101,7 +134,6 @@ def walk_to_depth(path: str | Path,
|
|
|
101
134
|
continue
|
|
102
135
|
yield from walk_to_depth(child, depth-1, exclude_pattern)
|
|
103
136
|
else:
|
|
104
|
-
_LOGGER.debug(f"yielding {child} from {path}")
|
|
105
137
|
yield child
|
|
106
138
|
|
|
107
139
|
|
|
@@ -157,31 +189,32 @@ def handle_api_key() -> str | None:
|
|
|
157
189
|
|
|
158
190
|
def _find_segmentation_files(segmentation_root_path: str,
|
|
159
191
|
images_files: list[str],
|
|
160
|
-
segmentation_metainfo: dict = None
|
|
161
|
-
) ->
|
|
192
|
+
segmentation_metainfo: dict | None = None
|
|
193
|
+
) -> list[dict]:
|
|
162
194
|
"""
|
|
163
195
|
Find the segmentation files that match the images files based on the same folder structure
|
|
164
196
|
"""
|
|
165
197
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if len(images_files) == 1 and os.path.isfile(images_files[0]) and os.path.isfile(segmentation_root_path):
|
|
170
|
-
return [{'files': [segmentation_root_path]}]
|
|
171
|
-
|
|
172
|
-
segmentation_files = []
|
|
173
|
-
acceptable_extensions = ['.nii.gz', '.nii', '.png']
|
|
174
|
-
|
|
198
|
+
segnames = None
|
|
199
|
+
classnames = None
|
|
175
200
|
if segmentation_metainfo is not None:
|
|
176
201
|
if 'segmentation_names' in segmentation_metainfo:
|
|
177
202
|
segnames = sorted(segmentation_metainfo['segmentation_names'],
|
|
178
203
|
key=lambda x: len(x))
|
|
179
|
-
else:
|
|
180
|
-
segnames = None
|
|
181
204
|
classnames = segmentation_metainfo.get('class_names', None)
|
|
182
205
|
if classnames is not None:
|
|
183
206
|
_LOGGER.debug(f"Number of class names: {len(classnames)}")
|
|
184
207
|
|
|
208
|
+
if len(images_files) == 1 and os.path.isfile(images_files[0]) and os.path.isfile(segmentation_root_path):
|
|
209
|
+
ret = [{'files': [segmentation_root_path]}]
|
|
210
|
+
if classnames is not None:
|
|
211
|
+
ret[0]['names'] = classnames
|
|
212
|
+
_LOGGER.debug(f"Returning segmentation files: {ret}")
|
|
213
|
+
return ret
|
|
214
|
+
|
|
215
|
+
segmentation_files = []
|
|
216
|
+
acceptable_extensions = ['.nii.gz', '.nii', '.png']
|
|
217
|
+
|
|
185
218
|
segmentation_root_path = Path(segmentation_root_path).absolute()
|
|
186
219
|
|
|
187
220
|
for imgpath in images_files:
|
|
@@ -197,7 +230,6 @@ def _find_segmentation_files(segmentation_root_path: str,
|
|
|
197
230
|
else:
|
|
198
231
|
common_parent = Path(*common_parent)
|
|
199
232
|
|
|
200
|
-
_LOGGER.debug(f"_find_segmentation_files::common_parent: {common_parent}")
|
|
201
233
|
path_structure = imgpath_parent.relative_to(common_parent).parts[1:]
|
|
202
234
|
|
|
203
235
|
# path_structure = imgpath_parent.relative_to(root_path).parts[1:]
|
|
@@ -230,24 +262,47 @@ def _find_segmentation_files(segmentation_root_path: str,
|
|
|
230
262
|
if len(frame_indices) > 0:
|
|
231
263
|
seginfo['frame_index'] = frame_indices
|
|
232
264
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
265
|
+
snames_associated = []
|
|
266
|
+
for segfile in seg_files:
|
|
267
|
+
# check if there is a metadata file associated, besides json, with the segmentation
|
|
268
|
+
for ext in ['.yaml', '.yml', '.csv']:
|
|
269
|
+
if str(segfile).endswith('nii.gz'):
|
|
270
|
+
# has two extensions, so we need to remove both
|
|
271
|
+
metadata_file = segfile.with_suffix('').with_suffix(ext)
|
|
272
|
+
if not metadata_file.exists():
|
|
273
|
+
metadata_file = segfile.with_suffix(ext)
|
|
274
|
+
else:
|
|
275
|
+
metadata_file = segfile.with_suffix(ext)
|
|
276
|
+
if metadata_file.exists():
|
|
277
|
+
_LOGGER.debug(f"Found metadata file: {metadata_file}")
|
|
278
|
+
try:
|
|
279
|
+
new_segmentation_metainfo = _read_segmentation_names(metadata_file)
|
|
280
|
+
cur_segnames = new_segmentation_metainfo.get('segmentation_names', segnames)
|
|
281
|
+
cur_classnames = new_segmentation_metainfo.get('class_names', classnames)
|
|
282
|
+
break
|
|
283
|
+
except Exception as e:
|
|
284
|
+
_LOGGER.warning(f"Error reading metadata file {metadata_file}: {e}")
|
|
285
|
+
else:
|
|
286
|
+
cur_segnames = segnames
|
|
287
|
+
cur_classnames = classnames
|
|
288
|
+
|
|
289
|
+
if cur_segnames is None:
|
|
290
|
+
_LOGGER.debug(f'adding {cur_classnames}')
|
|
291
|
+
snames_associated.append(cur_classnames)
|
|
292
|
+
else:
|
|
293
|
+
for segname in cur_segnames:
|
|
294
|
+
if segname in str(segfile):
|
|
295
|
+
if cur_classnames is not None:
|
|
296
|
+
new_segname = {cid: f'{segname}_{cname}' for cid, cname in cur_classnames.items()}
|
|
297
|
+
new_segname.update({'default': segname})
|
|
298
|
+
else:
|
|
299
|
+
new_segname = segname
|
|
300
|
+
snames_associated.append(new_segname)
|
|
301
|
+
break
|
|
238
302
|
else:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
new_segname = {cid: f'{segname}_{cname}' for cid, cname in classnames.items()}
|
|
243
|
-
new_segname.update({'default': segname})
|
|
244
|
-
else:
|
|
245
|
-
new_segname = segname
|
|
246
|
-
snames_associated.append(new_segname)
|
|
247
|
-
break
|
|
248
|
-
else:
|
|
249
|
-
_USER_LOGGER.warning(f"Segmentation file {segname} does not match any segmentation name.")
|
|
250
|
-
snames_associated.append(None)
|
|
303
|
+
_USER_LOGGER.warning(f"Segmentation file {segfile} does not match any segmentation name.")
|
|
304
|
+
snames_associated.append(None)
|
|
305
|
+
if len(snames_associated) > 0:
|
|
251
306
|
seginfo['names'] = snames_associated
|
|
252
307
|
|
|
253
308
|
segmentation_files.append(seginfo)
|
|
@@ -268,7 +323,7 @@ def _find_json_metadata(file_path: str | Path) -> Optional[str]:
|
|
|
268
323
|
Optional[str]: Path to the JSON metadata file if found, None otherwise
|
|
269
324
|
"""
|
|
270
325
|
file_path = Path(file_path)
|
|
271
|
-
|
|
326
|
+
|
|
272
327
|
# Handle .nii.gz files specially - need to remove both extensions
|
|
273
328
|
if file_path.name.endswith('.nii.gz'):
|
|
274
329
|
base_name = file_path.name[:-7] # Remove .nii.gz
|
|
@@ -320,7 +375,7 @@ def _collect_metadata_files(files_path: list[str], auto_detect_json: bool) -> tu
|
|
|
320
375
|
if used_json_files:
|
|
321
376
|
_LOGGER.debug(f"Filtering out {len(used_json_files)} JSON metadata files from main upload list")
|
|
322
377
|
filtered_metadata_files = []
|
|
323
|
-
|
|
378
|
+
|
|
324
379
|
for original_file in files_path:
|
|
325
380
|
if original_file not in used_json_files:
|
|
326
381
|
original_index = files_path.index(original_file)
|
|
@@ -376,8 +431,10 @@ def _parse_args() -> tuple[Any, list[str], Optional[list[dict]], Optional[list[s
|
|
|
376
431
|
help='Path to the segmentation file(s) or a directory')
|
|
377
432
|
parser.add_argument('--segmentation_names', type=_is_valid_path_argparse, metavar="FILE",
|
|
378
433
|
required=False,
|
|
379
|
-
help='Path to a yaml file containing the segmentation names.' +
|
|
380
|
-
'
|
|
434
|
+
help='Path to a yaml or csv file containing the segmentation names.' +
|
|
435
|
+
' If yaml, the file may contain two keys: "segmentation_names" and "class_names".'
|
|
436
|
+
' If csv, the file should contain the following columns:'
|
|
437
|
+
' index, r, g, b, ..., name')
|
|
381
438
|
parser.add_argument('--yes', action='store_true',
|
|
382
439
|
help='Automatically answer yes to all prompts')
|
|
383
440
|
parser.add_argument('--transpose-segmentation', action='store_true', default=False,
|
|
@@ -446,15 +503,17 @@ def _parse_args() -> tuple[Any, list[str], Optional[list[dict]], Optional[list[s
|
|
|
446
503
|
raise ValueError(f"No valid non-metadata files found in {args.path}")
|
|
447
504
|
|
|
448
505
|
if args.segmentation_names is not None:
|
|
449
|
-
|
|
450
|
-
segmentation_names = yaml.safe_load(f)
|
|
506
|
+
segmentation_names = _read_segmentation_names(args.segmentation_names)
|
|
451
507
|
else:
|
|
452
508
|
segmentation_names = None
|
|
453
509
|
|
|
454
510
|
_LOGGER.debug(f'finding segmentations at {args.segmentation_path}')
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
511
|
+
if args.segmentation_path is None:
|
|
512
|
+
segmentation_files = None
|
|
513
|
+
else:
|
|
514
|
+
segmentation_files = _find_segmentation_files(args.segmentation_path,
|
|
515
|
+
file_path,
|
|
516
|
+
segmentation_metainfo=segmentation_names)
|
|
458
517
|
|
|
459
518
|
_LOGGER.info(f"args parsed: {args}")
|
|
460
519
|
|
datamint/dataset/base_dataset.py
CHANGED
|
@@ -14,9 +14,9 @@ from torch.utils.data import DataLoader
|
|
|
14
14
|
import torch
|
|
15
15
|
from torch import Tensor
|
|
16
16
|
from datamint.apihandler.base_api_handler import DatamintException
|
|
17
|
-
from
|
|
17
|
+
from medimgkit.dicom_utils import is_dicom
|
|
18
18
|
import cv2
|
|
19
|
-
from
|
|
19
|
+
from medimgkit.io_utils import read_array_normalized
|
|
20
20
|
from datetime import datetime
|
|
21
21
|
|
|
22
22
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: datamint
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.2
|
|
4
4
|
Summary: A library for interacting with the Datamint API, designed for efficient data management, processing and Deep Learning workflows.
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -19,6 +19,7 @@ Requires-Dist: humanize (>=4.0.0,<5.0.0)
|
|
|
19
19
|
Requires-Dist: lazy-loader (>=0.3.0)
|
|
20
20
|
Requires-Dist: lightning
|
|
21
21
|
Requires-Dist: matplotlib
|
|
22
|
+
Requires-Dist: medimgkit
|
|
22
23
|
Requires-Dist: nest-asyncio (>=1.0.0,<2.0.0)
|
|
23
24
|
Requires-Dist: nibabel (>=4.0.0)
|
|
24
25
|
Requires-Dist: numpy
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
datamint/__init__.py,sha256=7rKCCsaa4RBRTIfuHB708rai1xwDHLtkFNFJGKYG5D4,757
|
|
2
|
-
datamint/apihandler/annotation_api_handler.py,sha256=
|
|
2
|
+
datamint/apihandler/annotation_api_handler.py,sha256=ChwaSYjoOAVS7vuyP3-cfpDHaHwk_wXLf8QQaSU_oSM,51893
|
|
3
3
|
datamint/apihandler/api_handler.py,sha256=cdVSddrFCKlF_BJ81LO1aJ0OP49rssjpNEFzJ6Q7YyY,384
|
|
4
4
|
datamint/apihandler/base_api_handler.py,sha256=XSxZEQEkbQpuixGDu_P9jbxUQht3Z3JgxaeiFKPkVDM,11690
|
|
5
|
-
datamint/apihandler/dto/annotation_dto.py,sha256=
|
|
5
|
+
datamint/apihandler/dto/annotation_dto.py,sha256=qId1RK1VO7dXrvGJ7dqJ31jBQB7Z8yy5x0tLSiMxTB4,7105
|
|
6
6
|
datamint/apihandler/exp_api_handler.py,sha256=hFUgUgBc5rL7odK7gTW3MnrvMY1pVfJUpUdzRNobMQE,6226
|
|
7
|
-
datamint/apihandler/root_api_handler.py,sha256=
|
|
7
|
+
datamint/apihandler/root_api_handler.py,sha256=O8Gn1Gp3w7AYeuT_FbwH413o6P_eAYLoRiW0baGY_b4,51795
|
|
8
8
|
datamint/client_cmd_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
datamint/client_cmd_tools/datamint_config.py,sha256=md7dnWrbl10lPtXKbmD9yo6onLJsajeG8Vz0ZWH1v4M,8181
|
|
10
|
-
datamint/client_cmd_tools/datamint_upload.py,sha256=
|
|
10
|
+
datamint/client_cmd_tools/datamint_upload.py,sha256=Q_C3HX9EFCdtIqmWY28qcWJmfUZfR1ml_pj0omnrkXw,29396
|
|
11
11
|
datamint/configs.py,sha256=Bdp6NydYwyCJ2dk19_gf_o3M2ZyQOmMHpLi8wEWNHUk,1426
|
|
12
12
|
datamint/dataset/__init__.py,sha256=4PlUKSvVhdfQvvuq8jQXrkdqnot-iTTizM3aM1vgSwg,47
|
|
13
|
-
datamint/dataset/base_dataset.py,sha256=
|
|
13
|
+
datamint/dataset/base_dataset.py,sha256=bSMuNHUzU7heN0awGemTn3e2zPLhuCsh-qSs_Qt6i9w,39082
|
|
14
14
|
datamint/dataset/dataset.py,sha256=AwS92t5kdmpm9NKFfXFmDmZxEbbPfb_FOMn-FWfu3bE,26590
|
|
15
15
|
datamint/examples/__init__.py,sha256=zcYnd5nLVme9GCTPYH-1JpGo8xXK2WEYvhzcy_2alZc,39
|
|
16
16
|
datamint/examples/example_projects.py,sha256=7Nb_EaIdzJTQa9zopqc-WhTBQWQJSoQZ_KjRS4PB4FI,2931
|
|
@@ -18,12 +18,10 @@ datamint/experiment/__init__.py,sha256=5qQOMzoG17DEd1YnTF-vS0qiM-DGdbNh42EUo91CR
|
|
|
18
18
|
datamint/experiment/_patcher.py,sha256=ZgbezoevAYhJsbiJTvWPALGTcUiMT371xddcTllt3H4,23296
|
|
19
19
|
datamint/experiment/experiment.py,sha256=aHK9dRFdQTi569xgUg1KqlCZLHZpDmSH3g3ndPIZvXw,44546
|
|
20
20
|
datamint/logging.yaml,sha256=a5dsATpul7QHeUHB2TjABFjWaPXBMbO--dgn8GlRqwk,483
|
|
21
|
-
datamint/utils/dicom_utils.py,sha256=sLukP6MB_acx7t868O2HDd_RDEILa97mEe_V9m1EMCY,28991
|
|
22
|
-
datamint/utils/io_utils.py,sha256=lKnUCJEip7W9Xj9wOWsTAA855HnKbjwQON1WjMGqJmM,7374
|
|
23
21
|
datamint/utils/logging_utils.py,sha256=DvoA35ATYG3JTwfXEXYawDyKRfHeCrH0a9czfkmz8kM,1851
|
|
24
22
|
datamint/utils/torchmetrics.py,sha256=lwU0nOtsSWfebyp7dvjlAggaqXtj5ohSEUXOg3L0hJE,2837
|
|
25
23
|
datamint/utils/visualization.py,sha256=yaUVAOHar59VrGUjpAWv5eVvQSfztFG0eP9p5Vt3l-M,4470
|
|
26
|
-
datamint-1.
|
|
27
|
-
datamint-1.
|
|
28
|
-
datamint-1.
|
|
29
|
-
datamint-1.
|
|
24
|
+
datamint-1.6.2.dist-info/METADATA,sha256=Raq2vLoDKmDH63F1DF6IqwIdk5dJDNr6TIvfbYpWUz4,4090
|
|
25
|
+
datamint-1.6.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
26
|
+
datamint-1.6.2.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
|
|
27
|
+
datamint-1.6.2.dist-info/RECORD,,
|