organ_percentile.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import numpy
  2. import sys
  3. import os
  4. import json
  5. import nibabel
  6. def organ_percentile(img, mask, level, p):
  7. #ORGAN_PERCENTILE - computes suv_x the pth percentile of the distribution of
  8. # image intensity values in img from within some ROI mask
  9. #
  10. # Inputs:
  11. # img - image. ndarray
  12. # mask - ROI mask. Must be same dimension as img. Must be binary ndarray
  13. # p - percentile. Between 0-100
  14. #
  15. # Outputs:
  16. # suv_x - percentile of distribution. Defined by:
  17. #
  18. # suv_x
  19. # p/100 = ∫ H(x) dx
  20. # 0
  21. #
  22. # where H(x) is the normalized distribution of image values within mask
  23. #img = np.array(img)
  24. #mask = np.array(mask)
  25. h = img[mask == level]
  26. suv_x = numpy.percentile(h, p)
  27. return suv_x
  28. def main(parameterFile):
  29. fhome=os.path.expanduser('~')
  30. with open(os.path.join(fhome,".labkey","setup.json")) as f:
  31. setup=json.load(f)
  32. sys.path.insert(0,setup["paths"]["nixWrapper"])
  33. import nixWrapper
  34. nixWrapper.loadLibrary("labkeyInterface")
  35. import labkeyInterface
  36. import labkeyDatabaseBrowser
  37. import labkeyFileBrowser
  38. fconfig=os.path.join(fhome,'.labkey','network.json')
  39. net=labkeyInterface.labkeyInterface()
  40. net.init(fconfig)
  41. db=labkeyDatabaseBrowser.labkeyDB(net)
  42. fb=labkeyFileBrowser.labkeyFileBrowser(net)
  43. with open(parameterFile) as f:
  44. pars=json.load(f)
  45. #segmentation layout
  46. project=pars['project']
  47. dataset=pars['targetQuery']
  48. schema=pars['targetSchema']
  49. view=pars['viewName']
  50. segSchema=pars['segmentationSchema']
  51. segQuery=pars['segmentationQuery']
  52. qQuery=pars['percentileQuery']
  53. segVersion=pars['version']
  54. segVersionI=int(pars['versionNumber'])
  55. tempBase=pars['tempBase']
  56. if not os.path.isdir(tempBase):
  57. os.makedirs(tempBase)
  58. participantField=pars['participantField']
  59. #all images from database
  60. ds=db.selectRows(project,schema,dataset,[],view)
  61. petField=pars['images']['PET']['queryField']
  62. rows=ds['rows']
  63. #rows=[ds['rows'][0]]
  64. pv=numpy.concatenate((numpy.linspace(10,50,5),
  65. numpy.linspace(55,80,6),
  66. numpy.linspace(82,90,5),
  67. numpy.linspace(91,100,10)))
  68. #debug, select
  69. #rows1=[r for r in rows \
  70. # if r['patientCode']=='NIX-LJU-D2002-IRAE-A010' \
  71. # and r['visitCode']=='VISIT_6']
  72. #rows=rows1
  73. #rows=[r for r in rows if r[participantField]=='956/18']
  74. for r in rows:
  75. #is segmentation available
  76. localPET=os.path.join(tempBase,'PET.nii.gz')
  77. localSeg=os.path.join(tempBase,'Seg.nii.gz')
  78. for f in [localPET,localSeg]:
  79. if os.path.isfile(f):
  80. os.remove(f)
  81. #build image path
  82. try:
  83. remoteDir=fb.buildPathURL(project,[pars['imageDir'],\
  84. r['patientCode'],r['visitCode']])
  85. except TypeError:
  86. print('[{}/{}]: Skipping - Failed to build remote dir'.\
  87. format(r[participantField],r['SequenceNum']))
  88. continue
  89. print('{}: {}'.format(petField,r[petField]))
  90. if r[petField]==None:
  91. print('[{}/{}]: Skipping - Missing PET'.\
  92. format(r[participantField],r['SequenceNum']))
  93. continue
  94. remotePET=remoteDir+'/'+r[petField]
  95. print('{}:{}'.format(remotePET,fb.entryExists(remotePET)))
  96. vFilter={'variable':'version','value':segVersion,'oper':'eq'}
  97. idFilter={'variable':'patientCode','value':r['patientCode'], 'oper':'eq'}
  98. visitFilter={'variable':'visitCode','value':r['visitCode'], 'oper':'eq'}
  99. dsSeg=db.selectRows(project,segSchema,segQuery,[idFilter,visitFilter,vFilter])
  100. if len(dsSeg['rows'])!=1:
  101. print('[{}/{}] Skipping - missing segmentation, version [{}]'.\
  102. format(r[participantField],r['SequenceNum'],segVersion))
  103. continue
  104. remoteSeg=remoteDir+'/'+dsSeg['rows'][0]['segmentation']
  105. print('{}:{}'.format(remoteSeg,fb.entryExists(remoteSeg)))
  106. fb.readFileToFile(remotePET,localPET)
  107. fb.readFileToFile(remoteSeg,localSeg)
  108. niPET=nibabel.load(localPET)
  109. niSeg=nibabel.load(localSeg)
  110. #3 lungs
  111. #4 thyroid
  112. #5 bowel
  113. dsP=db.selectRows(project,schema,qQuery,[idFilter,visitFilter,vFilter])
  114. db.modifyRows('delete',project,schema,qQuery,dsP['rows'])
  115. dspRows=[]
  116. for level in [1,2,3,4,5,6,7,8]:
  117. try:
  118. v=organ_percentile(niPET.get_fdata(),niSeg.get_fdata(),level,pv)
  119. except IndexError:
  120. print('Error for {}/{}: {}'.format(r['patientCode'],r['visitCode'],level))
  121. continue
  122. for (x,y) in zip(pv,v):
  123. #get existing entry
  124. seqNum=r['SequenceNum']+0.0001*x+0.01*segVersionI
  125. #print('[{:.8f}] {}/{}: {}/{}'.format(seqNum,r['patientCode'],r['visitCode'],x,y))
  126. # sFilter={'variable':'SequenceNum','value':'{}'.format(seqNum),'oper':'eq'}
  127. # oFilter={'variable':'organ','value':'{}'.format(level),'oper':'eq'}
  128. # mode='update'
  129. # if len(dsP['rows'])==0:
  130. # mode='insert'
  131. rowDSP={x:r[x] for x in [participantField,'patientCode','visitCode']}
  132. rowDSP['SequenceNum']=seqNum
  133. rowDSP['segmentationVersion']=segVersion
  134. # else:
  135. # rowDSP=dsP['rows'][0]
  136. rowDSP['percentile']=x
  137. rowDSP['value']=y
  138. rowDSP['organ']=level
  139. dspRows.append(rowDSP)
  140. db.modifyRows('insert',project,schema,qQuery,dspRows)
  141. print('Done')
  142. if __name__ == '__main__':
  143. main(sys.argv[1])