dbdicom 0.3.8__tar.gz → 0.3.10__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 dbdicom might be problematic. Click here for more details.

Files changed (67) hide show
  1. {dbdicom-0.3.8/src/dbdicom.egg-info → dbdicom-0.3.10}/PKG-INFO +1 -1
  2. {dbdicom-0.3.8 → dbdicom-0.3.10}/pyproject.toml +1 -1
  3. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/api.py +60 -46
  4. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/database.py +2 -2
  5. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/dataset.py +30 -316
  6. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/dbd.py +302 -162
  7. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/register.py +21 -0
  8. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/enhanced_mr_image.py +190 -271
  9. dbdicom-0.3.10/src/dbdicom/utils/arrays.py +128 -0
  10. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/utils/image.py +13 -13
  11. dbdicom-0.3.10/src/dbdicom/utils/pydicom_dataset.py +386 -0
  12. {dbdicom-0.3.8 → dbdicom-0.3.10/src/dbdicom.egg-info}/PKG-INFO +1 -1
  13. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom.egg-info/SOURCES.txt +4 -2
  14. dbdicom-0.3.10/tests/test_api.py +225 -0
  15. dbdicom-0.3.10/tests/test_sop_classes.py +33 -0
  16. dbdicom-0.3.10/tests/test_utils.py +101 -0
  17. dbdicom-0.3.8/src/dbdicom/utils/arrays.py +0 -40
  18. dbdicom-0.3.8/src/dbdicom/utils/variables.py +0 -161
  19. dbdicom-0.3.8/tests/test_api.py +0 -103
  20. {dbdicom-0.3.8 → dbdicom-0.3.10}/LICENSE +0 -0
  21. {dbdicom-0.3.8 → dbdicom-0.3.10}/MANIFEST.in +0 -0
  22. {dbdicom-0.3.8 → dbdicom-0.3.10}/README.rst +0 -0
  23. {dbdicom-0.3.8 → dbdicom-0.3.10}/setup.cfg +0 -0
  24. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/__init__.py +0 -0
  25. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/const.py +0 -0
  26. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/__init__.py +0 -0
  27. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/__pycache__/__init__.cpython-311.pyc +0 -0
  28. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/README.md +0 -0
  29. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/__init__.py +0 -0
  30. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/__pycache__/__init__.cpython-311.pyc +0 -0
  31. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/bin/__init__.py +0 -0
  32. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-311.pyc +0 -0
  33. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/bin/deidentify +0 -0
  34. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/bin/deidentify.bat +0 -0
  35. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/bin/emf2sf +0 -0
  36. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/bin/emf2sf.bat +0 -0
  37. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/etc/__init__.py +0 -0
  38. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/etc/emf2sf/__init__.py +0 -0
  39. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/etc/emf2sf/log4j.properties +0 -0
  40. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/__init__.py +0 -0
  41. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/commons-cli-1.4.jar +0 -0
  42. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/dcm4che-core-5.23.1.jar +0 -0
  43. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/dcm4che-emf-5.23.1.jar +0 -0
  44. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/dcm4che-tool-common-5.23.1.jar +0 -0
  45. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/dcm4che-tool-emf2sf-5.23.1.jar +0 -0
  46. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/log4j-1.2.17.jar +0 -0
  47. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/macosx-x86-64/libopencv_java.jnilib +0 -0
  48. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/slf4j-api-1.7.30.jar +0 -0
  49. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/slf4j-log4j12-1.7.30.jar +0 -0
  50. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/windows-x86/clib_jiio.dll +0 -0
  51. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/windows-x86/clib_jiio_sse2.dll +0 -0
  52. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/windows-x86/clib_jiio_util.dll +0 -0
  53. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/windows-x86/opencv_java.dll +0 -0
  54. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/external/dcm4che/lib/windows-x86-64/opencv_java.dll +0 -0
  55. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/ct_image.py +0 -0
  56. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/mr_image.py +0 -0
  57. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/parametric_map.py +0 -0
  58. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/secondary_capture.py +0 -0
  59. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/segmentation.py +0 -0
  60. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/ultrasound_multiframe_image.py +0 -0
  61. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/sop_classes/xray_angiographic_image.py +0 -0
  62. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/utils/dcm4che.py +0 -0
  63. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom/utils/files.py +0 -0
  64. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom.egg-info/dependency_links.txt +0 -0
  65. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom.egg-info/requires.txt +0 -0
  66. {dbdicom-0.3.8 → dbdicom-0.3.10}/src/dbdicom.egg-info/top_level.txt +0 -0
  67. {dbdicom-0.3.8 → dbdicom-0.3.10}/tests/test_dcm4che.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbdicom
