datamint 1.7.3__tar.gz → 1.7.4__tar.gz
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-1.7.3 → datamint-1.7.4}/PKG-INFO +2 -2
- {datamint-1.7.3 → datamint-1.7.4}/datamint/apihandler/base_api_handler.py +11 -10
- {datamint-1.7.3 → datamint-1.7.4}/datamint/apihandler/root_api_handler.py +30 -29
- {datamint-1.7.3 → datamint-1.7.4}/datamint/dataset/base_dataset.py +8 -5
- {datamint-1.7.3 → datamint-1.7.4}/pyproject.toml +2 -2
- {datamint-1.7.3 → datamint-1.7.4}/README.md +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/__init__.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/apihandler/annotation_api_handler.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/apihandler/api_handler.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/apihandler/dto/annotation_dto.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/apihandler/exp_api_handler.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/client_cmd_tools/__init__.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/client_cmd_tools/datamint_config.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/client_cmd_tools/datamint_upload.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/configs.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/dataset/__init__.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/dataset/annotation.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/dataset/dataset.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/examples/__init__.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/examples/example_projects.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/experiment/__init__.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/experiment/_patcher.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/experiment/experiment.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/logging.yaml +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/utils/logging_utils.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/utils/torchmetrics.py +0 -0
- {datamint-1.7.3 → datamint-1.7.4}/datamint/utils/visualization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: datamint
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.4
|
|
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,7 +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
|
+
Requires-Dist: medimgkit (>=0.2.0)
|
|
23
23
|
Requires-Dist: nest-asyncio (>=1.0.0,<2.0.0)
|
|
24
24
|
Requires-Dist: nibabel (>=4.0.0)
|
|
25
25
|
Requires-Dist: numpy
|
|
@@ -138,7 +138,7 @@ class BaseAPIHandler:
|
|
|
138
138
|
if isinstance(data, aiohttp.FormData): # Check if it's aiohttp.FormData
|
|
139
139
|
# Handle FormData by extracting fields
|
|
140
140
|
form_parts = []
|
|
141
|
-
for options,headers,value in data._fields:
|
|
141
|
+
for options, headers, value in data._fields:
|
|
142
142
|
# get the name from options
|
|
143
143
|
name = options.get('name', 'file')
|
|
144
144
|
if hasattr(value, 'read'): # File-like object
|
|
@@ -161,7 +161,7 @@ class BaseAPIHandler:
|
|
|
161
161
|
if session is None:
|
|
162
162
|
async with aiohttp.ClientSession() as s:
|
|
163
163
|
return await self._run_request_async(request_args, s, data_to_get)
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
async with self.semaphore:
|
|
166
166
|
try:
|
|
167
167
|
_LOGGER.debug(f"Running request to {request_args['url']}")
|
|
@@ -222,7 +222,7 @@ class BaseAPIHandler:
|
|
|
222
222
|
|
|
223
223
|
def _run_request(self,
|
|
224
224
|
request_args: dict,
|
|
225
|
-
session: Session = None):
|
|
225
|
+
session: Session | None = None):
|
|
226
226
|
if session is None:
|
|
227
227
|
with Session() as s:
|
|
228
228
|
return self._run_request(request_args, s)
|
|
@@ -281,24 +281,25 @@ class BaseAPIHandler:
|
|
|
281
281
|
@staticmethod
|
|
282
282
|
def convert_format(bytes_array: bytes,
|
|
283
283
|
mimetype: str,
|
|
284
|
-
file_path: str = None
|
|
284
|
+
file_path: str | None = None
|
|
285
285
|
) -> pydicom.dataset.Dataset | Image.Image | cv2.VideoCapture | bytes | nib_FileBasedImage:
|
|
286
|
+
""" Convert the bytes array to the appropriate format based on the mimetype."""
|
|
286
287
|
content_io = BytesIO(bytes_array)
|
|
287
|
-
if mimetype
|
|
288
|
+
if mimetype.endswith('/dicom'):
|
|
288
289
|
return pydicom.dcmread(content_io)
|
|
289
|
-
elif mimetype
|
|
290
|
+
elif mimetype.startswith('image/'):
|
|
290
291
|
return Image.open(content_io)
|
|
291
|
-
elif mimetype
|
|
292
|
+
elif mimetype.startswith('video/'):
|
|
292
293
|
if file_path is None:
|
|
293
|
-
raise NotImplementedError("file_path=None is not implemented yet for video
|
|
294
|
+
raise NotImplementedError("file_path=None is not implemented yet for video/* mimetypes.")
|
|
294
295
|
return cv2.VideoCapture(file_path)
|
|
295
296
|
elif mimetype == 'application/json':
|
|
296
297
|
return json.loads(bytes_array)
|
|
297
298
|
elif mimetype == 'application/octet-stream':
|
|
298
299
|
return bytes_array
|
|
299
|
-
elif mimetype
|
|
300
|
+
elif mimetype.endswith('nifti'):
|
|
300
301
|
if file_path is None:
|
|
301
|
-
raise NotImplementedError("file_path=None is not implemented yet for
|
|
302
|
+
raise NotImplementedError(f"file_path=None is not implemented yet for {mimetype}.")
|
|
302
303
|
return nib.load(file_path)
|
|
303
304
|
|
|
304
305
|
raise ValueError(f"Unsupported mimetype: {mimetype}")
|
|
@@ -8,10 +8,11 @@ import asyncio
|
|
|
8
8
|
import aiohttp
|
|
9
9
|
from medimgkit.dicom_utils import anonymize_dicom, to_bytesio, is_dicom
|
|
10
10
|
from medimgkit import dicom_utils
|
|
11
|
+
from medimgkit.io_utils import is_io_object
|
|
12
|
+
from medimgkit.format_detection import guess_type, guess_extension
|
|
11
13
|
import pydicom
|
|
12
14
|
from pathlib import Path
|
|
13
15
|
from datetime import date
|
|
14
|
-
import mimetypes
|
|
15
16
|
from PIL import Image
|
|
16
17
|
import cv2
|
|
17
18
|
from nibabel.filebasedimages import FileBasedImage as nib_FileBasedImage
|
|
@@ -26,13 +27,6 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
26
27
|
_USER_LOGGER = logging.getLogger('user_logger')
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
def _is_io_object(obj):
|
|
30
|
-
"""
|
|
31
|
-
Check if an object is a file-like object.
|
|
32
|
-
"""
|
|
33
|
-
return callable(getattr(obj, "read", None))
|
|
34
|
-
|
|
35
|
-
|
|
36
30
|
def _infinite_gen(x):
|
|
37
31
|
while True:
|
|
38
32
|
yield x
|
|
@@ -65,7 +59,7 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
65
59
|
publish: bool = False,
|
|
66
60
|
metadata_file: Optional[str | dict] = None,
|
|
67
61
|
) -> str:
|
|
68
|
-
if
|
|
62
|
+
if is_io_object(file_path):
|
|
69
63
|
name = file_path.name
|
|
70
64
|
else:
|
|
71
65
|
name = file_path
|
|
@@ -91,15 +85,12 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
91
85
|
name = new_file_path
|
|
92
86
|
_LOGGER.debug(f"New file path: {name}")
|
|
93
87
|
|
|
94
|
-
if mimetype is None:
|
|
95
|
-
mimetype = mimetypes.guess_type(name)[0]
|
|
96
88
|
is_a_dicom_file = None
|
|
97
89
|
if mimetype is None:
|
|
98
|
-
|
|
99
|
-
if
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
mimetype = 'application/x-nifti'
|
|
90
|
+
mimetype, _ = guess_type(file_path, use_magic=True)
|
|
91
|
+
if mimetype == 'application/gzip' and name.lower().endswith('nii.gz'):
|
|
92
|
+
# Special case for gzipped NIfTI files
|
|
93
|
+
mimetype = 'image/x.nifti'
|
|
103
94
|
|
|
104
95
|
filename = os.path.basename(name)
|
|
105
96
|
_LOGGER.debug(f"File name '{filename}' mimetype: {mimetype}")
|
|
@@ -451,7 +442,7 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
451
442
|
|
|
452
443
|
files_path, is_multiple_resources = RootAPIHandler.__process_files_parameter(files_path)
|
|
453
444
|
|
|
454
|
-
|
|
445
|
+
# Discard DICOM reports
|
|
455
446
|
if discard_dicom_reports:
|
|
456
447
|
files_path = [f for f in files_path if not RootAPIHandler._is_dicom_report(f)]
|
|
457
448
|
old_size = len(files_path)
|
|
@@ -678,7 +669,7 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
678
669
|
is_list = False
|
|
679
670
|
new_file_path = [file_path]
|
|
680
671
|
# Check if is an IO object
|
|
681
|
-
elif
|
|
672
|
+
elif is_io_object(file_path):
|
|
682
673
|
is_list = False
|
|
683
674
|
new_file_path = [file_path]
|
|
684
675
|
elif not hasattr(file_path, '__len__'):
|
|
@@ -728,8 +719,8 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
728
719
|
|
|
729
720
|
def get_resources(self,
|
|
730
721
|
status: Optional[ResourceStatus] = None,
|
|
731
|
-
from_date:
|
|
732
|
-
to_date:
|
|
722
|
+
from_date: date | str | None = None,
|
|
723
|
+
to_date: date | str | None = None,
|
|
733
724
|
tags: Optional[Sequence[str]] = None,
|
|
734
725
|
modality: Optional[str] = None,
|
|
735
726
|
mimetype: Optional[str] = None,
|
|
@@ -747,8 +738,8 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
747
738
|
|
|
748
739
|
Args:
|
|
749
740
|
status (ResourceStatus): The resource status. Possible values: 'inbox', 'published', 'archived' or None. If None, it will return all resources.
|
|
750
|
-
from_date (
|
|
751
|
-
to_date (
|
|
741
|
+
from_date (date | str | None): The start date.
|
|
742
|
+
to_date (date | str | None): The end date.
|
|
752
743
|
tags (Optional[list[str]]): The tags to filter the resources.
|
|
753
744
|
modality (Optional[str]): The modality of the resources.
|
|
754
745
|
mimetype (Optional[str]): The mimetype of the resources.
|
|
@@ -767,9 +758,15 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
767
758
|
"""
|
|
768
759
|
# Convert datetime objects to ISO format
|
|
769
760
|
if from_date:
|
|
770
|
-
from_date
|
|
761
|
+
if isinstance(from_date, str):
|
|
762
|
+
date.fromisoformat(from_date)
|
|
763
|
+
else:
|
|
764
|
+
from_date = from_date.isoformat()
|
|
771
765
|
if to_date:
|
|
772
|
-
to_date
|
|
766
|
+
if isinstance(to_date, str):
|
|
767
|
+
date.fromisoformat(to_date)
|
|
768
|
+
else:
|
|
769
|
+
to_date = to_date.isoformat()
|
|
773
770
|
|
|
774
771
|
# Prepare the payload
|
|
775
772
|
payload = {
|
|
@@ -953,6 +950,9 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
953
950
|
>>> api_handler.download_resource_file('resource_id', save_path='path/to/dicomfile.dcm')
|
|
954
951
|
saves the file in the specified path.
|
|
955
952
|
"""
|
|
953
|
+
if save_path is None and add_extension:
|
|
954
|
+
raise ValueError("If add_extension is True, save_path must be provided.")
|
|
955
|
+
|
|
956
956
|
url = f"{self._get_endpoint_url(RootAPIHandler.ENDPOINT_RESOURCES)}/{resource_id}/file"
|
|
957
957
|
request_params = {'method': 'GET',
|
|
958
958
|
'headers': {'accept': 'application/octet-stream'},
|
|
@@ -964,7 +964,8 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
964
964
|
mimetype = None
|
|
965
965
|
if auto_convert or add_extension:
|
|
966
966
|
resource_info = self.get_resources_by_ids(resource_id)
|
|
967
|
-
mimetype = resource_info
|
|
967
|
+
mimetype = resource_info.get('mimetype', guess_type(response.content)[0])
|
|
968
|
+
|
|
968
969
|
|
|
969
970
|
if auto_convert:
|
|
970
971
|
try:
|
|
@@ -985,15 +986,15 @@ class RootAPIHandler(BaseAPIHandler):
|
|
|
985
986
|
raise e
|
|
986
987
|
|
|
987
988
|
if save_path is not None:
|
|
988
|
-
if add_extension:
|
|
989
|
-
ext =
|
|
989
|
+
if add_extension and mimetype is not None:
|
|
990
|
+
ext = guess_extension(mimetype)
|
|
990
991
|
if ext is not None and not save_path.endswith(ext):
|
|
991
992
|
save_path += ext
|
|
992
993
|
with open(save_path, 'wb') as f:
|
|
993
994
|
f.write(response.content)
|
|
994
995
|
|
|
995
|
-
|
|
996
|
-
|
|
996
|
+
if add_extension:
|
|
997
|
+
return resource_file, save_path
|
|
997
998
|
return resource_file
|
|
998
999
|
|
|
999
1000
|
def download_resource_frame(self,
|
|
@@ -283,10 +283,12 @@ class DatamintBaseDataset:
|
|
|
283
283
|
"""Post-process data after loading metadata."""
|
|
284
284
|
self._check_integrity()
|
|
285
285
|
self._calculate_dataset_length()
|
|
286
|
-
self.
|
|
286
|
+
if self.return_frame_by_frame:
|
|
287
|
+
self._precompute_frame_data()
|
|
288
|
+
self.subset_indices = list(range(self.dataset_length))
|
|
287
289
|
self._setup_labels()
|
|
288
290
|
|
|
289
|
-
if self.discard_without_annotations
|
|
291
|
+
if self.discard_without_annotations:
|
|
290
292
|
self._filter_unannotated()
|
|
291
293
|
|
|
292
294
|
def _calculate_dataset_length(self) -> None:
|
|
@@ -301,9 +303,8 @@ class DatamintBaseDataset:
|
|
|
301
303
|
|
|
302
304
|
def _precompute_frame_data(self) -> None:
|
|
303
305
|
"""Precompute frame-related data for efficient indexing."""
|
|
304
|
-
|
|
305
|
-
self._cumulative_frames = np.cumsum([0] +
|
|
306
|
-
self.subset_indices = list(range(self.dataset_length))
|
|
306
|
+
num_frames_per_resource = self.__compute_num_frames_per_resource()
|
|
307
|
+
self._cumulative_frames = np.cumsum([0] + num_frames_per_resource)
|
|
307
308
|
|
|
308
309
|
def _setup_labels(self) -> None:
|
|
309
310
|
"""Setup label sets and mappings."""
|
|
@@ -989,9 +990,11 @@ class DatamintBaseDataset:
|
|
|
989
990
|
return Path(resource['file'])
|
|
990
991
|
else:
|
|
991
992
|
ext = guess_extension(resource['mimetype'], strict=False)
|
|
993
|
+
_LOGGER.debug(f"Guessed extension for resource {resource['id']}|{resource['mimetype']}: {ext}")
|
|
992
994
|
if ext is None:
|
|
993
995
|
_LOGGER.warning(f"Could not guess extension for resource {resource['id']}.")
|
|
994
996
|
ext = ''
|
|
997
|
+
raise Exception
|
|
995
998
|
return Path('images', f"{resource['id']}{ext}")
|
|
996
999
|
|
|
997
1000
|
def _get_annotation_file_path(self, annotation: dict | Annotation) -> Path | None:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "datamint"
|
|
3
3
|
description = "A library for interacting with the Datamint API, designed for efficient data management, processing and Deep Learning workflows."
|
|
4
|
-
version = "1.7.
|
|
4
|
+
version = "1.7.4"
|
|
5
5
|
dynamic = ["dependencies"]
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
readme = "README.md"
|
|
@@ -40,7 +40,7 @@ matplotlib = "*"
|
|
|
40
40
|
lightning = "*"
|
|
41
41
|
albumentations = ">=2.0.0"
|
|
42
42
|
lazy-loader = ">=0.3.0"
|
|
43
|
-
medimgkit = "
|
|
43
|
+
medimgkit = ">=0.2.0"
|
|
44
44
|
# For compatibility with the datamintapi package
|
|
45
45
|
datamintapi = "0.0.*"
|
|
46
46
|
# Extra dependencies for docs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|