dbdicom 0.2.6__py3-none-any.whl → 0.3.1__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 +287 -0
- dbdicom/const.py +144 -0
- dbdicom/dataset.py +721 -0
- dbdicom/dbd.py +736 -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 +310 -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.1.dist-info/METADATA +28 -0
- dbdicom-0.3.1.dist-info/RECORD +53 -0
- 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.6.dist-info/METADATA +0 -72
- dbdicom-0.2.6.dist-info/RECORD +0 -66
- {dbdicom-0.2.6.dist-info → dbdicom-0.3.1.dist-info}/WHEEL +0 -0
- {dbdicom-0.2.6.dist-info → dbdicom-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {dbdicom-0.2.6.dist-info → dbdicom-0.3.1.dist-info}/top_level.txt +0 -0
|
@@ -1,46 +1,173 @@
|
|
|
1
1
|
# Coded version of DICOM file 'C:\Users\steve\Dropbox\Software\QIB-Sheffield\dbdicom\tests\data\MULTIFRAME\IM_0010'
|
|
2
2
|
# Produced by pydicom codify utility script
|
|
3
|
-
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from datetime import timedelta
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pydicom
|
|
8
|
+
from pydicom.dataset import Dataset, FileDataset, FileMetaDataset
|
|
6
9
|
from pydicom.sequence import Sequence
|
|
10
|
+
from pydicom.uid import (
|
|
11
|
+
generate_uid,
|
|
12
|
+
MRImageStorage,
|
|
13
|
+
EnhancedMRImageStorage,
|
|
14
|
+
ExplicitVRLittleEndian
|
|
15
|
+
)
|
|
7
16
|
|
|
8
|
-
from dbdicom.ds.dataset import DbDataset
|
|
9
|
-
import dbdicom.utils.image as image
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
from dbdicom.utils import image
|
|
12
19
|
|
|
13
|
-
def __init__(self, dataset=None, template=None):
|
|
14
|
-
super().__init__()
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
import numpy as np
|
|
22
|
+
import pydicom
|
|
23
|
+
from pydicom.dataset import Dataset, FileDataset, FileMetaDataset
|
|
24
|
+
from pydicom.sequence import Sequence
|
|
25
|
+
from pydicom.uid import ExplicitVRLittleEndian, EnhancedMRImageStorage, generate_uid
|
|
26
|
+
from datetime import datetime, timedelta
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_5d_enhanced_mr_dataset(
|
|
30
|
+
time_points=20, flip_angles=10, slices=4, rows=128, cols=192
|
|
31
|
+
):
|
|
32
|
+
total_frames = time_points * flip_angles * slices
|
|
33
|
+
now = datetime.now()
|
|
34
|
+
|
|
35
|
+
# File Meta Info
|
|
36
|
+
file_meta = FileMetaDataset()
|
|
37
|
+
file_meta.MediaStorageSOPClassUID = EnhancedMRImageStorage
|
|
38
|
+
file_meta.MediaStorageSOPInstanceUID = generate_uid()
|
|
39
|
+
file_meta.ImplementationClassUID = generate_uid()
|
|
40
|
+
file_meta.TransferSyntaxUID = ExplicitVRLittleEndian
|
|
41
|
+
|
|
42
|
+
# Create FileDataset
|
|
43
|
+
ds = FileDataset(
|
|
44
|
+
filename_or_obj=None,
|
|
45
|
+
dataset=Dataset(),
|
|
46
|
+
file_meta=file_meta,
|
|
47
|
+
preamble=b"\0" * 128,
|
|
48
|
+
)
|
|
18
49
|
|
|
19
|
-
|
|
20
|
-
|
|
50
|
+
ds.is_little_endian = True
|
|
51
|
+
ds.is_implicit_VR = False
|
|
21
52
|
|
|
22
|
-
|
|
23
|
-
|
|
53
|
+
# Identification
|
|
54
|
+
ds.SOPClassUID = EnhancedMRImageStorage
|
|
55
|
+
ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID
|
|
56
|
+
ds.PatientName = "FiveD^Phantom"
|
|
57
|
+
ds.PatientID = "555555"
|
|
58
|
+
ds.StudyInstanceUID = generate_uid()
|
|
59
|
+
ds.SeriesInstanceUID = generate_uid()
|
|
60
|
+
ds.StudyDate = now.strftime("%Y%m%d")
|
|
61
|
+
ds.StudyTime = now.strftime("%H%M%S")
|
|
62
|
+
ds.Modality = "MR"
|
|
63
|
+
ds.Manufacturer = "PythonPACS"
|
|
64
|
+
ds.StudyID = "1"
|
|
65
|
+
ds.SeriesNumber = "1"
|
|
66
|
+
ds.InstanceNumber = "1"
|
|
67
|
+
|
|
68
|
+
# Image Dimensions
|
|
69
|
+
ds.Rows = rows
|
|
70
|
+
ds.Columns = cols
|
|
71
|
+
ds.NumberOfFrames = str(total_frames)
|
|
72
|
+
ds.SamplesPerPixel = 1
|
|
73
|
+
ds.PhotometricInterpretation = "MONOCHROME2"
|
|
74
|
+
ds.BitsAllocated = 16
|
|
75
|
+
ds.BitsStored = 12
|
|
76
|
+
ds.HighBit = 11
|
|
77
|
+
ds.PixelRepresentation = 0
|
|
78
|
+
ds.PixelSpacing = [1.0, 1.0]
|
|
79
|
+
ds.SliceThickness = 1.0
|
|
80
|
+
ds.FrameOfReferenceUID = generate_uid()
|
|
81
|
+
|
|
82
|
+
# Dummy pixel data
|
|
83
|
+
pixel_array = np.zeros((total_frames, rows, cols), dtype=np.uint16)
|
|
84
|
+
ds.PixelData = pixel_array.tobytes()
|
|
85
|
+
|
|
86
|
+
# Shared Functional Groups
|
|
87
|
+
shared_fg = Dataset()
|
|
88
|
+
pix_meas = Dataset()
|
|
89
|
+
pix_meas.PixelSpacing = ds.PixelSpacing
|
|
90
|
+
pix_meas.SliceThickness = ds.SliceThickness
|
|
91
|
+
shared_fg.PixelMeasuresSequence = [pix_meas]
|
|
92
|
+
ds.SharedFunctionalGroupsSequence = [shared_fg]
|
|
93
|
+
|
|
94
|
+
# Dimension Organization
|
|
95
|
+
dim_org_uid = generate_uid()
|
|
96
|
+
ds.DimensionOrganizationSequence = Sequence([
|
|
97
|
+
Dataset()
|
|
98
|
+
])
|
|
99
|
+
ds.DimensionOrganizationSequence[0].DimensionOrganizationUID = dim_org_uid
|
|
100
|
+
|
|
101
|
+
ds.DimensionIndexSequence = Sequence()
|
|
102
|
+
|
|
103
|
+
# Time dimension
|
|
104
|
+
temporal = Dataset()
|
|
105
|
+
temporal.DimensionOrganizationUID = dim_org_uid
|
|
106
|
+
temporal.DimensionIndexPointer = 0x00209164 # TemporalPositionIndex
|
|
107
|
+
temporal.FunctionalGroupPointer = 0x00209113 # TemporalPositionSequence
|
|
108
|
+
ds.DimensionIndexSequence.append(temporal)
|
|
109
|
+
|
|
110
|
+
# Flip angle dimension
|
|
111
|
+
flip = Dataset()
|
|
112
|
+
flip.DimensionOrganizationUID = dim_org_uid
|
|
113
|
+
flip.DimensionIndexPointer = 0x00181314 # FlipAngle
|
|
114
|
+
flip.FunctionalGroupPointer = 0x00189105 # MRImagingModifierSequence
|
|
115
|
+
ds.DimensionIndexSequence.append(flip)
|
|
116
|
+
|
|
117
|
+
# Slice position
|
|
118
|
+
slice_dim = Dataset()
|
|
119
|
+
slice_dim.DimensionOrganizationUID = dim_org_uid
|
|
120
|
+
slice_dim.DimensionIndexPointer = 0x00200032 # ImagePositionPatient
|
|
121
|
+
slice_dim.FunctionalGroupPointer = 0x00209113 # PlanePositionSequence
|
|
122
|
+
ds.DimensionIndexSequence.append(slice_dim)
|
|
123
|
+
|
|
124
|
+
# Per-Frame Functional Groups
|
|
125
|
+
per_frame_seq = []
|
|
126
|
+
|
|
127
|
+
base_time = now
|
|
128
|
+
flip_angle_values = np.linspace(5, 50, flip_angles) # Example flip angles
|
|
129
|
+
|
|
130
|
+
for t in range(time_points):
|
|
131
|
+
for f in range(flip_angles):
|
|
132
|
+
for z in range(slices):
|
|
133
|
+
frame = Dataset()
|
|
134
|
+
|
|
135
|
+
# Frame content
|
|
136
|
+
fc = Dataset()
|
|
137
|
+
fc.FrameAcquisitionNumber = len(per_frame_seq)
|
|
138
|
+
fc.AcquisitionTime = (base_time + timedelta(seconds=t)).strftime("%H%M%S.%f")[:13]
|
|
139
|
+
frame.FrameContentSequence = [fc]
|
|
140
|
+
|
|
141
|
+
# Temporal position
|
|
142
|
+
tp = Dataset()
|
|
143
|
+
tp.TemporalPositionIndex = t + 1
|
|
144
|
+
frame.TemporalPositionSequence = [tp]
|
|
145
|
+
|
|
146
|
+
# Flip angle
|
|
147
|
+
fa = Dataset()
|
|
148
|
+
fa.FlipAngle = float(flip_angle_values[f])
|
|
149
|
+
frame.MRImagingModifierSequence = [fa]
|
|
150
|
+
|
|
151
|
+
# Slice position
|
|
152
|
+
pos = Dataset()
|
|
153
|
+
pos.ImagePositionPatient = [0.0, 0.0, float(z)]
|
|
154
|
+
frame.PlanePositionSequence = [pos]
|
|
155
|
+
|
|
156
|
+
per_frame_seq.append(frame)
|
|
157
|
+
|
|
158
|
+
ds.PerFrameFunctionalGroupsSequence = Sequence(per_frame_seq)
|
|
24
159
|
|
|
25
|
-
|
|
26
|
-
def pixel_array(self):
|
|
27
|
-
"""Reimplements pydicom property pixel_array"""
|
|
28
|
-
return get_pixel_array(self)
|
|
160
|
+
return ds
|
|
29
161
|
|
|
30
|
-
def get_pixel_array(self):
|
|
31
|
-
return get_pixel_array(self)
|
|
32
162
|
|
|
33
|
-
def set_pixel_array(self, array, value_range=None):
|
|
34
|
-
set_pixel_array(self, array, value_range=value_range)
|
|
35
163
|
|
|
36
|
-
def image_type(self):
|
|
37
|
-
return image_type(self)
|
|
38
164
|
|
|
39
|
-
def signal_type(self):
|
|
40
|
-
return signal_type(self)
|
|
41
165
|
|
|
42
166
|
|
|
43
|
-
def
|
|
167
|
+
def default(): # UKRIN-MAPS
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
ds = Dataset()
|
|
44
171
|
|
|
45
172
|
# File meta info data elements
|
|
46
173
|
ds.file_meta = FileMetaDataset()
|
|
@@ -1,61 +1,208 @@
|
|
|
1
1
|
# Coded version of DICOM file
|
|
2
2
|
# 'RIDER Neuro MRI-3369019796\03-21-1904-BRAINRESEARCH-00598\14.000000-sag 3d gre c-04769\1-010.dcm'
|
|
3
3
|
# Produced by pydicom codify utility script
|
|
4
|
+
import os
|
|
4
5
|
import struct
|
|
5
6
|
import numpy as np
|
|
6
7
|
import pydicom
|
|
7
|
-
from pydicom.dataset import Dataset,
|
|
8
|
+
from pydicom.dataset import FileMetaDataset, Dataset, FileDataset
|
|
8
9
|
from pydicom.sequence import Sequence
|
|
10
|
+
from pydicom.uid import (
|
|
11
|
+
ExplicitVRLittleEndian,
|
|
12
|
+
generate_uid,
|
|
13
|
+
MRImageStorage,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
import tempfile
|
|
9
18
|
|
|
10
|
-
from dbdicom.ds.dataset import DbDataset
|
|
11
19
|
import dbdicom.utils.image as image
|
|
12
20
|
|
|
13
21
|
|
|
14
|
-
|
|
22
|
+
def pixel_data(ds):
|
|
23
|
+
"""Read the pixel array from an MR image"""
|
|
24
|
+
|
|
25
|
+
#array = ds.pixel_array.astype(np.float64)
|
|
26
|
+
#array = ds.pixel_array
|
|
27
|
+
#array = np.frombuffer(ds.PixelData, dtype=np.uint16).reshape(ds.Rows, ds.Columns)
|
|
28
|
+
#array = array.astype(np.float32)
|
|
29
|
+
|
|
30
|
+
array = ds.pixel_array
|
|
31
|
+
array = array.astype(np.float32)
|
|
32
|
+
if [0x2005, 0x100E] in ds: # 'Philips Rescale Slope'
|
|
33
|
+
slope = ds[(0x2005, 0x100E)].value
|
|
34
|
+
intercept = ds[(0x2005, 0x100D)].value
|
|
35
|
+
array -= intercept
|
|
36
|
+
array /= slope
|
|
37
|
+
else:
|
|
38
|
+
slope = float(getattr(ds, 'RescaleSlope', 1))
|
|
39
|
+
intercept = float(getattr(ds, 'RescaleIntercept', 0))
|
|
40
|
+
array *= slope
|
|
41
|
+
array += intercept
|
|
42
|
+
return np.transpose(array)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def set_pixel_data(ds, array):
|
|
15
46
|
|
|
16
|
-
|
|
17
|
-
|
|
47
|
+
if (0x2005, 0x100E) in ds:
|
|
48
|
+
del ds[0x2005, 0x100E] # Delete 'Philips Rescale Slope'
|
|
49
|
+
if (0x2005, 0x100D) in ds:
|
|
50
|
+
del ds[0x2005, 0x100D]
|
|
18
51
|
|
|
19
|
-
|
|
20
|
-
|
|
52
|
+
# clipping may slow down a lot
|
|
53
|
+
array = image.clip(array.astype(np.float32))
|
|
54
|
+
array, slope, intercept = image.scale_to_range(array, ds.BitsAllocated)
|
|
55
|
+
array = np.transpose(array)
|
|
21
56
|
|
|
22
|
-
|
|
23
|
-
|
|
57
|
+
ds.PixelRepresentation = 0
|
|
58
|
+
ds.RescaleSlope = 1 / slope
|
|
59
|
+
ds.RescaleIntercept = - intercept / slope
|
|
60
|
+
# ds.WindowCenter = (maximum + minimum) / 2
|
|
61
|
+
# ds.WindowWidth = maximum - minimum
|
|
62
|
+
ds.Rows = array.shape[0]
|
|
63
|
+
ds.Columns = array.shape[1]
|
|
64
|
+
ds.PixelData = array.tobytes()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def chat_gpt_3d(num_frames=10, rows=256, columns=256):
|
|
69
|
+
|
|
70
|
+
# File meta info
|
|
71
|
+
file_meta = FileMetaDataset()
|
|
72
|
+
file_meta.MediaStorageSOPClassUID = MRImageStorage
|
|
73
|
+
file_meta.MediaStorageSOPInstanceUID = generate_uid()
|
|
74
|
+
file_meta.ImplementationClassUID = generate_uid()
|
|
75
|
+
file_meta.TransferSyntaxUID = ExplicitVRLittleEndian
|
|
76
|
+
|
|
77
|
+
# Create FileDataset in memory
|
|
78
|
+
ds = FileDataset(
|
|
79
|
+
filename_or_obj=None,
|
|
80
|
+
dataset=Dataset(),
|
|
81
|
+
file_meta=file_meta,
|
|
82
|
+
preamble=b"\0" * 128,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Transfer syntax
|
|
86
|
+
ds.is_little_endian = True
|
|
87
|
+
ds.is_implicit_VR = False
|
|
88
|
+
|
|
89
|
+
now = datetime.now()
|
|
90
|
+
|
|
91
|
+
# Required fields
|
|
92
|
+
ds.SOPClassUID = MRImageStorage
|
|
93
|
+
ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID
|
|
94
|
+
ds.PatientName = "Multi^Frame"
|
|
95
|
+
ds.PatientID = "999999"
|
|
96
|
+
ds.StudyInstanceUID = generate_uid()
|
|
97
|
+
ds.SeriesInstanceUID = generate_uid()
|
|
98
|
+
ds.StudyDate = now.strftime("%Y%m%d")
|
|
99
|
+
ds.StudyTime = now.strftime("%H%M%S")
|
|
100
|
+
ds.Modality = "MR"
|
|
101
|
+
ds.Manufacturer = "PythonPACS"
|
|
102
|
+
ds.StudyID = "1"
|
|
103
|
+
ds.SeriesNumber = "1"
|
|
104
|
+
ds.InstanceNumber = "1"
|
|
105
|
+
|
|
106
|
+
# Image geometry
|
|
107
|
+
ds.Rows = rows
|
|
108
|
+
ds.Columns = columns
|
|
109
|
+
ds.PixelSpacing = [1.0, 1.0]
|
|
110
|
+
ds.ImagePositionPatient = [0.0, 0.0, 0.0]
|
|
111
|
+
ds.ImageOrientationPatient = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
|
|
112
|
+
ds.FrameOfReferenceUID = generate_uid()
|
|
113
|
+
ds.PositionReferenceIndicator = ""
|
|
114
|
+
|
|
115
|
+
# Multi-frame specific
|
|
116
|
+
ds.NumberOfFrames = str(num_frames)
|
|
117
|
+
ds.InstanceNumber = "1"
|
|
118
|
+
|
|
119
|
+
# Pixel data requirements
|
|
120
|
+
ds.SamplesPerPixel = 1
|
|
121
|
+
ds.PhotometricInterpretation = "MONOCHROME2"
|
|
122
|
+
ds.BitsAllocated = 16
|
|
123
|
+
ds.BitsStored = 12
|
|
124
|
+
ds.HighBit = 11
|
|
125
|
+
ds.PixelRepresentation = 0 # unsigned
|
|
24
126
|
|
|
25
|
-
|
|
26
|
-
|
|
127
|
+
# Create dummy image data (e.g., black frames)
|
|
128
|
+
pixel_array = np.zeros((num_frames, rows, columns), dtype=np.uint16)
|
|
129
|
+
ds.PixelData = pixel_array.tobytes()
|
|
27
130
|
|
|
28
|
-
|
|
29
|
-
return get_pixel_array(self)
|
|
131
|
+
return ds
|
|
30
132
|
|
|
31
|
-
def set_pixel_array(self, array):
|
|
32
|
-
set_pixel_array(self, array)
|
|
33
133
|
|
|
34
|
-
def get_attribute_image_type(self):
|
|
35
|
-
return get_attribute_image_type(self)
|
|
36
134
|
|
|
37
|
-
|
|
38
|
-
|
|
135
|
+
def chat_gpt_2d():
|
|
136
|
+
# Basic identifiers
|
|
137
|
+
file_meta = FileMetaDataset()
|
|
138
|
+
file_meta.MediaStorageSOPClassUID = MRImageStorage
|
|
139
|
+
file_meta.MediaStorageSOPInstanceUID = generate_uid()
|
|
140
|
+
file_meta.ImplementationClassUID = generate_uid()
|
|
39
141
|
|
|
40
|
-
|
|
41
|
-
|
|
142
|
+
# Create the main dataset
|
|
143
|
+
ds = FileDataset(
|
|
144
|
+
filename_or_obj=None,
|
|
145
|
+
dataset=Dataset(),
|
|
146
|
+
file_meta=file_meta,
|
|
147
|
+
preamble=b"\0" * 128,
|
|
148
|
+
)
|
|
42
149
|
|
|
43
|
-
|
|
44
|
-
|
|
150
|
+
# Set transfer syntax
|
|
151
|
+
ds.is_little_endian = True
|
|
152
|
+
ds.is_implicit_VR = False
|
|
153
|
+
|
|
154
|
+
# Required DICOM tags for MR Image Storage
|
|
155
|
+
now = datetime.now()
|
|
156
|
+
ds.SOPClassUID = MRImageStorage
|
|
157
|
+
ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID
|
|
158
|
+
ds.PatientName = "Anonymous^Patient"
|
|
159
|
+
ds.PatientID = "123456"
|
|
160
|
+
ds.StudyInstanceUID = generate_uid()
|
|
161
|
+
ds.SeriesInstanceUID = generate_uid()
|
|
162
|
+
ds.StudyDate = now.strftime("%Y%m%d")
|
|
163
|
+
ds.StudyTime = now.strftime("%H%M%S")
|
|
164
|
+
ds.Modality = "MR"
|
|
165
|
+
ds.Manufacturer = "PythonPACS"
|
|
166
|
+
ds.StudyID = "1"
|
|
167
|
+
ds.SeriesNumber = "1"
|
|
168
|
+
ds.InstanceNumber = "1"
|
|
169
|
+
ds.ImagePositionPatient = [0.0, 0.0, 0.0]
|
|
170
|
+
ds.ImageOrientationPatient = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
|
|
171
|
+
ds.FrameOfReferenceUID = generate_uid()
|
|
172
|
+
ds.PositionReferenceIndicator = ""
|
|
173
|
+
ds.Rows = 256
|
|
174
|
+
ds.Columns = 256
|
|
175
|
+
ds.PixelSpacing = [1.0, 1.0]
|
|
176
|
+
ds.BitsAllocated = 16
|
|
177
|
+
ds.BitsStored = 12
|
|
178
|
+
ds.HighBit = 11
|
|
179
|
+
ds.PixelRepresentation = 0 # unsigned
|
|
180
|
+
ds.SamplesPerPixel = 1
|
|
181
|
+
ds.PhotometricInterpretation = "MONOCHROME2"
|
|
182
|
+
ds.PixelData = (b"\0" * (ds.Rows * ds.Columns * 2)) # Dummy black image
|
|
45
183
|
|
|
184
|
+
return ds
|
|
46
185
|
|
|
47
186
|
|
|
48
|
-
def
|
|
187
|
+
def default(): # from the RIDER dataset
|
|
49
188
|
|
|
50
189
|
# File meta info data elements
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
190
|
+
file_meta = FileMetaDataset()
|
|
191
|
+
file_meta.FileMetaInformationGroupLength = 190
|
|
192
|
+
file_meta.FileMetaInformationVersion = b'\x00\x01'
|
|
193
|
+
file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.4'
|
|
194
|
+
file_meta.MediaStorageSOPInstanceUID = '1.3.6.1.4.1.9328.50.16.175333593952805976694548436931998383940'
|
|
195
|
+
file_meta.TransferSyntaxUID = '1.2.840.10008.1.2'
|
|
196
|
+
file_meta.ImplementationClassUID = '1.2.40.0.13.1.1'
|
|
197
|
+
file_meta.ImplementationVersionName = 'dcm4che-1.4.27'
|
|
198
|
+
|
|
199
|
+
# Create the main dataset
|
|
200
|
+
ds = FileDataset(
|
|
201
|
+
filename_or_obj=None,
|
|
202
|
+
dataset=Dataset(),
|
|
203
|
+
file_meta=file_meta,
|
|
204
|
+
preamble=b"\0" * 128,
|
|
205
|
+
)
|
|
59
206
|
|
|
60
207
|
ds.is_implicit_VR = True
|
|
61
208
|
ds.is_little_endian = True
|
|
@@ -162,117 +309,15 @@ def rider(ds): # required only - check
|
|
|
162
309
|
return ds
|
|
163
310
|
|
|
164
311
|
|
|
165
|
-
def get_pixel_array(ds):
|
|
166
|
-
"""Read the pixel array from an MR image"""
|
|
167
|
-
|
|
168
|
-
#array = ds.pixel_array.astype(np.float64)
|
|
169
|
-
#array = ds.pixel_array
|
|
170
|
-
#array = np.frombuffer(ds.PixelData, dtype=np.uint16).reshape(ds.Rows, ds.Columns)
|
|
171
|
-
#array = array.astype(np.float32)
|
|
172
|
-
|
|
173
|
-
array = ds.pixel_array
|
|
174
|
-
array = array.astype(np.float32)
|
|
175
|
-
if [0x2005, 0x100E] in ds: # 'Philips Rescale Slope'
|
|
176
|
-
slope = ds[(0x2005, 0x100E)].value
|
|
177
|
-
intercept = ds[(0x2005, 0x100D)].value
|
|
178
|
-
array -= intercept
|
|
179
|
-
array /= slope
|
|
180
|
-
else:
|
|
181
|
-
slope = float(getattr(ds, 'RescaleSlope', 1))
|
|
182
|
-
intercept = float(getattr(ds, 'RescaleIntercept', 0))
|
|
183
|
-
array *= slope
|
|
184
|
-
array += intercept
|
|
185
|
-
return np.transpose(array)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def set_pixel_array(ds, array):
|
|
189
312
|
|
|
190
|
-
if (0x2005, 0x100E) in ds:
|
|
191
|
-
del ds[0x2005, 0x100E] # Delete 'Philips Rescale Slope'
|
|
192
|
-
if (0x2005, 0x100D) in ds:
|
|
193
|
-
del ds[0x2005, 0x100D]
|
|
194
313
|
|
|
195
|
-
# clipping may slow down a lot
|
|
196
|
-
array = image.clip(array.astype(np.float32))
|
|
197
|
-
array, slope, intercept = image.scale_to_range(array, ds.BitsAllocated)
|
|
198
|
-
array = np.transpose(array)
|
|
199
|
-
|
|
200
|
-
ds.PixelRepresentation = 0
|
|
201
314
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
# ds.set_values('LargestImagePixelValue', int(2**ds.BitsAllocated - 1))
|
|
209
|
-
# else:
|
|
210
|
-
# ds.set_values('SmallestImagePixelValue', int(-2**(ds.BitsAllocated - 1)))
|
|
211
|
-
# ds.set_values('LargestImagePixelValue', int(2**(ds.BitsAllocated - 1)-1))
|
|
315
|
+
if __name__ == '__main__':
|
|
316
|
+
file = os.path.join(os.getcwd(), 'chat_gpt_mri.dcm')
|
|
317
|
+
chat_gpt_3d().save_as(file)
|
|
318
|
+
file = os.path.join(os.getcwd(), 'default_mri.dcm')
|
|
319
|
+
default().save_as(file)
|
|
320
|
+
print(file)
|
|
212
321
|
|
|
213
|
-
ds.RescaleSlope = 1 / slope
|
|
214
|
-
ds.RescaleIntercept = - intercept / slope
|
|
215
|
-
# ds.WindowCenter = (maximum + minimum) / 2
|
|
216
|
-
# ds.WindowWidth = maximum - minimum
|
|
217
|
-
ds.Rows = array.shape[0]
|
|
218
|
-
ds.Columns = array.shape[1]
|
|
219
|
-
ds.PixelData = array.tobytes()
|
|
220
322
|
|
|
221
323
|
|
|
222
|
-
def get_attribute_image_type(ds):
|
|
223
|
-
"""Determine if an image is Magnitude, Phase, Real or Imaginary image or None"""
|
|
224
|
-
|
|
225
|
-
if (0x0043, 0x102f) in ds:
|
|
226
|
-
private_ge = ds[0x0043, 0x102f]
|
|
227
|
-
try:
|
|
228
|
-
value = struct.unpack('h', private_ge.value)[0]
|
|
229
|
-
except:
|
|
230
|
-
value = private_ge.value
|
|
231
|
-
if value == 0:
|
|
232
|
-
return 'MAGNITUDE'
|
|
233
|
-
if value == 1:
|
|
234
|
-
return 'PHASE'
|
|
235
|
-
if value == 2:
|
|
236
|
-
return 'REAL'
|
|
237
|
-
if value == 3:
|
|
238
|
-
return 'IMAGINARY'
|
|
239
|
-
|
|
240
|
-
if 'ImageType' in ds:
|
|
241
|
-
type = set(ds.ImageType)
|
|
242
|
-
if set(['M', 'MAGNITUDE']).intersection(type):
|
|
243
|
-
return 'MAGNITUDE'
|
|
244
|
-
if set(['P', 'PHASE']).intersection(type):
|
|
245
|
-
return 'PHASE'
|
|
246
|
-
if set(['R', 'REAL']).intersection(type):
|
|
247
|
-
return 'REAL'
|
|
248
|
-
if set(['I', 'IMAGINARY']).intersection(type):
|
|
249
|
-
return 'IMAGINARY'
|
|
250
|
-
|
|
251
|
-
if 'ComplexImageComponent' in ds:
|
|
252
|
-
return ds.ComplexImageComponent
|
|
253
|
-
|
|
254
|
-
return 'UNKNOWN'
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def set_attribute_image_type(ds, value):
|
|
258
|
-
ds.ImageType = value
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
def get_attribute_signal_type(ds):
|
|
262
|
-
"""Determine if an image is Water, Fat, In-Phase, Out-phase image or None"""
|
|
263
|
-
|
|
264
|
-
if hasattr(ds, 'ImageType'):
|
|
265
|
-
type = set(ds.ImageType)
|
|
266
|
-
if set(['W', 'WATER']).intersection(type):
|
|
267
|
-
return 'WATER'
|
|
268
|
-
elif set(['F', 'FAT']).intersection(type):
|
|
269
|
-
return 'FAT'
|
|
270
|
-
elif set(['IP', 'IN_PHASE']).intersection(type):
|
|
271
|
-
return 'IN_PHASE'
|
|
272
|
-
elif set(['OP', 'OUT_PHASE']).intersection(type):
|
|
273
|
-
return 'OP_PHASE'
|
|
274
|
-
return 'UNKNOWN'
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def set_attribute_signal_type(ds, value):
|
|
278
|
-
ds.ImageType = value
|