convertToNRRD.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import os
  2. import sys
  3. import json
  4. import pathlib
  5. import slicer
  6. import subprocess
  7. import shutil
  8. def main(configFile):
  9. print('Running with {}'.format(configFile))
  10. with open(configFile) as f:
  11. config=json.load(f)
  12. fsetup=os.path.join(os.path.expanduser('~'),'.labkey','setup.json')
  13. with open(fsetup) as f:
  14. setup=json.load(f)
  15. sys.path.append(setup['paths']['nixWrapper'])
  16. import nixWrapper
  17. nixWrapper.loadLibrary('labkeyInterface')
  18. import labkeyInterface
  19. import labkeyDatabaseBrowser
  20. import labkeyFileBrowser
  21. nixWrapper.loadLibrary('orthancInterface')
  22. import orthancInterface
  23. import orthancDatabaseBrowser
  24. import orthancFileBrowser
  25. net=labkeyInterface.labkeyInterface()
  26. fnet=os.path.join(os.path.expanduser('~'),'.labkey',config['network'])
  27. net.init(fnet)
  28. net.getCSRF()
  29. fb=labkeyFileBrowser.labkeyFileBrowser(net)
  30. db=labkeyDatabaseBrowser.labkeyDB(net)
  31. onet=orthancInterface.orthancInterface()
  32. onet.init(fnet)
  33. ofb=orthancFileBrowser.orthancFileBrowser(onet)
  34. odb=orthancDatabaseBrowser.orthancDB(onet)
  35. qFilter=[]
  36. ds=db.selectRows(config['project'],config['schemaName'],config['queryName'],qFilter)
  37. try:
  38. rows=ds['rows']
  39. except KeyError:
  40. print('No rows returned')
  41. return
  42. for r in rows:
  43. print("Loading {}/{}".format(patientId(r,config),visitId(r,config)))
  44. nNames=nodeNames(r,config)
  45. rPath=fb.formatPathURL(config['project'],outputDir(r,config))
  46. rPath+='/'+nNames['CT']+'.nrrd'
  47. entryDone=fb.entryExists(rPath)
  48. if entryDone:
  49. try:
  50. if not config['recalculate']:
  51. print('Entry done')
  52. continue
  53. except KeyError:
  54. print('Entry done')
  55. continue
  56. print('Forced recalculation')
  57. print('{} available:{}'.format(nNames['CT'],fb.entryExists(rPath)))
  58. #loadPatient into slicer
  59. patient=loadPatient(ofb,r,config)
  60. #convert to nodes
  61. addCT(r,patient,config)
  62. addFrames(r,patient,config)
  63. addDummyInputFunction(r,patient,config)
  64. nodes=slicer.mrmlScene.GetNodesByClass('vtkMRMLScalarVolumeNode')
  65. print('Nodes')
  66. for node in nodes:
  67. print('\t{}'.format(node.GetName()))
  68. storeNode(fb,r,config,node)
  69. nodes=slicer.mrmlScene.GetNodesByClass('vtkMRMLDoubleArrayNode')
  70. print('Nodes (double array)')
  71. for node in nodes:
  72. print('\t{}'.format(node.GetName()))
  73. storeNode(fb,r,config,node)
  74. clearNodes(r,config)
  75. def patientId(row,config):
  76. return row[config['ParticipantField']]
  77. def visitId(row,config):
  78. return row['visitName']
  79. def code(row,config):
  80. return '{}_{}'.format(patientId(row,config),visitId(row,config))
  81. def pathList(row,config):
  82. return [config['baseDir'],patientId(row,config),visitId(row,config)]
  83. def outputDir(row,config):
  84. return '/'.join(pathList(row,config))
  85. def localDir(row,config):
  86. return os.path.join(config['tempDir'],code(row,config))
  87. def nodeNames(row,config):
  88. names={}
  89. x=code(row,config)
  90. names['NM']=['{}_Volume{}'.format(x,i) for i in range(20)]
  91. names['CT']='{}_CT'.format(x)
  92. return names
  93. def clearNodes(row,config):
  94. nodes=slicer.mrmlScene.GetNodesByClass('vtkMRMLScalarVolumeNode')
  95. nodes1=slicer.mrmlScene.GetNodesByClass('vtkMRMLDoubleArrayNode')
  96. for n in nodes1:
  97. nodes.AddItem(n)
  98. res=[slicer.mrmlScene.RemoveNode(f) for f in nodes]
  99. def loadPatient(ofb,r,config):
  100. sys.path.append('../pythonScripts')
  101. import parseDicom
  102. import vtkInterface
  103. pd=parseDicom.parseDicom()
  104. masterPath=downloadAndUnzip(ofb,r,"nmMaster",config)
  105. pd.readMasterDirectory(masterPath,False)
  106. print('Time [{} .. {}]'.format(pd.frame_start,pd.frame_stop))
  107. nmPath=downloadAndUnzip(ofb,r,"nmCorrData",config)
  108. frame_data, frame_time, frame_duration, frame_origin, \
  109. frame_pixel_size, frame_orientation=\
  110. pd.readNMDirectory(nmPath,False)
  111. print('Frame time {}'.format(frame_time))
  112. ctPath=downloadAndUnzip(ofb,r,"ct",config)
  113. ct_data,ct_origin,ct_pixel_size, \
  114. ct_orientation=pd.readCTDirectory(ctPath,False)
  115. print('CT pixel {}'.format(ct_pixel_size))
  116. ct_orientation=vtkInterface.completeOrientation(ct_orientation)
  117. frame_orientation=vtkInterface.completeOrientation(frame_orientation)
  118. ct={'data':ct_data,'origin':ct_origin,'pixel_size':ct_pixel_size,
  119. 'orientation':ct_orientation}
  120. nm={'data':frame_data,'time':frame_time,'duration':frame_duration,
  121. 'origin':frame_origin,'pixel_size':frame_pixel_size,
  122. 'orientation':frame_orientation}
  123. return {'CT':ct,'NM':nm}
  124. print('Done')
  125. def downloadAndUnzip(ofb,r,xcode,config):
  126. orthancId=r[xcode+'OrthancId']
  127. fileCode='{}_{}'.format(code(r,config),xcode)
  128. zipFile=os.path.join(config['tempDir'],fileCode+'.zip')
  129. ofb.getZip('series',orthancId,zipFile)
  130. zipDir=localDir(r,config)
  131. try:
  132. os.mkdir(zipDir)
  133. except FileExistsError:
  134. shutil.rmtree(zipDir)
  135. try:
  136. outTxt=subprocess.check_output(["unzip","-d",zipDir,"-xj",zipFile])
  137. except subprocess.CalledProcessError:
  138. print("unzip failed for {}".format(zipFile))
  139. return ""
  140. return zipDir
  141. def addCT(r,patient,config):
  142. sys.path.append('../pythonScripts')
  143. import vtkInterface
  144. ct=patient['CT']
  145. vtkData=vtkInterface.numpyToVTK(ct['data'],ct['data'].shape)
  146. nodeName=code(r,config)+'_CT'
  147. addNode(nodeName,vtkData,ct['origin'],ct['pixel_size'],ct['orientation'],0)
  148. def addFrames(r,patient,config):
  149. sys.path.append('../pythonScripts')
  150. import vtkInterface
  151. #convert data from numpy.array to vtkImageData
  152. #use time point it
  153. x=code(r,config)
  154. nm=patient['NM']
  155. print("NFrames: {}".format(nm['data'].shape[3]))
  156. for it in range(0,nm['data'].shape[3]):
  157. frame_data=nm['data'][:,:,:,it];
  158. nodeName=x+'_Volume'+str(it)
  159. vtkData=vtkInterface.numpyToVTK(frame_data,frame_data.shape)
  160. addNode(nodeName,vtkData,nm['origin'],nm['pixel_size'],nm['orientation'],1)
  161. def addNode(nodeName,v,origin,pixel_size,orientation,dataType):
  162. #origin,orientation in lps
  163. #dataType=0 is CT (to background)
  164. #dataType=1 is SPECT, view not adjusted, foreground,
  165. newNode=slicer.vtkMRMLScalarVolumeNode()
  166. newNode.SetName(nodeName)
  167. v.SetOrigin([0,0,0])
  168. v.SetSpacing([1,1,1])
  169. ijkToRAS = vtk.vtkMatrix4x4()
  170. #think how to do this with image orientation
  171. #orientation from lps to ras
  172. rasOrientation=[-orientation[i] if (i%3 < 2) else orientation[i]
  173. for i in range(0,len(orientation))]
  174. #origin from lps to ras
  175. rasOrigin=[-origin[i] if (i%3<2) else origin[i] for i in range(0,len(origin))]
  176. for i in range(0,3):
  177. for j in range(0,3):
  178. ijkToRAS.SetElement(i,j,pixel_size[i]*rasOrientation[3*j+i])
  179. ijkToRAS.SetElement(i,3,rasOrigin[i])
  180. newNode.SetIJKToRASMatrix(ijkToRAS)
  181. newNode.SetAndObserveImageData(v)
  182. slicer.mrmlScene.AddNode(newNode)
  183. def addDummyInputFunction(r,patient,config):
  184. nm=patient['NM']
  185. n=nm['data'].shape[3]
  186. dnsNodeName=code(r,config)+'_Dummy'
  187. dns = slicer.mrmlScene.GetNodesByClassByName('vtkMRMLDoubleArrayNode',dnsNodeName)
  188. if dns.GetNumberOfItems() == 0:
  189. dn = slicer.mrmlScene.AddNode(slicer.vtkMRMLDoubleArrayNode())
  190. dn.SetName(dnsNodeName)
  191. else:
  192. dn = dns.GetItemAsObject(0)
  193. dn.SetSize(n)
  194. ft=nm['time']
  195. dt=nm['duration']
  196. for i in range(0,n):
  197. fx=ft[i]
  198. fy=dt[i]
  199. dn.SetValue(i, 0, fx)
  200. dn.SetValue(i, 1, fy)
  201. dn.SetValue(i, 2, 0)
  202. print('{} ({},{})'.format(i,fx,fy))
  203. def storeNode(fb,row,config,node):
  204. suffix=".nrrd"
  205. if node.__class__.__name__=="vtkMRMLDoubleArrayNode":
  206. suffix=".mcsv"
  207. if (node.__class__.__name__=="vtkMRMLTransformNode" or \
  208. node.__class__.__name__=="vtkMRMLGridTransformNode"):
  209. suffix=".h5"
  210. fileName=node.GetName()+suffix
  211. localPath=os.path.join(localDir(row,config),fileName)
  212. slicer.util.saveNode(node,localPath)
  213. print("Stored to: {}".format(localPath))
  214. labkeyPath=fb.buildPathURL(config['project'],pathList(row,config))
  215. print ("Remote: {}".format(labkeyPath))
  216. #checks if exists
  217. remoteFile=labkeyPath+'/'+fileName
  218. fb.writeFileToFile(localPath,remoteFile)
  219. def readPatient(fb,localDir,project,patientId):
  220. rDir=fb.formatPathURL(project,'/'.join([patientId]))
  221. lDir=os.path.join(localDir,patientId)
  222. if not os.path.isdir(lDir):
  223. os.makedirs(lDir)
  224. ok,files=fb.listRemoteDir(rDir)
  225. locFiles=[]
  226. for f in files:
  227. print(f)
  228. p=pathlib.Path(f)
  229. localFile=os.path.join(lDir,p.name)
  230. fb.readFileToFile(f,localFile)
  231. locFiles.append(localFile)
  232. print('Done')
  233. return locFiles
  234. if __name__=='__main__':
  235. main(sys.argv[1])
  236. quit()