dbdicom 0.2.0__py3-none-any.whl → 0.3.16__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.
Files changed (72) hide show
  1. dbdicom/__init__.py +3 -25
  2. dbdicom/api.py +496 -0
  3. dbdicom/const.py +144 -0
  4. dbdicom/database.py +133 -0
  5. dbdicom/dataset.py +471 -0
  6. dbdicom/dbd.py +1290 -0
  7. dbdicom/external/__pycache__/__init__.cpython-311.pyc +0 -0
  8. dbdicom/external/dcm4che/__pycache__/__init__.cpython-311.pyc +0 -0
  9. dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-311.pyc +0 -0
  10. dbdicom/external/dcm4che/bin/emf2sf +57 -57
  11. dbdicom/register.py +402 -0
  12. dbdicom/{ds/types → sop_classes}/ct_image.py +2 -16
  13. dbdicom/{ds/types → sop_classes}/enhanced_mr_image.py +206 -160
  14. dbdicom/sop_classes/mr_image.py +338 -0
  15. dbdicom/sop_classes/parametric_map.py +381 -0
  16. dbdicom/sop_classes/secondary_capture.py +140 -0
  17. dbdicom/sop_classes/segmentation.py +311 -0
  18. dbdicom/{ds/types → sop_classes}/ultrasound_multiframe_image.py +1 -15
  19. dbdicom/{ds/types → sop_classes}/xray_angiographic_image.py +2 -17
  20. dbdicom/utils/arrays.py +142 -0
  21. dbdicom/utils/files.py +0 -20
  22. dbdicom/utils/image.py +43 -466
  23. dbdicom/utils/pydicom_dataset.py +386 -0
  24. dbdicom-0.3.16.dist-info/METADATA +26 -0
  25. dbdicom-0.3.16.dist-info/RECORD +54 -0
  26. {dbdicom-0.2.0.dist-info → dbdicom-0.3.16.dist-info}/WHEEL +1 -1
  27. dbdicom/create.py +0 -450
  28. dbdicom/ds/__init__.py +0 -10
  29. dbdicom/ds/create.py +0 -63
  30. dbdicom/ds/dataset.py +0 -841
  31. dbdicom/ds/dictionaries.py +0 -620
  32. dbdicom/ds/types/mr_image.py +0 -267
  33. dbdicom/ds/types/parametric_map.py +0 -226
  34. dbdicom/external/__pycache__/__init__.cpython-310.pyc +0 -0
  35. dbdicom/external/__pycache__/__init__.cpython-37.pyc +0 -0
  36. dbdicom/external/dcm4che/__pycache__/__init__.cpython-310.pyc +0 -0
  37. dbdicom/external/dcm4che/__pycache__/__init__.cpython-37.pyc +0 -0
  38. dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-310.pyc +0 -0
  39. dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-37.pyc +0 -0
  40. dbdicom/external/dcm4che/lib/linux-x86/libclib_jiio.so +0 -0
  41. dbdicom/external/dcm4che/lib/linux-x86-64/libclib_jiio.so +0 -0
  42. dbdicom/external/dcm4che/lib/linux-x86-64/libopencv_java.so +0 -0
  43. dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio.so +0 -0
  44. dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis.so +0 -0
  45. dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis2.so +0 -0
  46. dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio.so +0 -0
  47. dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis.so +0 -0
  48. dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis2.so +0 -0
  49. dbdicom/external/dcm4che/lib/solaris-x86/libclib_jiio.so +0 -0
  50. dbdicom/external/dcm4che/lib/solaris-x86-64/libclib_jiio.so +0 -0
  51. dbdicom/manager.py +0 -2077
  52. dbdicom/message.py +0 -119
  53. dbdicom/record.py +0 -1526
  54. dbdicom/types/database.py +0 -107
  55. dbdicom/types/instance.py +0 -184
  56. dbdicom/types/patient.py +0 -40
  57. dbdicom/types/series.py +0 -816
  58. dbdicom/types/study.py +0 -58
  59. dbdicom/utils/variables.py +0 -155
  60. dbdicom/utils/vreg.py +0 -2626
  61. dbdicom/wrappers/__init__.py +0 -7
  62. dbdicom/wrappers/dipy.py +0 -462
  63. dbdicom/wrappers/elastix.py +0 -855
  64. dbdicom/wrappers/numpy.py +0 -119
  65. dbdicom/wrappers/scipy.py +0 -1413
  66. dbdicom/wrappers/skimage.py +0 -1030
  67. dbdicom/wrappers/sklearn.py +0 -151
  68. dbdicom/wrappers/vreg.py +0 -273
  69. dbdicom-0.2.0.dist-info/METADATA +0 -276
  70. dbdicom-0.2.0.dist-info/RECORD +0 -81
  71. {dbdicom-0.2.0.dist-info → dbdicom-0.3.16.dist-info/licenses}/LICENSE +0 -0
  72. {dbdicom-0.2.0.dist-info → dbdicom-0.3.16.dist-info}/top_level.txt +0 -0
