dbdicom 0.3.8__py3-none-any.whl → 0.3.9__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/dbd.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import os
2
- from datetime import datetime
3
2
  import json
4
3
  from typing import Union
5
4
  import zipfile
@@ -9,12 +8,18 @@ from tqdm import tqdm
9
8
  import numpy as np
10
9
  import vreg
11
10
  from pydicom.dataset import Dataset
11
+ import pydicom
12
12
 
13
13
  import dbdicom.utils.arrays
14
14
  import dbdicom.dataset as dbdataset
15
15
  import dbdicom.database as dbdatabase
16
16
  import dbdicom.register as register
17
17
  import dbdicom.const as const
18
+ from dbdicom.utils.pydicom_dataset import (
19
+ get_values,
20
+ set_values,
21
+ set_value,
22
+ )
18
23
 
19
24
 
20
25
 
@@ -201,12 +206,13 @@ class DataBaseDicom():
201
206
  return register.series(self.register, entity, desc, contains, isin)
202
207
 
203
208
 
204
- def volume(self, entity:Union[list, str], dims:list=None) -> Union[vreg.Volume3D, list]:
209
+ def volume(self, entity:Union[list, str], dims:list=None, verbose=1) -> Union[vreg.Volume3D, list]:
205
210
  """Read volume or volumes.
206
211
 
207
212
  Args:
208
213
  entity (list, str): DICOM entity to read
209
214
  dims (list, optional): Non-spatial dimensions of the volume. Defaults to None.
215
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
210
216
 
211
217
  Returns:
212
218
  vreg.Volume3D | list: If the entity is a series this returns
@@ -229,12 +235,14 @@ class DataBaseDicom():
229
235
  # Read dicom files
230
236
  values = []
231
237
  volumes = []
232
- for f in tqdm(files, desc='Reading volume..'):
233
- ds = dbdataset.read_dataset(f)
234
- values.append(dbdataset.get_values(ds, dims))
238
+ for f in tqdm(files, desc='Reading volume..', disable=(verbose==0)):
239
+ ds = pydicom.dcmread(f)
240
+ values.append(get_values(ds, dims))
235
241
  volumes.append(dbdataset.volume(ds))
236
242
 
237
243
  # Format as mesh
244
+ # coords = np.stack(values, axis=-1, dtype=object)
245
+ values = [np.array(v, dtype=object) for v in values] # object array to allow for mixed types
238
246
  coords = np.stack(values, axis=-1)
239
247
  coords, inds = dbdicom.utils.arrays.meshvals(coords)
240
248
  vols = np.array(volumes)
@@ -292,7 +300,7 @@ class DataBaseDicom():
292
300
  ref_mgr = DataBaseDicom(ref[0])
293
301
  files = register.files(ref_mgr.register, ref)
294
302
  ref_mgr.close()
295
- ds = dbdataset.read_dataset(files[0])
303
+ ds = pydicom.dcmread(files[0])
296
304
 
297
305
  # Get the attributes of the destination series
298
306
  attr = self._series_attributes(series)
@@ -307,15 +315,17 @@ class DataBaseDicom():
307
315
  i=0
308
316
  vols = vol.separate().reshape(-1)
309
317
  for vt in tqdm(vols, desc='Writing volume..'):
310
- for sl in vt.split():
318
+ slices = vt.split()
319
+ for sl in slices:
311
320
  dbdataset.set_volume(ds, sl)
312
- dbdataset.set_value(ds, sl.dims, sl.coords[:,...])
321
+ sl_coords = [sl.coords[i,...].ravel()[0] for i in range(len(sl.dims))]
322
+ set_value(ds, sl.dims, sl_coords)
313
323
  self._write_dataset(ds, attr, n + 1 + i)
314
324
  i+=1
315
325
  return self
316
326
 
317
327
 
318
- def to_nifti(self, series:list, file:str, dims=None):
328
+ def to_nifti(self, series:list, file:str, dims=None, verbose=1):
319
329
  """Save a DICOM series in nifti format.
320
330
 
321
331
  Args:
@@ -323,8 +333,10 @@ class DataBaseDicom():
323
333
  file (str): file path of the nifti file.
324
334
  dims (list, optional): Non-spatial dimensions of the volume.
325
335
  Defaults to None.