3
- Version: 0.3.8
3
+ Version: 0.3.10
4
4
  Summary: A pythonic interface for reading and writing DICOM databases
5
5
  Author-email: Steven Sourbron <s.sourbron@sheffield.ac.uk>, Ebony Gunwhy <e.gunwhy@sheffield.ac.uk>
6
6
  Project-URL: Homepage, https://openmiblab.github.io/dbdicom/
@@ -7,7 +7,7 @@ requires = ['setuptools>=61.2']
7
7
 
8
8
  [project]
9
9
  name = "dbdicom"
10
- version = "0.3.8"
10
+ version = "0.3.10"
11
11
  dependencies = [
12
12
  "tqdm",
13
13
  "importlib-resources",
@@ -4,8 +4,7 @@ import zipfile
4
4
  from pathlib import Path
5
5
  from typing import Union
6
6
  from tqdm import tqdm
7
-
8
-
7
+ import numpy as np
9
8
  import vreg
10
9
 
11
10
  from dbdicom.dbd import DataBaseDicom
@@ -164,16 +163,22 @@ def series(entity:str | list, desc:str=None, contains:str=None, isin:list=None)-
164
163
  "To retrieve a series, the entity must be a database, patient or study."
165
164
  )
166
165
 
167
- def copy(from_entity:list, to_entity:list):
168
- """Copy a DICOM entity (patient, study or series)
166
+ def copy(from_entity:list, to_entity=None):
167
+ """Copy a DICOM entity (patient, study or series)
169
168
 
170
169
  Args:
171
170
  from_entity (list): entity to copy
172
- to_entity (list): entity after copying.
171
+ to_entity (list, optional): entity after copying. If this is not
172
+ provided, a copy will be made in the same study and returned.
173
+
174
+ Returns:
175
+ entity: the copied entity. If th to_entity is provided, this is
176
+ returned.
173
177
  """
174
178
  dbd = open(from_entity[0])
175
- dbd.copy(from_entity, to_entity)
179
+ from_entity_copy = dbd.copy(from_entity, to_entity)
176
180
  dbd.close()
181
+ return from_entity_copy
177
182
 
178
183
 
179
184
  def delete(entity:list):
@@ -216,24 +221,42 @@ def split_series(series:list, attr:Union[str, tuple], key=None)->list:
216
221
  return split_series
217
222
 
218
223
 
219
- def volume(entity:Union[list, str], dims:list=None) -> Union[vreg.Volume3D, list]:
220
- """Read volume or volumes.
224
+ def volume(series:list, dims:list=None, verbose=1) -> vreg.Volume3D:
225
+ """Read volume from a series.
221
226
 
222
227
  Args:
223
- entity (list, str): DICOM entity to read
228
+ series (list, str): DICOM entity to read
224
229
  dims (list, optional): Non-spatial dimensions of the volume. Defaults to None.
230
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
225
231
 
226
232
  Returns:
227
- vreg.Volume3D | list: If the entity is a series this returns
228
- a volume, else a list of volumes.
233
+ vreg.Volume3D.
229
234
  """
