loadDicom.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import pydicom
  2. import numpy
  3. import sys
  4. import os
  5. import nibabel
  6. import json
  7. import datetime
  8. import re
  9. # load the DICOM files
  10. def load(dir):
  11. files = []
  12. print('Loading: {}'.format(dir))
  13. for fname in os.listdir(dir):
  14. print("loading: {}".format(fname))
  15. files.append(pydicom.dcmread(os.path.join(dir,fname)))
  16. print("file count: {}".format(len(files)))
  17. # skip files with no SliceLocation (eg scout views)
  18. slices = []
  19. skipcount = 0
  20. for f in files:
  21. if hasattr(f, 'SliceLocation'):
  22. slices.append(f)
  23. else:
  24. skipcount = skipcount + 1
  25. print("skipped, no SliceLocation: {}".format(skipcount))
  26. # ensure they are in the correct order
  27. slices = sorted(slices, key=lambda s: s.SliceLocation)
  28. return slices;
  29. # pixel aspects, assuming all slices are the same
  30. for s in slices:
  31. print("Pixel spacing: {}".format(s.PixelSpacing))
  32. return;
  33. def convertToNIfTI(slices):
  34. # create 3D array
  35. img_shape = list(slices[0].pixel_array.shape)
  36. img_shape.append(len(slices))
  37. img3d = numpy.zeros(img_shape)
  38. # get position and orientation parameters
  39. x0=numpy.array([float(f) for f in slices[0].ImagePositionPatient])
  40. x1=numpy.array([float(f) for f in slices[-1].ImagePositionPatient])
  41. n=(x1-x0)/(len(slices)-1.);
  42. f=numpy.array([float(f) for f in slices[0].ImageOrientationPatient])
  43. s=numpy.array([float(f) for f in slices[0].PixelSpacing])
  44. #create affine
  45. a=numpy.zeros((4,4))
  46. f1=numpy.zeros(4)
  47. f1[0:3]=f[0:3]
  48. #swap to account for row/column to (c,r) position mismatch
  49. a[:,1]=f1*s[1]
  50. f2=numpy.zeros(4)
  51. f2[0:3]=f[3:6]
  52. a[:,0]=f2*s[0]
  53. nn=numpy.zeros(4)
  54. nn[0:3]=n[0:3]
  55. a[:,2]=nn
  56. xn=numpy.ones(4)
  57. xn[0:3]=x0[0:3]
  58. a[:,3]=xn
  59. # fill 3D array with the images from the files
  60. for i, s in enumerate(slices):
  61. img2d = s.pixel_array
  62. img3d[:, :, i] = img2d
  63. #orientation and pixel spacing
  64. img = nibabel.Nifti1Image(img3d, a)
  65. img.header.set_slope_inter(float(slices[0].RescaleSlope),float(slices[0].RescaleIntercept))
  66. return img
  67. def writeAnonymousSeries(slices,path,patientID, studyUID, fields):
  68. uid=uuid()
  69. seriesUID=uid.generateSeriesUUID('volume')
  70. i=0;
  71. for s in slices:
  72. outFile="DCM{:04d}.dcm".format(i)
  73. outFile=os.path.join(path,outFile)
  74. writeAnonymousDicomFile(s,patientID,studyUID,seriesUID,outFile,fields)
  75. i=i+1
  76. def writeAnonymousDicomFile(slice, patientID, studyUID, seriesUID, outFile, fields):
  77. uid=uuid()
  78. instanceUID=uid.generateSOPInstanceUUID('volume')
  79. file_meta = pydicom.Dataset()
  80. file_meta.MediaStorageSOPClassUID = slice.file_meta.MediaStorageSOPClassUID
  81. file_meta.MediaStorageSOPInstanceUID = instanceUID
  82. file_meta.ImplementationClassUID = slice.file_meta.ImplementationClassUID
  83. file_meta.TransferSyntaxUID=pydicom.uid.ImplicitVRLittleEndian
  84. print("Setting dataset values...")
  85. # Create the FileDataset instance (initially no data elements, but file_meta
  86. # supplied)
  87. ds = pydicom.FileDataset(outFile, {},
  88. file_meta=file_meta, preamble=b"\0" * 128)
  89. # Add the data elements -- not trying to set all required here. Check DICOM
  90. # standard
  91. ds.PatientName = patientID
  92. ds.PatientID = patientID
  93. ds.StudyInstanceUID=studyUID
  94. ds.SeriesInstanceUID=seriesUID
  95. ds.SOPInstanceUID=instanceUID
  96. # Set the transfer syntax
  97. ds.is_little_endian = True
  98. ds.is_implicit_VR = True
  99. # Set creation date/time
  100. #dt = datetime.datetime.now()
  101. #ds.ContentDate = dt.strftime('%Y%m%d')
  102. #timeStr = dt.strftime('%H%M%S.%f') # long format with micro seconds
  103. ds.ContentTime = slice.ContentTime
  104. #add variables from Daniel's list (fields)
  105. for f in fields:
  106. try:
  107. ds[f]=slice[f];
  108. except:
  109. print("Field {} missing".format(f))
  110. ds.PixelData=slice.PixelData
  111. print("Writing test file", outFile)
  112. ds.save_as(outFile)
  113. print("File saved.")
  114. class uuid:
  115. def __init__(self):
  116. fhome=os.path.expanduser('~')
  117. fconfig=os.path.join(fhome,'.uuid','config.json')
  118. self.basePath=os.path.join(fhome,'.uuid')
  119. with open(fconfig,'r') as f:
  120. self.config=json.load(f)
  121. self.labelUUID={'series':'1','study':'2','instance':'3','frameOfReference':4}
  122. self.dataUUID={'volume':'1','segmentation':'2','transformation':'3'}
  123. def getTimeCode(x):
  124. hour=x.strftime("%H")
  125. hour=re.sub('^0','',hour)
  126. return hour+x.strftime("%M%S")
  127. def generateStudyUUID(self,type):
  128. x=datetime.datetime.now()
  129. date=x.strftime("%Y%m%d")
  130. studyFile=os.path.join(self.basePath,'studyCount'+date+'.txt')
  131. try:
  132. f=open(studyFile,"r")
  133. id=int(f.readline())
  134. id=id+1
  135. f.close()
  136. except:
  137. id=0
  138. studyId="{}.{}.{}.{}.{}".format(self.config["baseUUID"],self.labelUUID['study'],
  139. self.dataUUID[type],date,id)
  140. f=open(studyFile,"w")
  141. f.write("{}".format(id))
  142. f.close()
  143. return studyId
  144. def generateSOPInstanceUUID(self,type):
  145. baseUUID=self.config["baseUUID"]
  146. x=datetime.datetime.now()
  147. date=x.strftime("%Y%m%d")
  148. instanceFile=os.path.join(self.basePath,'instanceCount'+date+'.txt')
  149. try:
  150. f=open(instanceFile,"r")
  151. id=int(f.readline())
  152. id=id+1
  153. f.close()
  154. except:
  155. id=0
  156. instanceId="{}.{}.{}.{}.{}".format(baseUUID,self.labelUUID['instance'],
  157. self.dataUUID[type],date,id)
  158. f=open(instanceFile,"w")
  159. f.write("{}".format(id))
  160. f.close()
  161. return instanceId
  162. def generateFrameOfReferenceUUID(self,type):
  163. baseUUID=self.config["baseUUID"]
  164. basePath=self.basePath
  165. x=datetime.datetime.now()
  166. date=x.strftime("%Y%m%d")
  167. forFile=os.path.join(basePath,'frameCount'+date+'.txt')
  168. try:
  169. f=open(studyFile,"r")
  170. id=int(f.readline())
  171. id=id+1
  172. f.close()
  173. except:
  174. id=0
  175. forId="{}.{}.{}.{}.{}".format(baseUUID,self.labelUUID['frameOfReference'],
  176. self.dataUUID[type],date,id)
  177. f=open(forFile,"w")
  178. f.write("{}".format(id))
  179. f.close()
  180. return forId
  181. def generateSeriesUUID(self,type):
  182. baseUUID=self.config["baseUUID"]
  183. x=datetime.datetime.now()
  184. seriesInstanceUid=baseUUID+'.'+self.labelUUID['series']+'.'
  185. seriesInstanceUid+=self.dataUUID[type]+'.'+x.strftime("%Y%m%d")+'.'+uuid.getTimeCode(x)
  186. return seriesInstanceUid