statUtils.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #load required libraries
  2. import sys
  3. import os
  4. import SimpleITK
  5. import json
  6. import chardet
  7. import numpy
  8. def connectDB(server):
  9. #you should get nixSuite via git clone https://git0.fmf.uni-lj.si/studen/nixSuite.git
  10. #if you don't put it to $HOME/software/src/, you should update the path
  11. nixSuite=os.path.join(os.path.expanduser('~'),'software','src','nixSuite')
  12. if not os.path.isdir(nixSuite):
  13. nixSuite=os.path.join(os.path.expanduser('~'),'software','src','nixsuite')
  14. sys.path.append(os.path.join(nixSuite,'wrapper'))
  15. import nixWrapper
  16. nixWrapper.loadLibrary('labkeyInterface')
  17. import labkeyInterface
  18. import labkeyDatabaseBrowser
  19. import labkeyFileBrowser
  20. #check connectivity. This checks the configuration in $HOME/.labkey/network.json,
  21. #where paths to certificates are stored
  22. net=labkeyInterface.labkeyInterface()
  23. fconfig=os.path.join(os.path.expanduser('~'),'.labkey','{}.json'.format(server))
  24. net.init(fconfig)
  25. #this reports the certificate used
  26. try:
  27. print('Using: {}'.format(net.connectionConfig['SSL']['user']))
  28. except KeyError:
  29. pass
  30. #This gets a deafult CSRF code; It should report user name plus a long string of random hex numbers
  31. net.getCSRF()
  32. db=labkeyDatabaseBrowser.labkeyDB(net)
  33. fb=labkeyFileBrowser.labkeyFileBrowser(net)
  34. return db,fb
  35. def getUsers(db,project):
  36. ds=db.selectRows(project,'core','Users',[])
  37. users={x['UserId']:x['DisplayName'] for x in ds['rows']}
  38. for u in users:
  39. print('{} {}'.format(u,users[u]))
  40. return users
  41. def getImage(setup, row, field, extraPath=None):
  42. fb=setup['fb']
  43. pathList=[setup['imageDir'],row['patientCode'],row['visitCode']]
  44. if extraPath!=None:
  45. pathList.append(extraPath)
  46. pathList.append(row[field])
  47. try:
  48. #if row[field] is None, this fails
  49. remotePath='/'.join(pathList)
  50. except TypError:
  51. #return marker for failed
  52. return "NONE"
  53. urlPath=fb.formatPathURL(setup['project'],remotePath)
  54. localPath=os.path.join(setup['localDir'],row[field])
  55. if os.path.isfile(localPath):
  56. pass
  57. #print('{} done'.format(localPath))
  58. else:
  59. if not fb.entryExists(urlPath):
  60. print('No file {}'.format(urlPath))
  61. return "NONE"
  62. fb.readFileToFile(urlPath,localPath)
  63. print('Copying remote {} -> {} done'.format(urlPath,localPath))
  64. return localPath
  65. def getSegmentations(setup,row):
  66. segName=setup.get('segmentationDataset','Segmentations')
  67. print(f'Using segmentation dataset {segName}')
  68. idField=setup['idField']
  69. visitField=setup['visitField']
  70. qFilter=[{'variable':x,'value':row[x],'oper':'eq'} for x in [idField,visitField]]
  71. dsSeg=setup['db'].selectRows(setup['project'],'study',segName,qFilter)
  72. if len(dsSeg['rows'])<1:
  73. print('Failed to find segmentation for {}/{}'.format(row[idField],row[visitField]))
  74. return {0:"NONE"}
  75. return {r['User']:getImage(setup,r,'latestFile',extraPath='Segmentations') for r in dsSeg['rows']}
  76. def loadSetup(path):
  77. with open(path,'r') as f:
  78. setup=json.load(f)
  79. fC=[x for x in setup.keys() if x.find('Dir')>-1]
  80. for q in fC:
  81. setup[q]=setup[q].replace('_home_',os.path.expanduser('~'))
  82. return setup
  83. def getSegments(image):
  84. keys=image.GetMetaDataKeys()
  85. i=0
  86. ids={}
  87. while True:
  88. id='Segment{}_ID'.format(i)
  89. value='Segment{}_LabelValue'.format(i)
  90. try:
  91. ids[image.GetMetaData(id)]=int(image.GetMetaData(value))
  92. except RuntimeError:
  93. break
  94. i+=1
  95. return ids
  96. def getStats(image):
  97. print(image.GetSize())
  98. print(image.GetOrigin())
  99. print(image.GetSpacing())
  100. print(image.GetNumberOfComponentsPerPixel())
  101. def getSegmentStats(pet,seg,label):
  102. o=getComponents(seg,label)
  103. cc=o['image']
  104. shape_stats = SimpleITK.LabelShapeStatisticsImageFilter()
  105. #shape_stats.ComputeOrientedBoundingBoxOn()
  106. shape_stats.Execute(cc)
  107. intensity_stats = SimpleITK.LabelIntensityStatisticsImageFilter()
  108. intensity_stats.Execute(cc,pet)
  109. #output
  110. out=[(shape_stats.GetPhysicalSize(i),
  111. intensity_stats.GetMean(i),
  112. intensity_stats.GetStandardDeviation(i),
  113. intensity_stats.GetSkewness(i)) for i in shape_stats.GetLabels()]
  114. print(out)
  115. def getComponents(seg,label):
  116. cc=SimpleITK.Threshold(seg,lower=label,upper=label)
  117. ccFilter=SimpleITK.ConnectedComponentImageFilter()
  118. cc1=ccFilter.Execute(cc)
  119. return {'image':cc1, 'count':ccFilter.GetObjectCount()}
  120. def drop_array(v):
  121. return float(v)
  122. #if type(v) is numpy.ndarray:
  123. # return v[0]
  124. #return v
  125. def getSUVmax(vals):
  126. return drop_array(vals['original_firstorder_Maximum'])
  127. def getSUVmean(vals):
  128. return drop_array(vals['original_firstorder_Mean'])
  129. def getSUVSD(vals):
  130. return numpy.sqrt(drop_array(vals['original_firstorder_Variance']))
  131. def getMTV(vals):
  132. try:
  133. return drop_array(vals['original_shape_MeshVolume'])
  134. except KeyError:
  135. return drop_array(vals['original_shape_VoxelVolume'])
  136. def getTLG(vals):
  137. V=vals['original_shape_VoxelVolume']
  138. return V*getSUVmean(vals)
  139. def getCOM(vals):
  140. return vals['diagnostics_Mask-original_CenterOfMassIndex']
  141. def getNVoxels(vals):
  142. return round(drop_array(vals['original_shape_VoxelVolume']/8))
  143. def getValue(vals,valueName):
  144. if valueName=='SUVmean':
  145. return getSUVmean(vals)
  146. if valueName=='SUVmax':
  147. return getSUVmax(vals)
  148. if valueName=='SUVSD':
  149. return getSUVSD(vals)
  150. if valueName=='MTV':
  151. return getMTV(vals)
  152. if valueName=='TLG':
  153. return getTLG(vals)
  154. if valueName=='COM':
  155. return getCOM(vals)
  156. if valueName=='voxelCount':
  157. return getNVoxels(vals)
  158. return 0
  159. def getRadiomicsStats(setup,pet,seg,label):
  160. o=getComponents(seg,label)
  161. cc=o['image']
  162. n=o['count']
  163. output={}
  164. for i in range(n):
  165. output[i]=getRadiomicsComponentStats(setup,pet,seg,label)
  166. return output
  167. def getRadiomicsComponentStats(setup,pet,cc,label):
  168. vals=setup['featureExtractor'].execute(pet,cc,label=label)
  169. output={x:getValue(vals,x) for x in setup['values']}
  170. #for v in vals:
  171. # print('{}: {}'.format(v,vals[v]))
  172. for c in output:
  173. print('{}: {}'.format(c,output[c]))
  174. return output
  175. def findMatchingComponent(o,a,b,label):
  176. statFilter=SimpleITK.StatisticsImageFilter()
  177. overlap_measures_filter = SimpleITK.LabelOverlapMeasuresImageFilter()
  178. print('{}: [{}]:{} [{}]:{}'.format(label,a,o[a]['count'],b,o[b]['count']))
  179. comps={v:{x+1:o[v]['image']==x+1 for x in range(o[v]['count'])} for v in [a,b]}
  180. stat={}
  181. for comp in comps:
  182. stat[comp]={}
  183. for x in comps[comp]:
  184. cc=comps[comp][x]
  185. statFilter.Execute(cc)
  186. stat[comp][x]={'sum':statFilter.GetSum()}
  187. for c in comps[a]:
  188. cc=comps[a][c]
  189. print('{}:{}'.format(c,stat[a][c]['sum']))
  190. for d in comps[b]:
  191. cc1=comps[b][d]
  192. overlap_measures_filter.Execute(cc, cc1)
  193. print('\t {}:{} {}'.format(d,stat[b][d]['sum'],overlap_measures_filter.GetDiceCoefficient()))
  194. def evaluateByLesions(pet,seg,ids):
  195. for id in ids:
  196. print('{}: {}'.format(id,ids[id]))
  197. o={x:getComponents(seg[x],ids[id]) for x in seg}
  198. a=segKeys[0]
  199. for x in segKeys[1:]:
  200. findMatchingComponent(o,a,x,ids[id])
  201. def updateDatasetRows(db,project,dataset,rows,filterVars=['ParticipantId','SequenceNum','User']):
  202. for r in rows:
  203. r['SequenceNum']+=r['segment']*0.01
  204. qFilter=[{'variable':x,'value':'{}'.format(r[x]),'oper':'eq'} for x in filterVars]
  205. ds=db.selectRows(project,'study',dataset,qFilter)
  206. if len(ds['rows'])>0:
  207. row=ds['rows'][0]
  208. row.update({x:r[x] for x in r if x not in filterVars})
  209. mode='update'
  210. resp=db.modifyRows('update',project,'study',dataset,[row])
  211. else:
  212. mode='insert'
  213. resp=db.modifyRows('insert',project,'study',dataset,[r])
  214. #encoding=chardet.detect(resp)['encoding']
  215. #respJSON=json.loads(resp.decode(encoding))
  216. try:
  217. print('[ERROR]: {}'.format(resp['exception']))
  218. print(f'Using mode={mode}')
  219. except KeyError:
  220. pass