230
- if isinstance(entity, str):
231
- entity = [entity]
232
- dbd = open(entity[0])
233
- vol = dbd.volume(entity, dims)
235
+ dbd = open(series[0])
236
+ vol = dbd.volume(series, dims, verbose)
234
237
  dbd.close()
235
238
  return vol
236
239
 
240
+
241
+ def values(series:list, *attr, dims:list=None, verbose=1) -> Union[np.ndarray, list]:
242
+ """Read the values of some attributes from a DICOM series
243
+
244
+ Args:
245
+ series (list): DICOM series to read.
246
+ attr (tuple, optional): DICOM attributes to read.
247
+ dims (list, optional): Dimensions to sort the values.
248
+ If dims is not provided, values are sorted by
249
+ InstanceNumber.
250
+
251
+ Returns:
252
+ tuple: arrays with values for the attributes.
253
+ """
254
+ dbd = open(series[0])
255
+ values = dbd.values(series, *attr, dims=dims, verbose=verbose)
256
+ dbd.close()
257
+ return values
258
+
259
+
237
260
  def write_volume(vol:Union[vreg.Volume3D, tuple], series:list, ref:list=None):
238
261
  """Write a vreg.Volume3D to a DICOM series
239
262
 
@@ -246,7 +269,26 @@ def write_volume(vol:Union[vreg.Volume3D, tuple], series:list, ref:list=None):
246
269
  dbd.write_volume(vol, series, ref)
247
270
  dbd.close()
248
271
 
249
- def to_nifti(series:list, file:str, dims:list=None):
272
+
273
+ def edit(series:list, new_values:dict, dims:list=None, verbose=1):
274
+ """Edit attribute values in a DICOM series
275
+
276
+ Warning: this function edits all values as requested. Please take care
277
+ when editing attributes that affect the DICOM file organisation, such as
278
+ UIDs, as this could corrupt the database.
279
+
280
+ Args:
281
+ series (list): DICOM series to edit
282
+ new_values (dict): dictionary with attribute: value pairs to write to the series
283
+ dims (list, optional): Non-spatial dimensions of the volume. Defaults to None.
284
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
285
+
286
+ """
287
+ dbd = open(series[0])
288
+ dbd.edit(series, new_values, dims=dims, verbose=verbose)
289
+ dbd.close()
290
+
291
+ def to_nifti(series:list, file:str, dims:list=None, verbose=1):
250
292
  """Save a DICOM series in nifti format.
251
293
 
252
294
  Args:
@@ -254,9 +296,10 @@ def to_nifti(series:list, file:str, dims:list=None):
254
296
  file (str): file path of the nifti file.
255
297
  dims (list, optional): Non-spatial dimensions of the volume.
256
298
  Defaults to None.
299
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
257
300
  """
258
301
  dbd = open(series[0])
259
- dbd.to_nifti(series, file, dims)
302
+ dbd.to_nifti(series, file, dims, verbose)
260
303
  dbd.close()
261
304
 
262
305
  def from_nifti(file:str, series:list, ref:list=None):
@@ -272,35 +315,6 @@ def from_nifti(file:str, series:list, ref:list=None):
272
315
  dbd.close()
273
316
 
274
317
 
275
- def values(series:list, attr=None, dims:list=None, coords=False) -> Union[dict, tuple]:
276
- """Read the values of some or all attributes from a DICOM series
277
-
278
- Args:
279
- series (list or str): DICOM series to read. This can also
280
- be a path to a folder containing DICOM files, or a
281
- patient or study to read all series in that patient or
282
- study. In those cases a list is returned.
283
- attr (list, optional): list of DICOM attributes to read.
284
- dims (list, optional): Dimensions to sort the attributes.
285
- If dims is not provided, values are sorted by
286
- InstanceNumber.
287
- coords (bool): If set to True, the coordinates of the
288
- attributes are returned alongside the values
289
-
290
- Returns:
291
- dict or tuple: values as a dictionary in the last
292
- return value, where each value is a numpy array with
293
- the required dimensions. If coords is set to True,
294
- these are returned too.
295
- """
296
- if isinstance(series, str):
297
- series = [series]
298
- dbd = open(series[0])
299
- array = dbd.values(series, attr, dims, coords)
300
- dbd.close()
301
- return array
302
-
303
-
304
318
  def files(entity:list) -> list:
305
319
  """Read the files in a DICOM entity
