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.

@@ -5,7 +5,7 @@ from humanize import naturalsize
5
5
  import logging
6
6
  from pathlib import Path
7
7
  import sys
8
- from datamint.utils.dicom_utils import is_dicom
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
- ) -> Optional[list[dict]]:
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
- if segmentation_root_path is None:
167
- return None
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
- if segmentation_metainfo is not None:
234
- snames_associated = []
235
- for segfile in seg_files:
236
- if segnames is None:
237
- snames_associated.append(classnames)
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
- for segname in segnames:
240
- if segname in str(segfile):
241
- if classnames is not None:
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
- ' The file may contain two keys: "segmentation_names" and "class_names".')
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
- with open(args.segmentation_names, 'r') as f:
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
- segmentation_files = _find_segmentation_files(args.segmentation_path,
456
- file_path,
457
- segmentation_metainfo=segmentation_names)
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
 
@@ -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 datamint.utils.dicom_utils import is_dicom
17
+ from medimgkit.dicom_utils import is_dicom
18
18
  import cv2
19
- from datamint.utils.io_utils import read_array_normalized
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.5.5
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=N8WFk-oO84fBKH9t-R1DW5J7hnxQxcz-zxgLuMkNbwA,47766
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=otCIesoqGBlbSOw4ErqFsXp2HwJsPNUQlkynQh_7pHg,7110
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=OIGq6aHX64B94MmAikcFzF0rdekRH4l1S59x2Pa_DJA,51739
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=VyLL2FgY9ibfbdp4K6HrKt0jgkQH-SVuU71D6e77074,26436
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=MQZ_wNFex4BKBfb4fAcXV6-fQXFV_zBK1ybWrMm6_pg,39092
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.5.5.dist-info/METADATA,sha256=o6BFPA7OS3SSPqflC85pJ_2Q7pETUtoZInY97B2Dxm8,4065
27
- datamint-1.5.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
28
- datamint-1.5.5.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
29
- datamint-1.5.5.dist-info/RECORD,,
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,,