336
+ verbose (bool, optional): If set to 1, shows progress bar. Defaults to 1.
337
+
326
338
  """
327
- vol = self.volume(series, dims)
339
+ vol = self.volume(series, dims, verbose)
328
340
  vreg.write_nifti(vol, file)
329
341
  return self
330
342
 
@@ -390,12 +402,12 @@ class DataBaseDicom():
390
402
  if attr is not None:
391
403
  values = np.empty(len(files), dtype=dict)
392
404
  for i, f in tqdm(enumerate(files), desc='Reading pixel data..'):
393
- ds = dbdataset.read_dataset(f)
394
- coords_array.append(dbdataset.get_values(ds, dims))
405
+ ds = pydicom.dcmread(f)
406
+ coords_array.append(get_values(ds, dims))
395
407
  # save as dict so numpy does not stack as arrays
396
408
  arrays[i] = {'pixel_data': dbdataset.pixel_data(ds)}
397
409
  if attr is not None:
398
- values[i] = {'values': dbdataset.get_values(ds, params)}
410
+ values[i] = {'values': get_values(ds, params)}
399
411
 
400
412
  # Format as mesh
401
413
  coords_array = np.stack([v for v in coords_array], axis=-1)
@@ -483,7 +495,7 @@ class DataBaseDicom():
483
495
  if attr is None:
484
496
  # If attributes are not provided, read all
485
497
  # attributes from the first file
486
- ds = dbdataset.read_dataset(files[0])
498
+ ds = pydicom.dcmread(files[0])
487
499
  exclude = ['PixelData', 'FloatPixelData', 'DoubleFloatPixelData']
488
500
  params = []
489
501
  param_labels = []
@@ -504,10 +516,10 @@ class DataBaseDicom():
504
516
  coords_array = []
505
517
  values = np.empty(len(files), dtype=dict)
506
518
  for i, f in tqdm(enumerate(files), desc='Reading values..'):
507
- ds = dbdataset.read_dataset(f)
508
- coords_array.append(dbdataset.get_values(ds, dims))
519
+ ds = pydicom.dcmread(f)
520
+ coords_array.append(get_values(ds, dims))
509
521
  # save as dict so numpy does not stack as arrays
510
- values[i] = {'values': dbdataset.get_values(ds, params)}
522
+ values[i] = {'values': get_values(ds, params)}
511
523
 
512
524
  # Format as mesh
513
525
  coords_array = np.stack([v for v in coords_array], axis=-1)
@@ -670,8 +682,8 @@ class DataBaseDicom():
670
682
  files = []
671
683
  values = []
672
684
  for f in tqdm(all_files, desc=f'Reading {attr}'):
673
- ds = dbdataset.read_dataset(f)
674
- v = dbdataset.get_values(ds, attr)
685
+ ds = pydicom.dcmread(f)
686
+ v = get_values(ds, attr)
675
687
  if key is not None:
676
688
  v = key(v)
677
689
  if v in values:
@@ -701,8 +713,8 @@ class DataBaseDicom():
701
713
  files = register.files(self.register, entity)
702
714
  v = np.empty((len(files), len(attributes)), dtype=object)
703
715
  for i, f in enumerate(files):
704
- ds = dbdataset.read_dataset(f)
705
- v[i,:] = dbdataset.get_values(ds, attributes)
716
+ ds = pydicom.dcmread(f)
717
+ v[i,:] = get_values(ds, attributes)
706
718
  return v
707
719
 
708
720
  def _copy_patient(self, from_patient, to_patient):
@@ -757,7 +769,7 @@ class DataBaseDicom():
757
769
  # Copy the files to the new series
758
770
  for i, f in tqdm(enumerate(files), total=len(files), desc=f'Copying series {to_series[1:]}'):
759
771
  # Read dataset and assign new properties
760
- ds = dbdataset.read_dataset(f)
772
+ ds = pydicom.dcmread(f)
761
773
  self._write_dataset(ds, attr, n + 1 + i)
762
774
 
763
775
  def _max_study_id(self, patient_id):
@@ -807,8 +819,8 @@ class DataBaseDicom():
807
819
  # If the patient exists and has files, read from file
808
820
  files = register.files(self.register, patient)
809
821
  attr = const.PATIENT_MODULE
810
- ds = dbdataset.read_dataset(files[0])
811
- vals = dbdataset.get_values(ds, attr)
822
+ ds = pydicom.dcmread(files[0])
823
+ vals = get_values(ds, attr)
812
824
  except:
813
825
  # If the patient does not exist, generate values
814
826
  if patient in self.patients():
@@ -827,8 +839,8 @@ class DataBaseDicom():
827
839
  # If the study exists and has files, read from file
828
840
  files = register.files(self.register, study)
829
841
  attr = const.STUDY_MODULE
830
- ds = dbdataset.read_dataset(files[0])
831
- vals = dbdataset.get_values(ds, attr)
842
+ ds = pydicom.dcmread(files[0])
843
+ vals = get_values(ds, attr)
832
844
  except register.AmbiguousError as e:
833
845
  raise register.AmbiguousError(e)
834
846
  except:
@@ -836,9 +848,9 @@ class DataBaseDicom():
836
848
  if study[:-1] not in self.patients():
837
849
  study_id = 1
838
850
  else:
839
- study_id = 1 + self._max_study_id(study[-1])
851
+ study_id = 1 + self._max_study_id(study[1])
840
852
  attr = ['StudyInstanceUID', 'StudyDescription', 'StudyID']
841
- study_uid = dbdataset.new_uid()
853
+ study_uid = pydicom.uid.generate_uid()
842
854
  study_desc = study[-1] if isinstance(study[-1], str) else study[-1][0]
843
855
  #study_date = datetime.today().strftime('%Y%m%d')
844
856
  vals = [study_uid, study_desc, str(study_id)]
@@ -851,8 +863,8 @@ class DataBaseDicom():
851
863
  # If the series exists and has files, read from file
852
864
  files = register.files(self.register, series)
853
865
  attr = const.SERIES_MODULE
854
- ds = dbdataset.read_dataset(files[0])
855
- vals = dbdataset.get_values(ds, attr)
866
+ ds = pydicom.dcmread(files[0])
867
+ vals = get_values(ds, attr)
856
868
  except register.AmbiguousError as e:
857
869
  raise register.AmbiguousError(e)
858
870
  except:
@@ -864,7 +876,7 @@ class DataBaseDicom():
864
876
  else:
865
877
  series_number = 1 + self._max_series_number(study_uid)
866
878
  attr = ['SeriesInstanceUID', 'SeriesDescription', 'SeriesNumber']
867
- series_uid = dbdataset.new_uid()
879
+ series_uid = pydicom.uid.generate_uid()
868
880
  series_desc = series[-1] if isinstance(series[-1], str) else series[-1][0]
869
881
  vals = [series_uid, series_desc, int(series_number)]
870
882
  return study_attr | {attr[i]:vals[i] for i in range(len(attr)) if vals[i] is not None}
@@ -872,9 +884,9 @@ class DataBaseDicom():
872
884
 
873
885
  def _write_dataset(self, ds:Dataset, attr:dict, instance_nr:int):
874
886
  # Set new attributes
875
- attr['SOPInstanceUID'] = dbdataset.new_uid()
887
+ attr['SOPInstanceUID'] = pydicom.uid.generate_uid()
876
888
  attr['InstanceNumber'] = str(instance_nr)
877
- dbdataset.set_values(ds, list(attr.keys()), list(attr.values()))
889
+ set_values(ds, list(attr.keys()), list(attr.values()))
878
890
  # Save results in a new file
879
891
  rel_dir = os.path.join(
880
892
  f"Patient__{attr['PatientID']}",
@@ -882,7 +894,7 @@ class DataBaseDicom():
882
894
  f"Series__{attr['SeriesNumber']}__{attr['SeriesDescription']}",
883
895
  )
884
896
  os.makedirs(os.path.join(self.path, rel_dir), exist_ok=True)
885
- rel_path = os.path.join(rel_dir, dbdataset.new_uid() + '.dcm')
897
+ rel_path = os.path.join(rel_dir, pydicom.uid.generate_uid() + '.dcm')
886
898
  dbdataset.write(ds, os.path.join(self.path, rel_path))
887
899
  # Add an entry in the register
888
900
  register.add_instance(self.register, attr, rel_path)
@@ -899,11 +911,13 @@ class DataBaseDicom():
899
911
  )
900
912
  os.makedirs(zip_dir, exist_ok=True)
901
913
  for sr in st['series']:
914
+ zip_file = os.path.join(
915
+ zip_dir,
916
+ f"Series__{sr['SeriesNumber']}__{sr['SeriesDescription']}.zip",
917
+ )
918
+ if os.path.exists(zip_file):
919
+ continue
902
920
  try:
903
- zip_file = os.path.join(
904
- zip_dir,
905
- f"Series__{sr['SeriesNumber']}__{sr['SeriesDescription']}.zip",
906
- )
907
921
  with zipfile.ZipFile(zip_file, 'w') as zipf:
908
922
  for rel_path in sr['instances'].values():
909
923
  file = os.path.join(self.path, rel_path)