dicube 0.2.2__cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.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.
- dicube/__init__.py +174 -0
- dicube/codecs/__init__.py +152 -0
- dicube/codecs/jph/__init__.py +15 -0
- dicube/codecs/jph/codec.py +161 -0
- dicube/codecs/jph/ojph_complete.cpython-38-aarch64-linux-gnu.so +0 -0
- dicube/codecs/jph/ojph_decode_complete.cpython-38-aarch64-linux-gnu.so +0 -0
- dicube/core/__init__.py +21 -0
- dicube/core/image.py +349 -0
- dicube/core/io.py +408 -0
- dicube/core/pixel_header.py +120 -0
- dicube/dicom/__init__.py +13 -0
- dicube/dicom/dcb_streaming.py +248 -0
- dicube/dicom/dicom_io.py +153 -0
- dicube/dicom/dicom_meta.py +740 -0
- dicube/dicom/dicom_status.py +259 -0
- dicube/dicom/dicom_tags.py +121 -0
- dicube/dicom/merge_utils.py +283 -0
- dicube/dicom/space_from_meta.py +70 -0
- dicube/exceptions.py +189 -0
- dicube/storage/__init__.py +17 -0
- dicube/storage/dcb_file.py +824 -0
- dicube/storage/pixel_utils.py +259 -0
- dicube/utils/__init__.py +6 -0
- dicube/validation.py +380 -0
- dicube-0.2.2.dist-info/METADATA +272 -0
- dicube-0.2.2.dist-info/RECORD +27 -0
- dicube-0.2.2.dist-info/WHEEL +6 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
from spacetransformer import Space
|
2
|
+
from .dicom_status import DicomStatus, get_dicom_status
|
3
|
+
from .dicom_tags import CommonTags
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
|
7
|
+
def get_space_from_DicomMeta(meta, axis_order="xyz"):
|
8
|
+
"""
|
9
|
+
Create a Space object from DICOM metadata.
|
10
|
+
|
11
|
+
Extracts geometric information from DICOM tags including:
|
12
|
+
- Image Position (Patient) for origin
|
13
|
+
- Pixel Spacing and Slice Thickness for spacing
|
14
|
+
- Image Orientation (Patient) for direction cosines
|
15
|
+
- Rows, Columns, and number of slices for shape
|
16
|
+
|
17
|
+
Args:
|
18
|
+
meta: DicomMeta object containing DICOM metadata
|
19
|
+
Must support meta[Tag] -> (value, status) interface
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
Space: A new Space instance with geometry matching the DICOM data
|
23
|
+
|
24
|
+
Raises:
|
25
|
+
ValueError: If required DICOM tags are missing or invalid
|
26
|
+
"""
|
27
|
+
|
28
|
+
num_images = meta.slice_count
|
29
|
+
status = get_dicom_status(meta)
|
30
|
+
if status not in (DicomStatus.CONSISTENT, DicomStatus.NON_UNIFORM_RESCALE_FACTOR):
|
31
|
+
return None
|
32
|
+
spacing = meta.get_shared_value(CommonTags.PixelSpacing)
|
33
|
+
spacing = [float(s) for s in spacing]
|
34
|
+
positions = np.array(
|
35
|
+
meta.get_values(CommonTags.ImagePositionPatient)
|
36
|
+
)
|
37
|
+
orientation = meta.get_shared_value(CommonTags.ImageOrientationPatient)
|
38
|
+
orientation = [float(s) for s in orientation]
|
39
|
+
origin = positions[0].tolist()
|
40
|
+
if num_images > 1:
|
41
|
+
diff = positions[-1] - positions[0]
|
42
|
+
z_orientation = diff / np.linalg.norm(diff).tolist()
|
43
|
+
z_step_vector = diff / (num_images - 1)
|
44
|
+
spacing.append(float(np.linalg.norm(z_step_vector)))
|
45
|
+
else:
|
46
|
+
thickness = meta.get_shared_value(CommonTags.SliceThickness)
|
47
|
+
if thickness is None:
|
48
|
+
thickness = 1
|
49
|
+
spacing.append(float(thickness))
|
50
|
+
z_orientation = np.cross(orientation[:3], orientation[3:6]).tolist()
|
51
|
+
shape = [
|
52
|
+
int(meta.get_shared_value(CommonTags.Columns)),
|
53
|
+
int(meta.get_shared_value(CommonTags.Rows)),
|
54
|
+
num_images,
|
55
|
+
]
|
56
|
+
space = Space(
|
57
|
+
origin=origin,
|
58
|
+
spacing=spacing,
|
59
|
+
x_orientation=orientation[:3],
|
60
|
+
y_orientation=orientation[3:6],
|
61
|
+
z_orientation=z_orientation,
|
62
|
+
shape=shape,
|
63
|
+
)
|
64
|
+
if axis_order == "xyz":
|
65
|
+
space = space
|
66
|
+
elif axis_order == "zyx":
|
67
|
+
space = space.reverse_axis_order()
|
68
|
+
else:
|
69
|
+
raise ValueError(f"Invalid axis order: {axis_order}")
|
70
|
+
return space
|
dicube/exceptions.py
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
"""Exceptions module for DiCube.
|
2
|
+
|
3
|
+
This module defines the exception hierarchy used throughout the DiCube library.
|
4
|
+
All exceptions inherit from the base DicomCubeError class to allow for
|
5
|
+
easy catching of all DiCube-related exceptions.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Optional, Dict, Any
|
9
|
+
|
10
|
+
|
11
|
+
class DicomCubeError(Exception):
|
12
|
+
"""Base exception class for all DicomCube-related errors.
|
13
|
+
|
14
|
+
All other exceptions in the DiCube library inherit from this class,
|
15
|
+
allowing applications to catch all DiCube-related exceptions with:
|
16
|
+
|
17
|
+
```python
|
18
|
+
try:
|
19
|
+
# DiCube operations
|
20
|
+
except DicomCubeError:
|
21
|
+
# Handle any DiCube error
|
22
|
+
```
|
23
|
+
|
24
|
+
This enhanced base class supports contextual error information and
|
25
|
+
helpful suggestions for resolving common issues.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
message: str,
|
31
|
+
context: Optional[str] = None,
|
32
|
+
suggestion: Optional[str] = None,
|
33
|
+
details: Optional[Dict[str, Any]] = None
|
34
|
+
):
|
35
|
+
"""Initialize a DicomCubeError with contextual information.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
message (str): The primary error message.
|
39
|
+
context (str, optional): Context about the operation that failed.
|
40
|
+
suggestion (str, optional): Helpful suggestion for resolving the error.
|
41
|
+
details (dict, optional): Additional structured error details.
|
42
|
+
"""
|
43
|
+
self.context = context
|
44
|
+
self.suggestion = suggestion
|
45
|
+
self.details = details or {}
|
46
|
+
|
47
|
+
formatted_message = self._format_message(message)
|
48
|
+
super().__init__(formatted_message)
|
49
|
+
|
50
|
+
def _format_message(self, message: str) -> str:
|
51
|
+
"""Format the error message with context and suggestions.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
message (str): The base error message.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
str: The formatted error message.
|
58
|
+
"""
|
59
|
+
parts = []
|
60
|
+
|
61
|
+
# Add context if provided
|
62
|
+
if self.context:
|
63
|
+
parts.append(f"{self.context}: {message}")
|
64
|
+
else:
|
65
|
+
parts.append(message)
|
66
|
+
|
67
|
+
# Add details if provided
|
68
|
+
if self.details:
|
69
|
+
detail_strs = [f"{k}={repr(v)}" for k, v in self.details.items()]
|
70
|
+
parts.append(f"Details: {', '.join(detail_strs)}")
|
71
|
+
|
72
|
+
# Add suggestion if provided
|
73
|
+
if self.suggestion:
|
74
|
+
parts.append(f"Suggestion: {self.suggestion}")
|
75
|
+
|
76
|
+
return "\n".join(parts)
|
77
|
+
|
78
|
+
|
79
|
+
class InvalidCubeFileError(DicomCubeError):
|
80
|
+
"""Raised when a file is not a valid DicomCube file.
|
81
|
+
|
82
|
+
This exception is raised when attempting to load a file that is not
|
83
|
+
in the expected DicomCube format. This could be due to incorrect
|
84
|
+
magic number, version mismatch, or a corrupted file structure.
|
85
|
+
"""
|
86
|
+
|
87
|
+
def __init__(
|
88
|
+
self,
|
89
|
+
message: str,
|
90
|
+
context: Optional[str] = None,
|
91
|
+
suggestion: Optional[str] = None,
|
92
|
+
details: Optional[Dict[str, Any]] = None
|
93
|
+
):
|
94
|
+
"""Initialize InvalidCubeFileError with file-specific suggestions.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
message (str): The primary error message.
|
98
|
+
context (str, optional): Context about the operation that failed.
|
99
|
+
suggestion (str, optional): Helpful suggestion for resolving the error.
|
100
|
+
details (dict, optional): Additional structured error details.
|
101
|
+
"""
|
102
|
+
if suggestion is None:
|
103
|
+
suggestion = "Verify the file is a valid DicomCube file and not corrupted"
|
104
|
+
|
105
|
+
super().__init__(message, context, suggestion, details)
|
106
|
+
|
107
|
+
|
108
|
+
class CodecError(DicomCubeError):
|
109
|
+
"""Raised when an error occurs in the encoding/decoding process.
|
110
|
+
|
111
|
+
This exception is raised when there are problems with image compression
|
112
|
+
or decompression, such as JPEG 2000 processing failures.
|
113
|
+
"""
|
114
|
+
|
115
|
+
def __init__(
|
116
|
+
self,
|
117
|
+
message: str,
|
118
|
+
context: Optional[str] = None,
|
119
|
+
suggestion: Optional[str] = None,
|
120
|
+
details: Optional[Dict[str, Any]] = None
|
121
|
+
):
|
122
|
+
"""Initialize CodecError with codec-specific suggestions.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
message (str): The primary error message.
|
126
|
+
context (str, optional): Context about the operation that failed.
|
127
|
+
suggestion (str, optional): Helpful suggestion for resolving the error.
|
128
|
+
details (dict, optional): Additional structured error details.
|
129
|
+
"""
|
130
|
+
if suggestion is None:
|
131
|
+
suggestion = "Check image data format and codec compatibility"
|
132
|
+
|
133
|
+
super().__init__(message, context, suggestion, details)
|
134
|
+
|
135
|
+
|
136
|
+
class MetaDataError(DicomCubeError):
|
137
|
+
"""Raised when metadata is missing or inconsistent.
|
138
|
+
|
139
|
+
This exception is raised when critical metadata (DicomMeta, Space, etc.)
|
140
|
+
is missing, corrupted, or inconsistent in a DicomCube file or operation.
|
141
|
+
"""
|
142
|
+
|
143
|
+
def __init__(
|
144
|
+
self,
|
145
|
+
message: str,
|
146
|
+
context: Optional[str] = None,
|
147
|
+
suggestion: Optional[str] = None,
|
148
|
+
details: Optional[Dict[str, Any]] = None
|
149
|
+
):
|
150
|
+
"""Initialize MetaDataError with metadata-specific suggestions.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
message (str): The primary error message.
|
154
|
+
context (str, optional): Context about the operation that failed.
|
155
|
+
suggestion (str, optional): Helpful suggestion for resolving the error.
|
156
|
+
details (dict, optional): Additional structured error details.
|
157
|
+
"""
|
158
|
+
if suggestion is None:
|
159
|
+
suggestion = "Verify metadata completeness and consistency in the source data"
|
160
|
+
|
161
|
+
super().__init__(message, context, suggestion, details)
|
162
|
+
|
163
|
+
|
164
|
+
class DataConsistencyError(DicomCubeError):
|
165
|
+
"""Raised when data arrays have consistency issues.
|
166
|
+
|
167
|
+
This exception is raised when image data arrays have mismatched
|
168
|
+
shapes, incompatible types, or other consistency-related issues.
|
169
|
+
"""
|
170
|
+
|
171
|
+
def __init__(
|
172
|
+
self,
|
173
|
+
message: str,
|
174
|
+
context: Optional[str] = None,
|
175
|
+
suggestion: Optional[str] = None,
|
176
|
+
details: Optional[Dict[str, Any]] = None
|
177
|
+
):
|
178
|
+
"""Initialize DataConsistencyError with data-specific suggestions.
|
179
|
+
|
180
|
+
Args:
|
181
|
+
message (str): The primary error message.
|
182
|
+
context (str, optional): Context about the operation that failed.
|
183
|
+
suggestion (str, optional): Helpful suggestion for resolving the error.
|
184
|
+
details (dict, optional): Additional structured error details.
|
185
|
+
"""
|
186
|
+
if suggestion is None:
|
187
|
+
suggestion = "Check data array shapes, types, and dimensional consistency"
|
188
|
+
|
189
|
+
super().__init__(message, context, suggestion, details)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"""Storage module for DiCube library.
|
2
|
+
|
3
|
+
This module provides implementations of DiCube binary file formats (.dcb)
|
4
|
+
and utilities for pixel data processing. It handles the storage and retrieval
|
5
|
+
of 3D medical images along with their metadata.
|
6
|
+
|
7
|
+
Classes:
|
8
|
+
DcbFile: Base class for DiCube file implementations.
|
9
|
+
DcbSFile: Speed-optimized implementation of DiCube file format.
|
10
|
+
"""
|
11
|
+
|
12
|
+
from .dcb_file import DcbFile, DcbSFile
|
13
|
+
|
14
|
+
__all__ = [
|
15
|
+
"DcbFile",
|
16
|
+
"DcbSFile",
|
17
|
+
]
|