dbdicom 0.2.5__py3-none-any.whl → 0.3.0__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 dbdicom might be problematic. Click here for more details.
- dbdicom/__init__.py +1 -28
- dbdicom/api.py +267 -0
- dbdicom/const.py +144 -0
- dbdicom/dataset.py +752 -0
- dbdicom/dbd.py +719 -0
- dbdicom/external/__pycache__/__init__.cpython-311.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-311.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-311.pyc +0 -0
- dbdicom/register.py +527 -0
- dbdicom/{ds/types → sop_classes}/ct_image.py +2 -16
- dbdicom/{ds/types → sop_classes}/enhanced_mr_image.py +153 -26
- dbdicom/{ds/types → sop_classes}/mr_image.py +185 -140
- dbdicom/sop_classes/parametric_map.py +307 -0
- dbdicom/sop_classes/secondary_capture.py +140 -0
- dbdicom/sop_classes/segmentation.py +311 -0
- dbdicom/{ds/types → sop_classes}/ultrasound_multiframe_image.py +1 -15
- dbdicom/{ds/types → sop_classes}/xray_angiographic_image.py +2 -17
- dbdicom/utils/arrays.py +36 -0
- dbdicom/utils/files.py +0 -20
- dbdicom/utils/image.py +10 -629
- dbdicom-0.3.0.dist-info/METADATA +28 -0
- dbdicom-0.3.0.dist-info/RECORD +53 -0
- {dbdicom-0.2.5.dist-info → dbdicom-0.3.0.dist-info}/WHEEL +1 -1
- dbdicom/create.py +0 -457
- dbdicom/dro.py +0 -174
- dbdicom/ds/__init__.py +0 -10
- dbdicom/ds/create.py +0 -63
- dbdicom/ds/dataset.py +0 -869
- dbdicom/ds/dictionaries.py +0 -620
- dbdicom/ds/types/parametric_map.py +0 -226
- dbdicom/extensions/__init__.py +0 -9
- dbdicom/extensions/dipy.py +0 -448
- dbdicom/extensions/elastix.py +0 -503
- dbdicom/extensions/matplotlib.py +0 -107
- dbdicom/extensions/numpy.py +0 -271
- dbdicom/extensions/scipy.py +0 -1512
- dbdicom/extensions/skimage.py +0 -1030
- dbdicom/extensions/sklearn.py +0 -243
- dbdicom/extensions/vreg.py +0 -1390
- dbdicom/manager.py +0 -2132
- dbdicom/message.py +0 -119
- dbdicom/pipelines.py +0 -66
- dbdicom/record.py +0 -1893
- dbdicom/types/database.py +0 -107
- dbdicom/types/instance.py +0 -231
- dbdicom/types/patient.py +0 -40
- dbdicom/types/series.py +0 -2874
- dbdicom/types/study.py +0 -58
- dbdicom-0.2.5.dist-info/METADATA +0 -71
- dbdicom-0.2.5.dist-info/RECORD +0 -66
- {dbdicom-0.2.5.dist-info → dbdicom-0.3.0.dist-info/licenses}/LICENSE +0 -0
- {dbdicom-0.2.5.dist-info → dbdicom-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pydicom
|
|
3
|
+
from pydicom.dataset import Dataset, FileDataset
|
|
4
|
+
from pydicom.sequence import Sequence
|
|
5
|
+
import datetime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def create_binary_segmentation_dicom(rows=128, cols=128):
|
|
9
|
+
# Create file metadata
|
|
10
|
+
meta = Dataset()
|
|
11
|
+
meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.66.4' # Segmentation Storage
|
|
12
|
+
meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()
|
|
13
|
+
meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian
|
|
14
|
+
meta.ImplementationClassUID = pydicom.uid.generate_uid()
|
|
15
|
+
|
|
16
|
+
# Create the FileDataset (in memory)
|
|
17
|
+
ds = FileDataset(None, {}, file_meta=meta, preamble=b"\0" * 128)
|
|
18
|
+
ds.is_little_endian = True
|
|
19
|
+
ds.is_implicit_VR = False
|
|
20
|
+
|
|
21
|
+
# Required general attributes
|
|
22
|
+
ds.SOPClassUID = meta.MediaStorageSOPClassUID
|
|
23
|
+
ds.SOPInstanceUID = meta.MediaStorageSOPInstanceUID
|
|
24
|
+
ds.Modality = 'SEG'
|
|
25
|
+
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
|
|
26
|
+
ds.StudyInstanceUID = pydicom.uid.generate_uid()
|
|
27
|
+
ds.FrameOfReferenceUID = pydicom.uid.generate_uid()
|
|
28
|
+
ds.PatientName = 'Seg^Test'
|
|
29
|
+
ds.PatientID = 'SEG001'
|
|
30
|
+
|
|
31
|
+
# Set content date/time
|
|
32
|
+
dt = datetime.datetime.now()
|
|
33
|
+
ds.ContentDate = dt.strftime('%Y%m%d')
|
|
34
|
+
ds.ContentTime = dt.strftime('%H%M%S.%f')[:13]
|
|
35
|
+
|
|
36
|
+
# Segmentation-specific
|
|
37
|
+
ds.SegmentationType = 'BINARY'
|
|
38
|
+
ds.ContentLabel = 'MASK'
|
|
39
|
+
ds.ContentDescription = 'Binary segmentation mask'
|
|
40
|
+
ds.ContentCreatorName = 'AutoGen'
|
|
41
|
+
|
|
42
|
+
ds.Rows = rows
|
|
43
|
+
ds.Columns = cols
|
|
44
|
+
ds.SamplesPerPixel = 1
|
|
45
|
+
ds.BitsAllocated = 1
|
|
46
|
+
ds.BitsStored = 1
|
|
47
|
+
ds.HighBit = 0
|
|
48
|
+
ds.PixelRepresentation = 0
|
|
49
|
+
ds.PhotometricInterpretation = 'MONOCHROME2'
|
|
50
|
+
ds.NumberOfFrames = 1
|
|
51
|
+
ds.BurnedInAnnotation = 'NO'
|
|
52
|
+
ds.ImageType = ['DERIVED', 'PRIMARY']
|
|
53
|
+
|
|
54
|
+
# Create a dummy mask (circle in center)
|
|
55
|
+
Y, X = np.ogrid[:rows, :cols]
|
|
56
|
+
mask = ((X - cols // 2)**2 + (Y - rows // 2)**2) < (min(rows, cols) // 4)**2
|
|
57
|
+
binary_frame = np.packbits(mask.astype(np.uint8).flatten())
|
|
58
|
+
|
|
59
|
+
# Assign Pixel Data
|
|
60
|
+
ds.PixelData = binary_frame.tobytes()
|
|
61
|
+
|
|
62
|
+
# SegmentSequence: define what the mask means
|
|
63
|
+
segment = Dataset()
|
|
64
|
+
segment.SegmentNumber = 1
|
|
65
|
+
segment.SegmentLabel = 'Kidney'
|
|
66
|
+
segment.SegmentAlgorithmType = 'MANUAL'
|
|
67
|
+
segment.SegmentAlgorithmName = 'ManualDraw'
|
|
68
|
+
|
|
69
|
+
segment.SegmentedPropertyCategoryCodeSequence = Sequence([Dataset()])
|
|
70
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodeValue = 'T-D0050'
|
|
71
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodingSchemeDesignator = 'SRT'
|
|
72
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodeMeaning = 'Tissue'
|
|
73
|
+
|
|
74
|
+
segment.SegmentedPropertyTypeCodeSequence = Sequence([Dataset()])
|
|
75
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodeValue = 'T-71000'
|
|
76
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodingSchemeDesignator = 'SRT'
|
|
77
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodeMeaning = 'Kidney'
|
|
78
|
+
|
|
79
|
+
ds.SegmentSequence = Sequence([segment])
|
|
80
|
+
|
|
81
|
+
# Functional groups (Plane Position)
|
|
82
|
+
fg = Dataset()
|
|
83
|
+
pp = Dataset()
|
|
84
|
+
pp.ImagePositionPatient = [0.0, 0.0, 0.0]
|
|
85
|
+
fg.PlanePositionSequence = Sequence([pp])
|
|
86
|
+
ds.PerFrameFunctionalGroupsSequence = Sequence([fg])
|
|
87
|
+
|
|
88
|
+
return ds
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def create_multi_segment_segmentation_dicom(masks_dict, spacing=(1.0, 1.0), origin=(0.0, 0.0, 0.0)):
|
|
94
|
+
"""
|
|
95
|
+
Create a multi-segment binary DICOM Segmentation object.
|
|
96
|
+
|
|
97
|
+
Parameters:
|
|
98
|
+
- masks_dict: dict of {label: binary 2D NumPy array}
|
|
99
|
+
- spacing: (row_spacing, col_spacing)
|
|
100
|
+
- origin: (x, y, z) ImagePositionPatient
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
- pydicom FileDataset object
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
labels = list(masks_dict.keys())
|
|
107
|
+
first_mask = next(iter(masks_dict.values()))
|
|
108
|
+
rows, cols = first_mask.shape
|
|
109
|
+
|
|
110
|
+
# Create metadata
|
|
111
|
+
meta = Dataset()
|
|
112
|
+
meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.66.4'
|
|
113
|
+
meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()
|
|
114
|
+
meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian
|
|
115
|
+
meta.ImplementationClassUID = pydicom.uid.generate_uid()
|
|
116
|
+
|
|
117
|
+
ds = FileDataset(None, {}, file_meta=meta, preamble=b"\0" * 128)
|
|
118
|
+
ds.is_little_endian = True
|
|
119
|
+
ds.is_implicit_VR = False
|
|
120
|
+
|
|
121
|
+
# Required general attributes
|
|
122
|
+
ds.SOPClassUID = meta.MediaStorageSOPClassUID
|
|
123
|
+
ds.SOPInstanceUID = meta.MediaStorageSOPInstanceUID
|
|
124
|
+
ds.Modality = 'SEG'
|
|
125
|
+
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
|
|
126
|
+
ds.StudyInstanceUID = pydicom.uid.generate_uid()
|
|
127
|
+
ds.FrameOfReferenceUID = pydicom.uid.generate_uid()
|
|
128
|
+
ds.PatientName = 'Seg^Multi'
|
|
129
|
+
ds.PatientID = 'MULTISEG001'
|
|
130
|
+
ds.ContentDate = datetime.datetime.now().strftime('%Y%m%d')
|
|
131
|
+
ds.ContentTime = datetime.datetime.now().strftime('%H%M%S.%f')[:13]
|
|
132
|
+
|
|
133
|
+
ds.Rows = rows
|
|
134
|
+
ds.Columns = cols
|
|
135
|
+
ds.SamplesPerPixel = 1
|
|
136
|
+
ds.BitsAllocated = 1
|
|
137
|
+
ds.BitsStored = 1
|
|
138
|
+
ds.HighBit = 0
|
|
139
|
+
ds.PixelRepresentation = 0
|
|
140
|
+
ds.PhotometricInterpretation = 'MONOCHROME2'
|
|
141
|
+
ds.SegmentationType = 'BINARY'
|
|
142
|
+
ds.BurnedInAnnotation = 'NO'
|
|
143
|
+
ds.ImageType = ['DERIVED', 'PRIMARY']
|
|
144
|
+
ds.ContentLabel = 'MULTI_SEG'
|
|
145
|
+
ds.ContentCreatorName = 'AutoGen'
|
|
146
|
+
|
|
147
|
+
# SegmentSequence
|
|
148
|
+
segment_sequence = []
|
|
149
|
+
pixel_data_bytes = b''
|
|
150
|
+
fg_sequence = []
|
|
151
|
+
|
|
152
|
+
for i, label in enumerate(labels):
|
|
153
|
+
segment = Dataset()
|
|
154
|
+
segment.SegmentNumber = i + 1
|
|
155
|
+
segment.SegmentLabel = label
|
|
156
|
+
segment.SegmentAlgorithmType = 'MANUAL'
|
|
157
|
+
segment.SegmentAlgorithmName = 'ManualDraw'
|
|
158
|
+
|
|
159
|
+
# Use generic SRT codes for tissue/organ
|
|
160
|
+
segment.SegmentedPropertyCategoryCodeSequence = Sequence([Dataset()])
|
|
161
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodeValue = 'T-D0050'
|
|
162
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodingSchemeDesignator = 'SRT'
|
|
163
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodeMeaning = 'Tissue'
|
|
164
|
+
|
|
165
|
+
segment.SegmentedPropertyTypeCodeSequence = Sequence([Dataset()])
|
|
166
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodeValue = 'T-00000'
|
|
167
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodingSchemeDesignator = '99LOCAL'
|
|
168
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodeMeaning = label
|
|
169
|
+
|
|
170
|
+
segment_sequence.append(segment)
|
|
171
|
+
|
|
172
|
+
# Mask -> 1-bit packed frame
|
|
173
|
+
mask = masks_dict[label]
|
|
174
|
+
assert mask.shape == (rows, cols), f"Shape mismatch for label '{label}'"
|
|
175
|
+
packed = np.packbits(mask.astype(np.uint8).flatten())
|
|
176
|
+
pixel_data_bytes += packed.tobytes()
|
|
177
|
+
|
|
178
|
+
# Per-frame functional group (Plane Position and Segment Identification)
|
|
179
|
+
fg = Dataset()
|
|
180
|
+
|
|
181
|
+
# Plane Position
|
|
182
|
+
pp = Dataset()
|
|
183
|
+
pp.ImagePositionPatient = [float(origin[0]), float(origin[1]), float(origin[2] + i)]
|
|
184
|
+
fg.PlanePositionSequence = Sequence([pp])
|
|
185
|
+
|
|
186
|
+
# Segment Identification
|
|
187
|
+
si = Dataset()
|
|
188
|
+
si.ReferencedSegmentNumber = i + 1
|
|
189
|
+
fg.SegmentIdentificationSequence = Sequence([si])
|
|
190
|
+
|
|
191
|
+
fg_sequence.append(fg)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def create_multiframe_segmentation(masks_dict, pixel_spacing=(1.0, 1.0), slice_thickness=1.0, origin=(0.0, 0.0, 0.0)):
|
|
196
|
+
"""
|
|
197
|
+
Create a DICOM Segmentation object with multiple frames per segment (e.g., 3D masks).
|
|
198
|
+
|
|
199
|
+
Parameters:
|
|
200
|
+
masks_dict: dict {label: 3D numpy array (Z, Y, X)}
|
|
201
|
+
pixel_spacing: tuple of (row_spacing, col_spacing)
|
|
202
|
+
slice_thickness: float
|
|
203
|
+
origin: tuple of (x, y, z)
|
|
204
|
+
"""
|
|
205
|
+
labels = list(masks_dict.keys())
|
|
206
|
+
first_volume = next(iter(masks_dict.values()))
|
|
207
|
+
num_slices, rows, cols = first_volume.shape
|
|
208
|
+
|
|
209
|
+
meta = Dataset()
|
|
210
|
+
meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.66.4'
|
|
211
|
+
meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()
|
|
212
|
+
meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian
|
|
213
|
+
meta.ImplementationClassUID = pydicom.uid.generate_uid()
|
|
214
|
+
|
|
215
|
+
ds = FileDataset(None, {}, file_meta=meta, preamble=b"\0" * 128)
|
|
216
|
+
ds.is_little_endian = True
|
|
217
|
+
ds.is_implicit_VR = False
|
|
218
|
+
|
|
219
|
+
ds.SOPClassUID = meta.MediaStorageSOPClassUID
|
|
220
|
+
ds.SOPInstanceUID = meta.MediaStorageSOPInstanceUID
|
|
221
|
+
ds.Modality = 'SEG'
|
|
222
|
+
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
|
|
223
|
+
ds.StudyInstanceUID = pydicom.uid.generate_uid()
|
|
224
|
+
ds.FrameOfReferenceUID = pydicom.uid.generate_uid()
|
|
225
|
+
ds.PatientName = 'Seg^3D'
|
|
226
|
+
ds.PatientID = 'SEG3D001'
|
|
227
|
+
dt = datetime.datetime.now()
|
|
228
|
+
ds.ContentDate = dt.strftime('%Y%m%d')
|
|
229
|
+
ds.ContentTime = dt.strftime('%H%M%S.%f')[:13]
|
|
230
|
+
|
|
231
|
+
ds.Rows = rows
|
|
232
|
+
ds.Columns = cols
|
|
233
|
+
ds.SamplesPerPixel = 1
|
|
234
|
+
ds.BitsAllocated = 1
|
|
235
|
+
ds.BitsStored = 1
|
|
236
|
+
ds.HighBit = 0
|
|
237
|
+
ds.PixelRepresentation = 0
|
|
238
|
+
ds.PhotometricInterpretation = 'MONOCHROME2'
|
|
239
|
+
ds.SegmentationType = 'BINARY'
|
|
240
|
+
ds.BurnedInAnnotation = 'NO'
|
|
241
|
+
ds.ImageType = ['DERIVED', 'PRIMARY']
|
|
242
|
+
ds.ContentLabel = 'MULTIFRAME_SEG'
|
|
243
|
+
ds.ContentCreatorName = 'AutoGen'
|
|
244
|
+
|
|
245
|
+
ds.NumberOfFrames = num_slices * len(labels)
|
|
246
|
+
|
|
247
|
+
pixel_data_bytes = b''
|
|
248
|
+
segment_sequence = []
|
|
249
|
+
per_frame_sequence = []
|
|
250
|
+
|
|
251
|
+
for seg_index, label in enumerate(labels):
|
|
252
|
+
vol = masks_dict[label]
|
|
253
|
+
assert vol.shape == (num_slices, rows, cols)
|
|
254
|
+
|
|
255
|
+
segment = Dataset()
|
|
256
|
+
segment.SegmentNumber = seg_index + 1
|
|
257
|
+
segment.SegmentLabel = label
|
|
258
|
+
segment.SegmentAlgorithmType = 'MANUAL'
|
|
259
|
+
segment.SegmentAlgorithmName = 'ManualDraw'
|
|
260
|
+
|
|
261
|
+
segment.SegmentedPropertyCategoryCodeSequence = Sequence([Dataset()])
|
|
262
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodeValue = 'T-D0050'
|
|
263
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodingSchemeDesignator = 'SRT'
|
|
264
|
+
segment.SegmentedPropertyCategoryCodeSequence[0].CodeMeaning = 'Tissue'
|
|
265
|
+
|
|
266
|
+
segment.SegmentedPropertyTypeCodeSequence = Sequence([Dataset()])
|
|
267
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodeValue = 'T-00000'
|
|
268
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodingSchemeDesignator = '99LOCAL'
|
|
269
|
+
segment.SegmentedPropertyTypeCodeSequence[0].CodeMeaning = label
|
|
270
|
+
|
|
271
|
+
segment_sequence.append(segment)
|
|
272
|
+
|
|
273
|
+
for z in range(num_slices):
|
|
274
|
+
# Pack each slice (frame)
|
|
275
|
+
frame = np.packbits(vol[z].astype(np.uint8).flatten())
|
|
276
|
+
pixel_data_bytes += frame.tobytes()
|
|
277
|
+
|
|
278
|
+
# Functional Group for this frame
|
|
279
|
+
fg = Dataset()
|
|
280
|
+
|
|
281
|
+
# Position
|
|
282
|
+
pos = list(origin)
|
|
283
|
+
pos[2] += z * slice_thickness
|
|
284
|
+
plane = Dataset()
|
|
285
|
+
plane.ImagePositionPatient = [str(v) for v in pos]
|
|
286
|
+
fg.PlanePositionSequence = Sequence([plane])
|
|
287
|
+
|
|
288
|
+
# Segment reference
|
|
289
|
+
seg_id = Dataset()
|
|
290
|
+
seg_id.ReferencedSegmentNumber = seg_index + 1
|
|
291
|
+
fg.SegmentIdentificationSequence = Sequence([seg_id])
|
|
292
|
+
|
|
293
|
+
per_frame_sequence.append(fg)
|
|
294
|
+
|
|
295
|
+
ds.SegmentSequence = Sequence(segment_sequence)
|
|
296
|
+
ds.PixelData = pixel_data_bytes
|
|
297
|
+
ds.PerFrameFunctionalGroupsSequence = Sequence(per_frame_sequence)
|
|
298
|
+
|
|
299
|
+
# Shared functional groups
|
|
300
|
+
shared = Dataset()
|
|
301
|
+
geom = Dataset()
|
|
302
|
+
geom.PixelSpacing = [str(pixel_spacing[0]), str(pixel_spacing[1])]
|
|
303
|
+
geom.SliceThickness = str(slice_thickness)
|
|
304
|
+
geom.ImageOrientationPatient = ['1', '0', '0', '0', '1', '0']
|
|
305
|
+
shared.PixelMeasuresSequence = Sequence([geom])
|
|
306
|
+
ds.SharedFunctionalGroupsSequence = Sequence([shared])
|
|
307
|
+
|
|
308
|
+
return ds
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
@@ -3,23 +3,9 @@
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
5
|
import pydicom
|
|
6
|
-
from pydicom.dataset import
|
|
6
|
+
from pydicom.dataset import FileMetaDataset
|
|
7
7
|
from pydicom.sequence import Sequence
|
|
8
8
|
|
|
9
|
-
from dbdicom.ds.dataset import DbDataset
|
|
10
|
-
|
|
11
|
-
class UltrasoundMultiFrameImage(DbDataset):
|
|
12
|
-
def __init__(self, dataset=None, template=None):
|
|
13
|
-
super().__init__()
|
|
14
|
-
|
|
15
|
-
if (dataset is None) and (template is None):
|
|
16
|
-
template = 'DEFAULT'
|
|
17
|
-
|
|
18
|
-
if dataset is not None:
|
|
19
|
-
self.__dict__ = dataset.__dict__
|
|
20
|
-
|
|
21
|
-
if template == 'DEFAULT':
|
|
22
|
-
default(self)
|
|
23
9
|
|
|
24
10
|
def default(ds):
|
|
25
11
|
|
|
@@ -2,25 +2,10 @@
|
|
|
2
2
|
# Produced by pydicom codify utility script
|
|
3
3
|
import numpy as np
|
|
4
4
|
import pydicom
|
|
5
|
-
from pydicom.dataset import
|
|
5
|
+
from pydicom.dataset import FileMetaDataset
|
|
6
6
|
from pydicom.sequence import Sequence
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class XrayAngiographicImage(DbDataset):
|
|
11
|
-
def __init__(self, dataset=None, template=None):
|
|
12
|
-
super().__init__()
|
|
13
|
-
|
|
14
|
-
if (dataset is None) and (template is None):
|
|
15
|
-
template = 'ANGIO'
|
|
16
|
-
|
|
17
|
-
if dataset is not None:
|
|
18
|
-
self.__dict__ = dataset.__dict__
|
|
19
|
-
|
|
20
|
-
if template == 'ANGIO':
|
|
21
|
-
angio(self)
|
|
22
|
-
|
|
23
|
-
def angio(ds):
|
|
8
|
+
def default(ds):
|
|
24
9
|
|
|
25
10
|
# File meta info data elements
|
|
26
11
|
ds.file_meta = FileMetaDataset()
|
dbdicom/utils/arrays.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def meshvals(coords):
|
|
5
|
+
# Input array shape: (d, f) with d = nr of dims and f = nr of frames
|
|
6
|
+
# Output array shape: (d, f1,..., fd)
|
|
7
|
+
if coords.size == 0:
|
|
8
|
+
return np.array([])
|
|
9
|
+
# Sort by column
|
|
10
|
+
sorted_indices = np.lexsort(coords[::-1])
|
|
11
|
+
sorted_array = coords[:, sorted_indices]
|
|
12
|
+
# Find shape
|
|
13
|
+
shape = _mesh_shape(sorted_array)
|
|
14
|
+
# Reshape
|
|
15
|
+
mesh_array = sorted_array.reshape(shape)
|
|
16
|
+
return mesh_array, sorted_indices
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _mesh_shape(sorted_array):
|
|
20
|
+
|
|
21
|
+
nd = np.unique(sorted_array[0,:]).size
|
|
22
|
+
shape = (sorted_array.shape[0], nd)
|
|
23
|
+
|
|
24
|
+
for dim in range(1,shape[0]):
|
|
25
|
+
shape_dim = (shape[0], np.prod(shape[1:]), -1)
|
|
26
|
+
sorted_array = sorted_array.reshape(shape_dim)
|
|
27
|
+
nd = [np.unique(sorted_array[dim,d,:]).size for d in range(shape_dim[1])]
|
|
28
|
+
shape = shape + (max(nd),)
|
|
29
|
+
|
|
30
|
+
if np.prod(shape) != sorted_array.size:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
'These are not mesh coordinates.'
|
|
33
|
+
'Make sure to specify dimensions for a multidimensional series.'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return shape
|
dbdicom/utils/files.py
CHANGED
|
@@ -1,29 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import platform
|
|
3
3
|
import zipfile
|
|
4
|
-
import imageio
|
|
5
|
-
import numpy as np
|
|
6
4
|
|
|
7
|
-
from PIL import Image, ImageSequence
|
|
8
|
-
import numpy as np
|
|
9
5
|
|
|
10
6
|
|
|
11
|
-
def _load_gif_frames(image: Image, mode='RGBA'):
|
|
12
|
-
return np.array([
|
|
13
|
-
np.array(frame.convert(mode))
|
|
14
|
-
for frame in ImageSequence.Iterator(image)
|
|
15
|
-
])
|
|
16
|
-
|
|
17
|
-
def gif2numpy(file):
|
|
18
|
-
with Image.open(file) as im:
|
|
19
|
-
frames = _load_gif_frames(im)
|
|
20
|
-
# GIF files are in RGBA format but assume grescale for now
|
|
21
|
-
frames = frames[...,0]
|
|
22
|
-
# Transpose in-plane x-y
|
|
23
|
-
frames = np.transpose(frames, (0,2,1))
|
|
24
|
-
return frames
|
|
25
|
-
#return imageio.imread(file)
|
|
26
|
-
|
|
27
7
|
def all_files(path):
|
|
28
8
|
files = [item.path for item in scan_tree(path) if item.is_file()]
|
|
29
9
|
# Windows has maximum path length of 260 - ignore any files that are longer
|