306
320
 
@@ -7,7 +7,7 @@ import pydicom
7
7
 
8
8
  import dbdicom.utils.dcm4che as dcm4che
9
9
  import dbdicom.utils.files as filetools
10
- import dbdicom.dataset as dbdataset
10
+ from dbdicom.utils.pydicom_dataset import get_values
11
11
 
12
12
 
13
13
  COLUMNS = [
@@ -41,7 +41,7 @@ def read(path):
41
41
  if 'TransferSyntaxUID' in ds.file_meta:
42
42
  if not 'Rows' in ds: # Image only
43
43
  continue
44
- row = dbdataset.get_values(ds, tags)
44
+ row = get_values(ds, tags)
45
45
  array.append(row)
46
46
  index = os.path.relpath(file, path)
47
47
  dicom_files.append(index)
@@ -2,19 +2,18 @@
2
2
  # https://www.aliza-dicom-viewer.com/download/datasets
3
3
 
4
4
  import os
5
- from datetime import datetime
6
5
  import struct
7
6
  from tqdm import tqdm
8
7
 
9
8
  import numpy as np
10
9
  import pydicom
11
10
  from pydicom.util.codify import code_file
11
+ from pydicom.tag import Tag
12
12
  import pydicom.config
13
13
  import vreg
14
14
 
15
-
15
+ from dbdicom.utils.pydicom_dataset import get_values, set_values
16
16
  import dbdicom.utils.image as image
17
- import dbdicom.utils.variables as variables
18
17
  from dbdicom.sop_classes import (
19
18
  xray_angiographic_image,
20
19
  ct_image,
@@ -50,15 +49,15 @@ SOPCLASSMODULE = {
50
49
  }
51
50
 
52
51
 
53
- def read_dataset(file):
52
+ # def read_dataset(file):
54
53
 
55
- try:
56
- ds = pydicom.dcmread(file)
57
- # ds = pydicom.dcmread(file, force=True) # more robust but hides corrupted data
58
- except Exception:
59
- raise FileNotFoundError('File not found')
54
+ # try:
55
+ # ds = pydicom.dcmread(file)
56
+ # # ds = pydicom.dcmread(file, force=True) # more robust but hides corrupted data
57
+ # except Exception:
58
+ # raise FileNotFoundError('File not found')
60
59
 
61
- return ds
60
+ # return ds
62
61
 
63
62
 
64
63
  def new_dataset(sop_class):
@@ -81,149 +80,6 @@ def new_dataset(sop_class):
81
80
  )
82
81
 
83
82
 
84
-
85
- def get_values(ds, tags):
86
- """Return a list of values for a dataset"""
87
-
88
- # https://pydicom.github.io/pydicom/stable/guides/element_value_types.html
89
- if np.isscalar(tags):
90
- return get_values(ds, [tags])[0]
91
-
92
- row = []
93
- for tag in tags:
94
- value = None
95
-
96
- # If the tag is provided as string
97
- if isinstance(tag, str):
98
- if hasattr(ds, tag):
99
- pydcm_value = ds[tag].value
100
- try:
101
- VR = pydicom.datadict.dictionary_VR(tag)
102
- except:
103
- VR = None
104
- value = to_set_type(pydcm_value, VR) # ELIMINATE THIS STEP - return pydicom datatypes
105
-
106
- # If the tag is a tuple of hexadecimal values
107
- else:
108
- if tag in ds:
109
- try:
110
- VR = pydicom.datadict.dictionary_VR(tag)
111
- except:
112
- VR = None
113
- value = to_set_type(ds[tag].value, VR)
114
-
115
- # If a tag is not present in the dataset, check if it can be derived
116
- if value is None:
117
- value = derive_data_element(ds, tag)
118
-
119
- row.append(value)
120
- return row
121
-
122
-
123
- def set_values(ds, tags, values, VR=None, coords=None):
124
-
125
- if np.isscalar(tags):
126
- tags = [tags]
127
- values = [values]
128
- VR = [VR]
129
- elif VR is None:
130
- VR = [None] * len(tags)
131
-
132
- if coords is not None:
133
- tags += list(coords.keys())
134
- values += list(coords.values())
135
-
136
- for i, tag in enumerate(tags):
137
-
138
- if values[i] is None:
139
- if isinstance(tag, str):
140
- if hasattr(ds, tag):
141
- del ds[tag]
142
- else: # hexadecimal tuple
143
- if tag in ds:
144
- del ds[tag]
145
-
146
- elif isinstance(tag, str):
147
- if hasattr(ds, tag):
148
- ds[tag].value = format_value(values[i], tag=tag)
149
- else:
150
- _add_new(ds, tag, values[i], VR=VR[i])
151
-
152
- else: # hexadecimal tuple
153
- if tag in ds:
154
- ds[tag].value = format_value(values[i], tag=tag)
155
- else:
156
- _add_new(ds, tag, values[i], VR=VR[i])
157
-
158
- #_set_derived_data_element(ds, tag, values[i])
159
-
160
- return ds
161
-
162
-
163
-
164
- def value(ds, tags):
165
- # Same as get_values but without VR lookup
166
-
167
- # https://pydicom.github.io/pydicom/stable/guides/element_value_types.html
168
- if np.isscalar(tags):
169
- return get_values(ds, [tags])[0]
170
-
171
- row = []
172
- for tag in tags:
173
- value = None
174
-
175
- # If the tag is provided as string
176
- if isinstance(tag, str):
177
-
178
- if hasattr(ds, tag):
179
- value = to_set_type(ds[tag].value)
180
-
181
- # If the tag is a tuple of hexadecimal values
182
- else:
183
- if tag in ds:
184
- value = to_set_type(ds[tag].value)
185
-
186
- # If a tag is not present in the dataset, check if it can be derived
187
- if value is None:
188
- value = derive_data_element(ds, tag)
189
-
190
- row.append(value)
191
- return row
192
-
193
-
194
- def set_value(ds, tags, values):
195
- # Same as set_values but without VR lookup
196
- # This excludes new private tags - set those using add_private()
197
- if np.isscalar(tags):
198
- tags = [tags]
199
- values = [values]
200
-
201
- for i, tag in enumerate(tags):
202
-
203
- if values[i] is None:
204
- if isinstance(tag, str):
205
- if hasattr(ds, tag):
206
- del ds[tag]
207
- else: # hexadecimal tuple
208
- if tag in ds:
209
- del ds[tag]
210
-
211
- elif isinstance(tag, str):
212
- if hasattr(ds, tag):
213
- ds[tag].value = check_value(values[i], tag)
214
- else:
215
- add_new(ds, tag, values[i])
216
-
217
- else: # hexadecimal tuple
218
- if tag in ds:
219
- ds[tag].value = check_value(values[i], tag)
220
- else:
221
- add_new(ds, tag, values[i])
222
-
223
- return ds
224
-
225
-
226
-
227
83
  def write(ds, file, status=None):
228
84
  # check if directory exists and create it if not
229
85
  dir = os.path.dirname(file)
@@ -268,164 +124,12 @@ def read_data(files, tags, path=None, images_only=False): # obsolete??
268
124
 
269
125
 
270
126
 
271
-
272
- def _add_new(ds, tag, value, VR='OW'):
273
- if not isinstance(tag, pydicom.tag.BaseTag):
274
- tag = pydicom.tag.Tag(tag)
275
- if not tag.is_private: # Add a new data element
276
- value_repr = pydicom.datadict.dictionary_VR(tag)
277
- if value_repr == 'US or SS':
278
- if value >= 0:
279
- value_repr = 'US'
280
- else:
281
- value_repr = 'SS'
282
- elif value_repr == 'OB or OW':
283
- value_repr = 'OW'
284
- ds.add_new(tag, value_repr, format_value(value, value_repr))
285
- else:
286
- if (tag.group, 0x0010) not in ds:
287
- ds.private_block(tag.group, 'dbdicom ' + str(tag.group), create=True)
288
- ds.add_new(tag, VR, format_value(value, VR))
289
-
290
-
291
- def add_new(ds, tag, value):
292
- if not isinstance(tag, pydicom.tag.BaseTag):
293
- tag = pydicom.tag.Tag(tag)
294
- if tag.is_private:
295
- raise ValueError("if you want to add a private data element, use "
296
- "dataset.add_private()")
297
- # Add a new data element
298
- value_repr = pydicom.datadict.dictionary_VR(tag)
299
- if value_repr == 'US or SS':
300
- if value >= 0:
301
- value_repr = 'US'
302
- else:
303
- value_repr = 'SS'
304
- elif value_repr == 'OB or OW':
305
- value_repr = 'OW'
306
- ds.add_new(tag, value_repr, format_value(value, value_repr))
307
-
308
-
309
-
310
- def add_private(ds, tag, value, VR):
311
- if not isinstance(tag, pydicom.tag.BaseTag):
312
- tag = pydicom.tag.Tag(tag)
313
- if (tag.group, 0x0010) not in ds:
314
- ds.private_block(tag.group, 'dbdicom ' + str(tag.group), create=True)
315
- ds.add_new(tag, VR, format_value(value, VR))
316
-
317
-
318
- def derive_data_element(ds, tag):
319
- """Tags that are not required but can be derived from other required tags"""
320
-
321
- if tag == 'SliceLocation' or tag == (0x0020, 0x1041):
322
- if 'ImageOrientationPatient' in ds and 'ImagePositionPatient' in ds:
323
- return image.slice_location(
324
- ds['ImageOrientationPatient'].value,
325
- ds['ImagePositionPatient'].value,
326
- )
327
- # To be extended ad hoc with other tags that can be derived
328
-
329
-
330
-
331
- def format_value(value, VR=None, tag=None):
332
-
333
- # If the change below is made (TM, DA, DT) then this needs to
334
- # convert those to string before setting
335
-
336
- # Slow - dictionary lookup for every value write
337
-
338
- if VR is None:
339
- VR = pydicom.datadict.dictionary_VR(tag)
340
-
341
- if VR == 'LO':
342
- if len(value) > 64:
343
- return value[-64:]
344
- #return value[:64]
345
- if VR == 'TM':
346
- return variables.seconds_to_str(value)
347
- if VR == 'DA':
348
- if not is_valid_dicom_date(value):
349
- return '99991231'
127
+ # def new_uid(n=None):
350
128
 
351
- return value
352
-
353
-
354
-
355
- def is_valid_dicom_date(da_str: str) -> bool:
356
- if not isinstance(da_str, str) or len(da_str) != 8 or not da_str.isdigit():
357
- return False
358
- try:
359
- datetime.strptime(da_str, "%Y%m%d")
360
- return True
361
- except ValueError:
362
- return False
363
-
364
-
365
- def check_value(value, tag):
366
-
367
- # If the change below is made (TM, DA, DT) then this needs to
368
- # convert those to string before setting
369
-
370
- LO = [
371
- 'SeriesDescription',
372
- 'StudyDescription',
373
- ]
374
- TM = [
375
- 'AcquisitionTime',
376
- ]
377
-
378
- if tag in LO:
379
- if len(value) > 64:
380
- return value[-64:]
381
- if tag in TM:
382
- return variables.seconds_to_str(value)
383
-
384
- return value
385
-
386
-
387
- def to_set_type(value, VR=None):
388
- """
389
- Convert pydicom datatypes to the python datatypes used to set the parameter.
390
- """
391
- # Not a good idea to modify pydicom set/get values. confusing and requires extra VR lookups
392
-
393
- if VR == 'TM':
394
- # pydicom sometimes returns string values for TM data types
395
- if isinstance(value, str):
396
- return variables.str_to_seconds(value)
397
-
398
- if value.__class__.__name__ == 'MultiValue':
399
- return [to_set_type(v, VR) for v in value]
400
- if value.__class__.__name__ == 'PersonName':
401
- return str(value)
402
- if value.__class__.__name__ == 'Sequence':
403
- return [ds for ds in value]
404
- if value.__class__.__name__ == 'TM':
405
- return variables.time_to_seconds(value) # return datetime.time
406
- if value.__class__.__name__ == 'UID':
407
- return str(value)
408
- if value.__class__.__name__ == 'IS':
409
- return int(value)
410
- if value.__class__.__name__ == 'DT':
411
- return variables.datetime_to_str(value) # return datetime.datetime
412
- if value.__class__.__name__ == 'DA': # return datetime.date
413
- return variables.date_to_str(value)
414
- if value.__class__.__name__ == 'DSfloat':
415
- return float(value)
416
- if value.__class__.__name__ == 'DSdecimal':
417
- return int(value)
418
-
419
- return value
420
-
421
-
422
- def new_uid(n=None):
423
-
424
- if n is None:
425
- return pydicom.uid.generate_uid()
426
- else:
427
- return [pydicom.uid.generate_uid() for _ in range(n)]
428
-
129
+ # if n is None:
130
+ # return pydicom.uid.generate_uid()
131
+ # else:
132
+ # return [pydicom.uid.generate_uid() for _ in range(n)]
429
133
 
430
134
 
431
135
 
@@ -638,6 +342,15 @@ def set_pixel_data(ds, array):
638
342
  def volume(ds):
639
343
  return vreg.volume(pixel_data(ds), affine(ds))
640
344
 
345
+
346
+
347
+ def is_valid_dicom_tag(value):
348
+ try:
349
+ tag = Tag(value)
350
+ return pydicom.datadict.dictionary_keyword(tag) != ''
351
+ except Exception:
352
+ return False
353
+
641
354
  def set_volume(ds, volume:vreg.Volume3D):
642
355
  if volume is None:
643
356
  raise ValueError('The volume cannot be set to an empty value.')
@@ -657,17 +370,18 @@ def set_volume(ds, volume:vreg.Volume3D):
657
370
  set_affine(ds, volume.affine)
658
371
  if volume.coords is not None:
659
372
  # All other dimensions should have size 1
660
- coords = volume.coords.reshape((volume.coords.shape[0], -1))
373
+ coords = [c.reshape(-1) for c in volume.coords]
661
374
  for i, d in enumerate(volume.dims):
662
- try:
663
- set_values(ds, d, coords[i,0])
664
- except KeyError:
375
+ if not is_valid_dicom_tag(d):
665
376
  raise ValueError(
666
377
  "Cannot write volume to DICOM. "
667
378
  f"Volume dimension {d} is not a recognized DICOM data-element. "
668
- f"Use Volume3D.set_dims() with proper DICOM keywords "
669
- "or (group, element) tags to change the dimensions."
379
+ f"Use Volume3D.set_dims() with proper DICOM "
380
+ "tags to change the dimensions."
670
381
  )
382
+ else:
383
+ set_values(ds, d, coords[i][0])
384
+
671
385
 
672
386
 
673
387
  def image_type(ds):