dbdicom/__init__.py CHANGED
@@ -1,26 +1,4 @@
1
+ from dbdicom.api import *
1
2
 
2
- from .create import (
3
- database,
4
- database_hollywood,
5
- patient,
6
- study,
7
- series,
8
- as_series,
9
- zeros,
10
- )
11
- from .record import (
12
- copy_to,
13
- move_to,
14
- group,
15
- merge,
16
- )
17
- from .types.series import (
18
- array
19
- )
20
- from .record import Record
21
- from .types.database import Database
22
- from .types.patient import Patient
23
- from .types.study import Study
24
- from .types.series import Series
25
-
26
- from .utils import image
3
+ # Utilities
4
+ from dbdicom.utils.image import affine_matrix
dbdicom/api.py ADDED
@@ -0,0 +1,496 @@
1
+ import os
2
+ import shutil
3
+ import zipfile
4
+ from pathlib import Path
5
+ from typing import Union
6
+ from tqdm import tqdm
7
+ import numpy as np
8
+ import vreg
9
+
10
+ from dbdicom.dbd import DataBaseDicom
11
+
12
+
13
+
14
+
15
+ def open(path:str) -> DataBaseDicom:
16
+ """Open a DICOM database
17
+
18
+ Args:
19
+ path (str): path to the DICOM folder
20
+
21
+ Returns:
22
+ DataBaseDicom: database instance.
23
+ """
24
+ return DataBaseDicom(path)
25
+
26
+ def to_json(path):
27
+ """Summarise the contents of the DICOM folder in a json file
28
+
29
+ Args:
30
+ path (str): path to the DICOM folder
31
+ """
32
+ dbd = open(path)
33
+ dbd.close()
34
+
35
+ def print(path):
36
+ """Print the contents of the DICOM folder
37
+
38
+ Args:
39
+ path (str): path to the DICOM folder
40
+ """
41
+ dbd = open(path)
42
+ dbd.print()
43
+ dbd.close()
44
+
45
+
46
+ def summary(path) -> dict:
47
+ """Return a summary of the contents of the database.
48
+
49
+ Args:
50
+ path (str): path to the DICOM folder
51
+
52
+ Returns:
53
+ dict: Nested dictionary with summary information on the database.
54
+ """
55
+ dbd = open(path)
56
+ s = dbd.summary()
57
+ dbd.close()
58
+ return s
59
+
60
+
61
+ def tree(path) -> dict:
62
+ """Return the structure of the database as a dictionary tree.
63
+
64
+ Args:
65
+ path (str): path to the DICOM folder
66
+
67
+ Returns:
68
+ dict: Nested dictionary with summary information on the database.
69
+ """
70
+ dbd = open(path)
71
+ s = dbd.register
72
+ dbd.close()
73
+ return s
74
+
75
+
76
+ def to_csv(path, csv_file) -> dict:
77
+ """Write a summary of the contents of the database to csv.
78
+
79
+ Args:
80
+ path (str): path to the DICOM folder
81
+ csv_file (str): path to the csv file
82
+ """
83
+ dbd = open(path)
84
+ dbd.to_csv(csv_file)
85
+ dbd.close()
86
+
87
+
88
+ def patients(path, name:str=None, contains:str=None, isin:list=None)->list:
89
+ """Return a list of patients in the DICOM folder.
90
+
91
+ Args:
92
+ path (str): path to the DICOM folder
93
+ name (str, optional): value of PatientName, to search for
94
+ individuals with a given name. Defaults to None.
95
+ contains (str, optional): substring of PatientName, to
96
+ search for individuals based on part of their name.
97
+ Defaults to None.
98
+ isin (list, optional): List of PatientName values, to search
99
+ for patients whose name is in the list. Defaults to None.
100
+
101
+ Returns:
102
+ list: list of patients fulfilling the criteria.
103
+ """
104
+ dbd = open(path)
105
+ p = dbd.patients(name, contains, isin)
106
+ dbd.close()
107
+ return p
108
+
109
+
110
+ def studies(entity:str | list, desc:str=None, contains:str=None, isin:list=None)->list:
111
+ """Return a list of studies in the DICOM folder.
112
+
113
+ Args:
114
+ entity (str or list): path to a DICOM folder (to search in
115
+ the whole folder), or a two-element list identifying a
116
+ patient (to search studies of a given patient).
117
+ desc (str, optional): value of StudyDescription, to search for
118
+ studies with a given description. Defaults to None.
119
+ contains (str, optional): substring of StudyDescription, to
120
+ search for studies based on part of their description.
121
+ Defaults to None.
122
+ isin (list, optional): List of StudyDescription values, to search
123
+ for studies whose description is in a list. Defaults to None.
124
+
125
+ Returns:
126
+ list: list of studies fulfilling the criteria.
127
+ """
128
+ if isinstance(entity, str): # path = folder
129
+ dbd = open(entity)
130
+ s = dbd.studies(entity, desc, contains, isin)
131
+ dbd.close()
132
+ return s
133
+ elif len(entity)==2: # path = patient
134
+ dbd = open(entity[0])
135
+ s = dbd.studies(entity, desc, contains, isin)
136
+ dbd.close()
137
+ return s
138
+ else:
139
+ raise ValueError(
140
+ "The path must be a folder or a 2-element list "
141
+ "with a folder and a patient name."
142
+ )
143
+
144
+ def series(entity:str | list, desc:str=None, contains:str=None, isin:list=None)->list:
145
+ """Return a list of series in the DICOM folder.
146
+
147
+ Args:
148
+ entity (str or list): path to a DICOM folder (to search in
149
+ the whole folder), or a list identifying a
150
+ patient or a study (to search series of a given patient
151
+ or study).
152
+ desc (str, optional): value of SeriesDescription, to search for
153
+ series with a given description. Defaults to None.
154
+ contains (str, optional): substring of SeriesDescription, to
155
+ search for series based on part of their description.
156
+ Defaults to None.
157
+ isin (list, optional): List of SeriesDescription values, to search
158
+ for series whose description is in a list. Defaults to None.
159
+
160
+ Returns:
161
+ list: list of series fulfilling the criteria.
162
+ """
163
+ if isinstance(entity, str): # path = folder
164
+ dbd = open(entity)
165
+ s = dbd.series(entity, desc, contains, isin)
166
+ dbd.close()
167
+ return s
168
+ elif len(entity) in [2,3]:
169
+ dbd = open(entity[0])
170
+ s = dbd.series(entity, desc, contains, isin)
171
+ dbd.close()
172
+ return s
173
+ else:
174
+ raise ValueError(
175
+ "To retrieve a series, the entity must be a database, patient or study."
176
+ )
177
+
178
+ def copy(from_entity:list, to_entity=None):
179
+ """Copy a DICOM entity (patient, study or series)
180
+
181
+ Args:
182
+ from_entity (list): entity to copy
183
+ to_entity (list, optional): entity after copying. If this is not
184
+ provided, a copy will be made in the same study and returned.
185
+
186
+ Returns:
187
+ entity: the copied entity. If th to_entity is provided, this is
188
+ returned.
189
+ """
190
+ dbd = open(from_entity[0])
191
+ from_entity_copy = dbd.copy(from_entity, to_entity)
192
+ dbd.close()
193
+ return from_entity_copy
194
+
195
+
196
+ def delete(entity:list, not_exists_ok=False):
197
+ """Delete a DICOM entity
198
+
199
+ Args:
200
+ entity (list): entity to delete
201
+ not_exists_ok (bool): By default, an exception is raised when attempting
202
+ to delete an entity that does not exist. Set this to True to pass over this silently.
203
+ """
204
+ dbd = open(entity[0])
205
+ dbd.delete(entity, not_exists_ok)
206
+ dbd.close()
207
+
208
+
209
+ def move(from_entity:list, to_entity:list):
210
+ """Move a DICOM entity
211
+
212
+ Args:
213
+ entity (list): entity to move
214
+ """
215
+ dbd = open(from_entity[0])
216
+ dbd.copy(from_entity, to_entity)
217
+ dbd.delete(from_entity)
218
+ dbd.close()
219
+
220
+ def split_series(series:list, attr:Union[str, tuple], key=None)->list:
221
+ """
222
+ Split a series into multiple series
223
+
224
+ Args:
225
+ series (list): series to split.
226
+ attr (str or tuple): dicom attribute to split the series by.
227
+ key (function): split by by key(attr)
228
+ Returns:
229
+ list: list of two-element tuples, where the first element is
230
+ is the value and the second element is the series corresponding to that value.
231
+ """
232
+ dbd = open(series[0])
233
+ split_series = dbd.split_series(series, attr, key)
234
+ dbd.close()
235
+ return split_series
236
+
237
+
238
+ def volume(series:list, dims:list=None, verbose=1, **kwargs) -> vreg.Volume3D:
239
+ """Read volume from a series.
240
+
241
+ Args:
242
+ series (list, str): DICOM entity to read
243
+ dims (list, optional): Non-spatial dimensions of the volume. Defaults to None.
244
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
245
+ kwargs (dict, optional): keywords to filter the series.
246
+
247
+ Returns:
248
+ vreg.Volume3D.
249
+ """
250
+ dbd = open(series[0])
251
+ vol = dbd.volume(series, dims, verbose, **kwargs)
252
+ dbd.close()
253
+ return vol
254
+
255
+
256
+ def slices(series:list, dims:list=None, verbose=1) -> vreg.Volume3D:
257
+ """Read 2D volumes from the series
258
+
259
+ Args:
260
+ entity (list, str): DICOM series to read
261
+ dims (list, optional): Non-spatial dimensions of the volume. Defaults to None.
262
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
263
+
264
+ Returns:
265
+ list of vreg.Volume3D
266
+ """
267
+ dbd = open(series[0])
268
+ vol = dbd.slices(series, dims, verbose)
269
+ dbd.close()
270
+ return vol
271
+
272
+
273
+ # Obsolete API - phase out
274
+ def volumes_2d(*args, **kwargs):
275
+ return slices(*args, **kwargs)
276
+
277
+
278
+ def values(series:list, *attr, dims:list=None, verbose=1) -> Union[np.ndarray, list]:
279
+ """Read the values of some attributes from a DICOM series
280
+
281
+ Args:
282
+ series (list): DICOM series to read.
283
+ attr (tuple, optional): DICOM attributes to read.
284
+ dims (list, optional): Dimensions to sort the values.
285
+ If dims is not provided, values are sorted by
286
+ InstanceNumber.
287
+
288
+ Returns:
289
+ tuple: arrays with values for the attributes.
290
+ """
291
+ dbd = open(series[0])
292
+ values = dbd.values(series, *attr, dims=dims, verbose=verbose)
293
+ dbd.close()
294
+ return values
295
+
296
+
297
+
298
+ def write_volume(vol:Union[vreg.Volume3D, tuple], series:list,
299
+ ref:list=None, append=False, verbose=1, **kwargs):
300
+ """Write a vreg.Volume3D to a DICOM series
301
+
302
+ Args:
303
+ vol (vreg.Volume3D or tuple): Volume to write to the series.
304
+ series (list): DICOM series to read
305
+ dims (list, optional): Non-spatial dimensions of the volume. Defaults to None.
306
+ append (bool): by default write_volume will only write to a new series,
307
+ and raise an error when attempting to write to an existing series.
308
+ To overrule this behaviour and add the volume to an existing series, set append to True.
309
+ Default is False.
310
+ verbose (bool): if set to 1, a progress bar is shown. verbose=0 does not show updates.
311
+ kwargs: Keyword-value pairs to be set on the fly
312
+ """
313
+ dbd = open(series[0])
314
+ dbd.write_volume(vol, series, ref=ref, append=append, verbose=verbose, **kwargs)
315
+ dbd.close()
316
+
317
+
318
+ def edit(series:list, new_values:dict, dims:list=None, verbose=1):
319
+ """Edit attribute values in a DICOM series
320
+
321
+ Warning: this function edits all values as requested. Please take care
322
+ when editing attributes that affect the DICOM file organisation, such as
323
+ UIDs, as this could corrupt the database.
324
+
325
+ Args:
326
+ series (list): DICOM series to edit
327
+ new_values (dict): dictionary with attribute: value pairs to write to the series
328
+ dims (list, optional): Non-spatial dimensions of the volume. Defaults to None.
329
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
330
+
331
+ """
332
+ dbd = open(series[0])
333
+ dbd.edit(series, new_values, dims=dims, verbose=verbose)
334
+ dbd.close()
335
+
336
+ def to_nifti(series:list, file:str, dims:list=None, verbose=1):
337
+ """Save a DICOM series in nifti format.
338
+
339
+ Args:
340
+ series (list): DICOM series to read
341
+ file (str): file path of the nifti file.
342
+ dims (list, optional): Non-spatial dimensions of the volume.
343
+ Defaults to None.
344
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
345
+ """
346
+ dbd = open(series[0])
347
+ dbd.to_nifti(series, file, dims, verbose)
348
+ dbd.close()
349
+
350
+ def from_nifti(file:str, series:list, ref:list=None):
351
+ """Create a DICOM series from a nifti file.
352
+
353
+ Args:
354
+ file (str): file path of the nifti file.
355
+ series (list): DICOM series to create
356
+ ref (list): DICOM series to use as template.
357
+ """
358
+ dbd = open(series[0])
359
+ dbd.from_nifti(file, series, ref)
360
+ dbd.close()
361
+
362
+
363
+ def files(entity:list) -> list:
364
+ """Read the files in a DICOM entity
365
+
366
+ Args:
367
+ entity (list or str): DICOM entity to read. This can
368
+ be a path to a folder containing DICOM files, or a
369
+ patient or study to read all series in that patient or
370
+ study.
371
+
372
+ Returns:
373
+ list: list of valid dicom files.
374
+ """
375
+ if isinstance(entity, str):
376
+ entity = [entity]
377
+ dbd = open(entity[0])
378
+ files = dbd.files(entity)
379
+ dbd.close()
380
+ return files
381
+
382
+
383
+ def pixel_data(series:list, dims:list=None, verbose=1) -> tuple:
384
+ """Read the pixel data from a DICOM series
385
+
386
+ Args:
387
+ series (list or str): DICOM series to read. This can also
388
+ be a path to a folder containing DICOM files, or a
389
+ patient or study to read all series in that patient or
390
+ study. In those cases a list is returned.
391
+ dims (list, optional): Dimensions of the array.
392
+
393
+ Returns:
394
+ numpy.ndarray or tuple: numpy array with pixel values, with
395
+ at least 3 dimensions (x,y,z).
396
+ """
397
+ if isinstance(series, str):
398
+ series = [series]
399
+ dbd = open(series[0])
400
+ array = dbd.pixel_data(series, dims, verbose)
401
+ dbd.close()
402
+ return array
403
+
404
+
405
+ def unique(pars:list, entity:list) -> dict:
406
+ """Return a list of unique values for a DICOM entity
407
+
408
+ Args:
409
+ pars (list, str/tuple): attribute or attributes to return.
410
+ entity (list): DICOM entity to search (Patient, Study or Series)
411
+
412
+ Returns:
413
+ dict: if a pars is a list, this returns a dictionary with
414
+ unique values for each attribute. If pars is a scalar this returnes a list of values
415
+ """
416
+ dbd = open(entity[0])
417
+ u = dbd.unique(pars, entity)
418
+ dbd.close()
419
+ return u
420
+
421
+
422
+ def archive(path, archive_path):
423
+ dbd = open(path)
424
+ dbd.archive(archive_path)
425
+ dbd.close()
426
+
427
+
428
+ def restore(archive_path, path):
429
+ _copy_and_extract_zips(archive_path, path)
430
+ dbd = open(path)
431
+ dbd.close()
432
+
433
+
434
+ def _copy_and_extract_zips(src_folder, dest_folder):
435
+ if not os.path.exists(dest_folder):
436
+ os.makedirs(dest_folder)
437
+
438
+ # First pass: count total files
439
+ total_files = sum(len(files) for _, _, files in os.walk(src_folder))
440
+
441
+ with tqdm(total=total_files, desc="Copying and extracting") as pbar:
442
+ for root, dirs, files in os.walk(src_folder):
443
+ rel_path = os.path.relpath(root, src_folder)
444
+ dest_path = os.path.join(dest_folder, rel_path)
445
+ os.makedirs(dest_path, exist_ok=True)
446
+
447
+ for file in files:
448
+ src_file_path = os.path.join(root, file)
449
+ dest_file_path = os.path.join(dest_path, file)
450
+
451
+ if file.lower().endswith('.zip'):
452
+ try:
453
+ zip_dest_folder = dest_file_path[:-4]
454
+ if os.path.exists(zip_dest_folder):
455
+ continue
456
+ with zipfile.ZipFile(src_file_path, 'r') as zip_ref:
457
+ zip_ref.extractall(zip_dest_folder)
458
+ #tqdm.write(f"Extracted ZIP: {src_file_path}")
459
+ #_flatten_folder(zip_dest_folder) # still needed?
460
+ except zipfile.BadZipFile:
461
+ tqdm.write(f"Bad ZIP file skipped: {src_file_path}")
462
+ else:
463
+ if os.path.exists(dest_file_path):
464
+ continue
465
+ shutil.copy2(src_file_path, dest_file_path)
466
+
467
+ pbar.update(1)
468
+
469
+
470
+ def _flatten_folder(root_folder):
471
+ for dirpath, dirnames, filenames in os.walk(root_folder, topdown=False):
472
+ for filename in filenames:
473
+ src_path = os.path.join(dirpath, filename)
474
+ dst_path = os.path.join(root_folder, filename)
475
+
476
+ # If file with same name exists, optionally rename or skip
477
+ if os.path.exists(dst_path):
478
+ base, ext = os.path.splitext(filename)
479
+ counter = 1
480
+ while os.path.exists(dst_path):
481
+ dst_path = os.path.join(root_folder, f"{base}_{counter}{ext}")
482
+ counter += 1
483
+
484
+ shutil.move(src_path, dst_path)
485
+
486
+ # Remove empty subdirectories (but skip the root folder)
487
+ if dirpath != root_folder:
488
+ try:
489
+ os.rmdir(dirpath)
490
+ except OSError:
491
+ print(f"Could not remove {dirpath} — not empty or in use.")
492
+
493
+
494
+
495
+ if __name__=='__main__':
496
+ pass
dbdicom/const.py ADDED
@@ -0,0 +1,144 @@
1
+
2
+
3
+ PATIENT_MODULE = [
4
+ 'ReferencedPatientSequence',
5
+ 'PatientName',
6
+ 'PatientID',
7
+ 'IssuerOfPatientID',
8
+ 'TypeOfPatientID',
9
+ 'IssuerOfPatientIDQualifiersSequence',
10
+ 'SourcePatientGroupIdentificationSequence',
11
+ 'GroupOfPatientsIdentificationSequence',
12
+ 'PatientBirthDate',
13
+ 'PatientBirthTime',
14
+ 'PatientBirthDateInAlternativeCalendar',
15
+ 'PatientDeathDateInAlternativeCalendar',
16
+ 'PatientAlternativeCalendar',
17
+ 'PatientSex',
18
+ 'QualityControlSubject',
19
+ 'StrainDescription',
20
+ 'StrainNomenclature',
21
+ 'StrainStockSequence',
22
+ 'StrainAdditionalInformation',
23
+ 'StrainCodeSequence',
24
+ 'GeneticModificationsSequence',
25
+ 'OtherPatientNames',
26
+ 'OtherPatientIDsSequence',
27
+ 'ReferencedPatientPhotoSequence',
28
+ 'EthnicGroup',
29
+ 'PatientSpeciesDescription',
30
+ 'PatientSpeciesCodeSequence',
31
+ 'PatientBreedDescription',
32
+ 'PatientBreedCodeSequence',
33
+ 'BreedRegistrationSequence',
34
+ 'ResponsiblePerson',
35
+ 'ResponsiblePersonRole',
36
+ 'ResponsibleOrganization',
37
+ 'PatientComments',
38
+ 'PatientIdentityRemoved',
39
+ 'DeidentificationMethod',
40
+ 'DeidentificationMethodCodeSequence',
41
+ 'ClinicalTrialSponsorName',
42
+ 'ClinicalTrialProtocolID',
43
+ 'ClinicalTrialProtocolName',
44
+ 'ClinicalTrialSiteID',
45
+ 'ClinicalTrialSiteName',
46
+ 'ClinicalTrialSubjectID',
47
+ 'ClinicalTrialSubjectReadingID',
48
+ 'ClinicalTrialProtocolEthicsCommitteeName',
49
+ 'ClinicalTrialProtocolEthicsCommitteeApprovalNumber',
50
+ ]
51
+
52
+
53
+
54
+ STUDY_MODULE = [
55
+ 'StudyDate',
56
+ 'StudyTime',
57
+ 'AccessionNumber',
58
+ 'IssuerOfAccessionNumberSequence',
59
+ 'ReferringPhysicianName',
60
+ 'ReferringPhysicianIdentificationSequence',
61
+ 'ConsultingPhysicianName',
62
+ 'ConsultingPhysicianIdentificationSequence',
63
+ 'StudyDescription',
64
+ 'ProcedureCodeSequence',
65
+ 'PhysiciansOfRecord',
66
+ 'PhysiciansOfRecordIdentificationSequence',
67
+ 'NameOfPhysiciansReadingStudy',
68
+ 'PhysiciansReadingStudyIdentificationSequence',
69
+ 'ReferencedStudySequence',
70
+ 'StudyInstanceUID',
71
+ 'StudyID',
72
+ 'RequestingService',
73
+ 'RequestingServiceCodeSequence',
74
+ 'ReasonForPerformedProcedureCodeSequence',
75
+ 'AdmittingDiagnosesDescription',
76
+ 'AdmittingDiagnosesCodeSequence',
77
+ 'PatientAge',
78
+ 'PatientSize',
79
+ 'PatientSizeCodeSequence',
80
+ 'PatientBodyMassIndex',
81
+ 'MeasuredAPDimension',
82
+ 'MeasuredLateralDimension',
83
+ 'PatientWeight',
84
+ 'MedicalAlerts',
85
+ 'Allergies',
86
+ 'Occupation',
87
+ 'SmokingStatus',
88
+ 'AdditionalPatientHistory',
89
+ 'PregnancyStatus',
90
+ 'LastMenstrualDate',
91
+ 'PatientSexNeutered',
92
+ 'ReasonForVisit',
93
+ 'ReasonForVisitCodeSequence',
94
+ 'AdmissionID',
95
+ 'IssuerOfAdmissionIDSequence',
96
+ 'ServiceEpisodeID',
97
+ 'ServiceEpisodeDescription',
98
+ 'IssuerOfServiceEpisodeIDSequence',
99
+ 'PatientState',
100
+ 'ClinicalTrialTimePointID',
101
+ 'ClinicalTrialTimePointDescription',
102
+ 'LongitudinalTemporalOffsetFromEvent',
103
+ 'LongitudinalTemporalEventType',
104
+ 'ConsentForClinicalTrialUseSequence',
105
+ ]
106
+
107
+
108
+
109
+ SERIES_MODULE = [
110
+ 'SeriesDate',
111
+ 'SeriesTime',
112
+ 'Modality',
113
+ 'SeriesDescription',
114
+ 'SeriesDescriptionCodeSequence',
115
+ 'PerformingPhysicianName',
116
+ 'PerformingPhysicianIdentificationSequence',
117
+ 'OperatorsName',
118
+ 'OperatorIdentificationSequence',
119
+ 'ReferencedPerformedProcedureStepSequence',
120
+ 'RelatedSeriesSequence',
121
+ 'AnatomicalOrientationType',
122
+ 'BodyPartExamined',
123
+ 'ProtocolName',
124
+ 'PatientPosition',
125
+ 'ReferencedDefinedProtocolSequence',
126
+ 'ReferencedPerformedProtocolSequence',
127
+ 'SeriesInstanceUID',
128
+ 'SeriesNumber',
129
+ 'Laterality',
130
+ 'SmallestPixelValueInSeries',
131
+ 'LargestPixelValueInSeries',
132
+ 'PerformedProcedureStepStartDate',
133
+ 'PerformedProcedureStepStartTime',
134
+ 'PerformedProcedureStepEndDate',
135
+ 'PerformedProcedureStepEndTime',
136
+ 'PerformedProcedureStepID',
137
+ 'PerformedProcedureStepDescription',
138
+ 'PerformedProtocolCodeSequence',
139
+ 'RequestAttributesSequence',
140
+ 'CommentsOnThePerformedProcedureStep',
141
+ 'ClinicalTrialCoordinatingCenterName',
142
+ 'ClinicalTrialSeriesID',
143
+ 'ClinicalTrialSeriesDescription',
144
+ ]