generateNPZ.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import os
  2. import sys
  3. import numpy
  4. import random
  5. import SimpleITK
  6. import datetime
  7. def initDB(site,returnFB=False):
  8. home=os.path.expanduser('~')
  9. print(home)
  10. nixSuite=os.path.join(home,'software','src','nixSuite')
  11. sys.path.append(os.path.join(nixSuite,'wrapper'))
  12. import nixWrapper
  13. nixWrapper.loadLibrary('labkeyInterface')
  14. import labkeyInterface
  15. import labkeyDatabaseBrowser
  16. import labkeyFileBrowser
  17. net=labkeyInterface.labkeyInterface()
  18. fconfig=os.path.join(home,'.labkey','{}.json'.format(site))
  19. net.init(fconfig)
  20. net.getCSRF()
  21. if not returnFB:
  22. return labkeyDatabaseBrowser.labkeyDB(net)
  23. return labkeyDatabaseBrowser.labkeyDB(net),\
  24. labkeyFileBrowser.labkeyFileBrowser(net)
  25. def initOrthanc(site,returnFB=False):
  26. home=os.path.expanduser('~')
  27. nixSuite=os.path.join(home,'software','src','nixSuite')
  28. sys.path.append(os.path.join(nixSuite,'wrapper'))
  29. import nixWrapper
  30. nixWrapper.loadLibrary('orthancInterface')
  31. import orthancInterface
  32. import orthancDatabaseBrowser
  33. import orthancFileBrowser
  34. net=orthancInterface.orthancInterface()
  35. fconfig=os.path.join(home,'.labkey','{}.json'.format(site))
  36. net.init(fconfig)
  37. if not returnFB:
  38. return orthancDatabaseBrowser.orthancDB(net)
  39. return orthancDatabaseBrowser.orthancDB(net),\
  40. orthancFileBrowser.orthancFileBrowser(net)
  41. def parsePos(x):
  42. return [float(y) for y in x.split('\\')]
  43. def parseOrientation(x):
  44. z=[float(y) for y in x.split('\\')]
  45. O=[z[0:3],z[3:6]]
  46. O.append(list(numpy.cross(numpy.array(O[0]),numpy.array(O[1]))))
  47. return O
  48. def getGeometry(code,odb,seriesId):
  49. #imagepositionpatient 0020-0032
  50. #imageorientationpatient 0020-0037
  51. #pixelSpacing 0028-0030
  52. #instance number 0020-0013
  53. #sliceThickness 0018-0050
  54. sd=odb.getData('series',seriesId)
  55. firstId=sd['Instances'][0]
  56. ior=parseOrientation(odb.getDicomField(firstId,'0020-0037'))
  57. #spacing
  58. ips=parsePos(odb.getDicomField(firstId,'0028-0030'))
  59. ist=parsePos(odb.getDicomField(firstId,'0018-0050'))
  60. #don't use slice thickness for pixel spacing
  61. #ips.append(ist[0])
  62. ipos=[parsePos(odb.getDicomField(y,'0020-0032')) for y in sd['Instances']]
  63. #guess spacing from ipos
  64. m=[numpy.dot(numpy.array(ior[2]),numpy.array(x)) for x in ipos]
  65. #sort array
  66. m1=numpy.sort(m)
  67. dm=m1[1:]-m1[:-1]
  68. zSpc=numpy.average(dm)
  69. zDev=numpy.std(dm)
  70. print(f'sliceThickness={zSpc} +- {zDev}')
  71. ips.append(zSpc)
  72. #direction, ie. scaled orientation
  73. #copy orientation to direction
  74. xdir=[y for y in ior]
  75. for i in range(3):
  76. xdir[i]=[ips[i]*w for w in xdir[i]]
  77. #find minimum from original array to idenfify lower slice
  78. im=numpy.argmin(numpy.array(m))
  79. print(im)
  80. org=ipos[im]
  81. print(org)
  82. print(xdir)
  83. return {f'{code}origin':org,f'{code}orientation':xdir}
  84. def getFile(db,odb,project,outdir,n1=0,n2=1):
  85. schema='study'
  86. query='Imaging1'
  87. #coreDir=os.path.join(os.path.expanduser('~'),'temp')
  88. ds=db.selectRows(project,schema,query,[])
  89. rows=ds['rows']
  90. if n2>0:
  91. rows=rows[n1:n2]
  92. imgs={'CT':'CT_orthancId','PET':'PETWB_orthancId'}
  93. files=[]
  94. for r in rows:
  95. code=r['pseudoCode']
  96. doUpdate=False
  97. if not code:
  98. code='{:16x}'.format(random.randrange(16**16))
  99. r['pseudoCode']=code
  100. doUpdate=True
  101. fname=os.path.join(outdir,f'{code}.npz')
  102. arr={}
  103. #debug don't load array
  104. iSUV={x:getSUVfactor(odb,r[imgs[x]]) for x in imgs}
  105. arr.update({x:iSUV[x]*numpy.load(odb.getNumpy('series',r[imgs[x]])) for x in imgs})
  106. #debug don't calculate geometry
  107. igeo={x:getGeometry(x,odb,r[imgs[x]]) for x in imgs}
  108. #debug skip saving
  109. #continue
  110. _={arr.update(igeo[x]) for x in igeo}
  111. #print(arr)
  112. numpy.savez_compressed(fname,**arr)
  113. files.append(fname)
  114. if doUpdate:
  115. db.modifyRows('update',project,schema,query,[r])
  116. return files
  117. def mergeDateAndTime(date,time):
  118. time=time.replace(' ','')
  119. dateTimeStr=' '.join([date,time])
  120. #print(f'[{date}] [{time}] [{dateTimeStr}]')
  121. dt=datetime.datetime.strptime(dateTimeStr,'%Y%m%d %H%M%S.%f')
  122. #print(dt)
  123. return dt
  124. def getSUVfactor(odb,seriesId):
  125. sd=odb.getData('series',seriesId)
  126. orthancId=sd['Instances'][0]
  127. MODALITY='0008-0060'
  128. PATIENT_WEIGHT='0010-1030'
  129. RADIOPHARMACEUTICAL_STARTTIME='0054-0016/0/0018-1072'
  130. RADIONUCLIDE_TOTALDOSE='0054-0016/0/0018-1074'
  131. RADIONUCLIDE_HALFLIFE='0054-0016/0/0018-1075'
  132. RADIOPHARMACEUTICAL_STARTDATETIME='0054-0016/0/0018-1078'
  133. SERIES_TIME='0008-0031'
  134. SERIES_DATE='0008-0021'
  135. modality=odb.getDicomField(orthancId,MODALITY)
  136. if modality!='PT':
  137. print('Returning 1')
  138. return 1
  139. weight = float(odb.getDicomField(orthancId,PATIENT_WEIGHT))
  140. #print(weight)
  141. halfLife = float(odb.getDicomField(orthancId,RADIONUCLIDE_HALFLIFE))#seconds
  142. #print(halfLife)
  143. injDose = float(odb.getDicomField(orthancId,RADIONUCLIDE_TOTALDOSE))#in Bq
  144. #print(injDose)
  145. #two options for time of activity measurement (full date time or time only, then match it with studyDate)
  146. #injDateTime = odb.getDicomField(orthancId,RADIOPHARMACEUTICAL_STARTDATETIME)
  147. #if troubles:
  148. injDateTime = mergeDateAndTime(odb.getDicomField(orthancId,SERIES_DATE),
  149. odb.getDicomField(orthancId,RADIOPHARMACEUTICAL_STARTTIME))
  150. scanDateTime=mergeDateAndTime(odb.getDicomField(orthancId,SERIES_DATE),
  151. odb.getDicomField(orthancId,SERIES_TIME))
  152. t=(scanDateTime-injDateTime).total_seconds()
  153. scanDose = injDose*numpy.exp(-(numpy.log(2)*t)/halfLife);
  154. SUV_factor = 1/((scanDose/weight)*0.001);
  155. print(SUV_factor)
  156. return SUV_factor