pv_parseBruker_md_np.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. """
  2. Created on 10/08/2017
  3. @author: Niklas Pallast
  4. Neuroimaging & Neuroengineering
  5. Department of Neurology
  6. University Hospital Cologne
  7. """
  8. from __future__ import print_function
  9. import os,sys
  10. import numpy as np
  11. from dict2xml import createXML
  12. # from string import split
  13. def parsePV(filename):
  14. """
  15. Parser for Bruker ParaVision parameter files in JCAMP-DX format
  16. Prarmeters:
  17. ===========
  18. filename: 'acqp', 'method', 'd3proc', 'roi', 'visu_pars', etc.
  19. """
  20. if not os.path.exists(filename):
  21. return []
  22. # Read file 'filename' -> list 'lines'
  23. f = open(filename, 'r')
  24. lines = f.readlines()
  25. f.close()
  26. # Dictionary for parameters
  27. params = {}
  28. # Get STUDYNAME, EXPNO, and PROCNO
  29. #if filename[-9:] == 'visu_pars':
  30. if 'visu_pars' in filename:
  31. tmp = lines[6].split('/')
  32. try:
  33. params['studyname'] = [[], tmp[-5]]
  34. params['expno'] = [[], tmp[-4]]
  35. params['procno'] = [[], tmp[-2]]
  36. except IndexError:
  37. params['studyname'] = [[], "leave"] # This part created an error which is not relevant because visu_parameters are not neccessory for AIDAqc.
  38. params['expno'] = [[], "Me"]
  39. params['procno'] = [[], "alone"]
  40. # Remove comment lines
  41. remove = [] # Index list
  42. for index, line in enumerate(lines): # Find lines
  43. if line[0:2] == '$$':
  44. remove.append(index)
  45. for offset, index in enumerate(remove): # Remove lines
  46. del lines[index-offset]
  47. # Create list of LDR (Labelled Data Record) elements
  48. lines = ''.join(lines).split('\n##') # Join lines and split into LDRs
  49. #lines = map(rstrip, lines) # Remove trailing whitespace from each LDR
  50. lines[0] = lines[0].lstrip('##') # Remove leading '##' from first LDR
  51. # Combine LDR lines
  52. for index, line in enumerate(lines):
  53. lines[index] = ''.join(line.split('\n'))
  54. # Fill parameter dictionary
  55. if np.size(lines) == 1:
  56. sys.exit("Error: visu_pars is not readable")
  57. if 'subject' in filename:
  58. try:
  59. tmp = lines[32].split('#$Name,')
  60. params['coilname'] = tmp[1].split('#$Id')[0]
  61. except IndexError:
  62. params['coilname'] = "Unknown"
  63. return params
  64. for line in lines:
  65. line = line.split('=', 1)
  66. if line[0][0] == '$':
  67. key = line[0].lstrip('$')
  68. dataset = line[1]
  69. params[key] = []
  70. pos = 0
  71. if (len(dataset) > 4) and (dataset[0:2] == '( '):
  72. pos = dataset.find(' )', 2)
  73. if pos > 2:
  74. pardim = [int(dim) for dim in dataset[2:pos].split(',')]
  75. params[key].append(pardim)
  76. params[key].append(dataset[pos+2:])
  77. if pos <= 2:
  78. params[key].append([])
  79. params[key].append(dataset)
  80. # Remove specific elements from parameter dictionary
  81. if '$VisuCoreDataMin' in params: del params['$VisuCoreDataMin']
  82. if '$VisuCoreDataMax' in params: del params['$VisuCoreDataMax']
  83. if '$VisuCoreDataOffs' in params: del params['$VisuCoreDataOffs']
  84. if '$VisuCoreDataSlope' in params: del params['$VisuCoreDataSlope']
  85. if '$VisuAcqImagePhaseEncDir' in params: del params['$VisuAcqImagePhaseEncDir']
  86. for key in params.keys():
  87. pardim = params[key][0]
  88. parval = params[key][1]
  89. if (len(pardim) > 0) and (len(parval) > 0) and (parval[0] == '<'):
  90. params[key][1] = parval.replace('<', '"').replace('>', '"')
  91. elif (len(parval) > 0) and (parval[0] == '('):
  92. params[key][1] = parval.replace('<', '"').replace('>', '"')
  93. params[key] = params[key][1]
  94. return params
  95. def getXML(filename, writeFile=False):
  96. """
  97. Writes header dictionary to xml format
  98. Parameters:
  99. ==========
  100. filename: Bruker ParaVision '2dseq' file
  101. writeFile: Boolean, if 'False' return string containing xml-header, else save to file
  102. """
  103. path = os.path.abspath(os.path.dirname(filename))
  104. # Parse all parameter files
  105. header_acqp = parsePV(os.path.join(path, '..', '..', 'acqp'))
  106. header_method = parsePV(os.path.join(path, '..', '..', 'method'))
  107. #header_d3proc = parsePV(os.path.join(path, 'd3proc')) # removed for PV6
  108. header_visu = parsePV(os.path.join(path, 'visu_pars'))
  109. header = {'Scaninfo': {}}
  110. header['Scaninfo']['acqp'] = header_acqp
  111. header['Scaninfo']['method'] = header_method
  112. #header['Scaninfo']['d3proc'] = header_d3proc # removed for PV6
  113. header['Scaninfo']['visu_pars'] = header_visu
  114. xml = createXML(header, '<?xml version="1.0"?>\n')
  115. if writeFile:
  116. f = open('scaninfo.xml', 'w')
  117. f.write(xml)
  118. f.close()
  119. else:
  120. return xml
  121. def getNiftiHeader(params, sc=10):
  122. """
  123. Returns necessary header parameters for NIfTI generation ()
  124. Parameters:
  125. ===========
  126. filename: header returned from parser
  127. sc: scales pixel dimension (defaults to 10 for animal imaging)
  128. """
  129. # List of 'VisuCoreSize' parameter strings
  130. if params == []:
  131. return
  132. CoreSize = str.split(params['VisuCoreSize'])
  133. if params['VisuCoreDimDesc'] == 'spectroscopic':
  134. print("spectroscopic")
  135. #quit(42)
  136. return params['VisuStudyDate'], int(CoreSize[0]), 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 8
  137. # Dimensions
  138. nX = int(CoreSize[0])
  139. nY = int(CoreSize[1])
  140. nZ = 1
  141. nT = 1
  142. # FrameGroup dimensions
  143. if 'VisuFGOrderDescDim' in params:
  144. if int(params['VisuFGOrderDescDim']) > 0:
  145. FGOrderDesc = params['VisuFGOrderDesc'][1:-1].split(') (')
  146. #FGOrderDesc = map(lambda item: item.split(', '), FGOrderDesc)
  147. FGOrderDesc = [item.split(', ') for item in FGOrderDesc]
  148. #frameDims = map(lambda item: int(item[0]), FGOrderDesc)
  149. frameDims = [int(item[0]) for item in FGOrderDesc]
  150. # Number of slices
  151. nZ = frameDims[0]
  152. if int(params['VisuFGOrderDescDim']) > 1:
  153. nT = frameDims[1]
  154. if int(params['VisuFGOrderDescDim']) > 2:
  155. nT *= frameDims[2]
  156. # Voxel dimensions
  157. extent = params['VisuCoreExtent'].split()
  158. dX = sc * float(extent[0]) / nX
  159. dY = sc * float(extent[1]) / nY
  160. VisuCoreSlicePacksSliceDist = params.get('VisuCoreSlicePacksSliceDist')
  161. #print("VisuCoreSlicePacksSliceDist",VisuCoreSlicePacksSliceDist)
  162. #print("VisuCoreFrameThickness", params['VisuCoreFrameThickness'])
  163. if VisuCoreSlicePacksSliceDist is None:
  164. dZ = sc * float(params['VisuCoreFrameThickness'])
  165. else:
  166. # Slice thickness inclusive gap (PV6)
  167. VisuCoreSlicePacksSliceDist=VisuCoreSlicePacksSliceDist.split()[0]
  168. #print("VisuCoreSlicePacksSliceDist",VisuCoreSlicePacksSliceDist)
  169. dZ = sc * float(VisuCoreSlicePacksSliceDist)
  170. if 'VisuAcqRepetitionTime' in params:
  171. if (nT > 1) and (float(params['VisuAcqRepetitionTime']) > 0 ):
  172. dT = float(params['VisuAcqRepetitionTime']) / 1000
  173. else:
  174. dT=0
  175. else:
  176. dT = 0
  177. if int(params['VisuCoreDim']) == 3:
  178. nZ = int(CoreSize[2])
  179. nT = 1
  180. frameDims = None
  181. if 'VisuFGOrderDescDim' in params:
  182. if int(params['VisuFGOrderDescDim']) > 0:
  183. nT = frameDims[0]
  184. dZ = sc * float(extent[2]) / nZ
  185. if (nT > 1) and (float(params['VisuAcqRepetitionTime']) > 1 ):
  186. dT = float(params['VisuAcqRepetitionTime']) / 1000
  187. else:
  188. dT = 0
  189. DT = 4
  190. if params['VisuCoreWordType'] == '_8BIT_UNSGN_INT': DT = 'int8'
  191. if params['VisuCoreWordType'] == '_16BIT_SGN_INT' : DT = 'int16'
  192. if params['VisuCoreWordType'] == '_32BIT_SGN_INT' : DT = 'int32'
  193. if params['VisuCoreWordType'] == '_32BIT_FLOAT' : DT = 'float32'
  194. tmp = params['studyname'] + '.' + params['expno'] + '.' + params['procno']
  195. return tmp, nX, nY, nZ, nT, dX, dY, dZ, dT, 0, 0, 0, DT
  196. ''' def getNiftiHeader_md(params, sc=10):
  197. """
  198. Returns necessary header parameters for NIfTI generation ()
  199. Parameters:
  200. ===========
  201. filename: header returned from parser
  202. sc: scales pixel dimension (defaults to 10 for animal imaging)
  203. """
  204. # List of 'VisuCoreSize' parameter strings
  205. global frameDims
  206. CoreSize = str.split(params['VisuCoreSize'])
  207. if params['VisuCoreDimDesc'] == 'spectroscopic':
  208. return params['VisuStudyName'], int(CoreSize[0]), 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 8
  209. # Dimensions
  210. nX = int(CoreSize[0])
  211. nY = int(CoreSize[1])
  212. nZ = 1
  213. nT = 1
  214. # FrameGroup dimensions
  215. if int(params['VisuFGOrderDescDim']) > 0:
  216. FGOrderDesc = params['VisuFGOrderDesc'][1:-1].split(') (')
  217. #FGOrderDesc = map(lambda item: item.split(', '), FGOrderDesc)
  218. FGOrderDesc = [item.split(', ') for item in FGOrderDesc]
  219. #frameDims = map(lambda item: int(item[0]), FGOrderDesc)
  220. frameDims = [int(item[0]) for item in FGOrderDesc]
  221. # Number of slices
  222. nZ = frameDims[0]
  223. if int(params['VisuFGOrderDescDim']) > 1:
  224. nT = frameDims[1]
  225. # Voxel dimensions
  226. extent = params['VisuCoreExtent'].split()
  227. dX = sc * float(extent[0]) / nX
  228. dY = sc * float(extent[1]) / nY
  229. VisuCoreSlicePacksSliceDist = params.get('VisuCoreSlicePacksSliceDist')
  230. print("VisuCoreSlicePacksSliceDist",VisuCoreSlicePacksSliceDist)
  231. if VisuCoreSlicePacksSliceDist is None:
  232. dZ = sc * float(params['VisuCoreFrameThickness'])
  233. else:
  234. # Slice thickness inclusive gap (PV6)
  235. dz = sc * float(VisuCoreSlicePacksSliceDist)
  236. print("dz",dz)
  237. if (nT > 1) and (float(params['VisuAcqRepetitionTime']) > 0 ):
  238. dT = float(params['VisuAcqRepetitionTime']) / 1000
  239. else:
  240. dT = 0
  241. if int(params['VisuCoreDim']) == 3:
  242. nZ = int(CoreSize[2])
  243. nT = 1
  244. if int(params['VisuFGOrderDescDim']) > 0:
  245. nT = frameDims[0]
  246. dZ = sc * float(extent[2]) / nZ
  247. if (nT > 1) and (float(params['VisuAcqRepetitionTime']) > 1 ):
  248. dT = float(params['VisuAcqRepetitionTime']) / 1000
  249. else:
  250. dT = 0
  251. CoreWordType = params['VisuCoreWordType']
  252. if CoreWordType == '_8BIT_UNSGN_INT': DT = 'DT_UINT8' # 2: 'int8'
  253. elif CoreWordType == '_16BIT_SGN_INT': DT = 'DT_INT16' # 4: 'int16'
  254. elif CoreWordType == '_32BIT_SGN_INT': DT = 'DT_INT32' # 8: 'int32'
  255. elif CoreWordType == '_32BIT_FLOAT': DT = 'DT_FLOAT32' # 16: 'float32'
  256. else: DT = 4
  257. tmp = params['studyname'] + '.' + params['expno'] + '.' + params['procno']
  258. return (tmp, nX, nY, nZ, nT, dX, dY, dZ, dT, 0, 0, 0, DT)
  259. '''
  260. def getRotMatrix(filename):
  261. """
  262. Returns rotation matrix for image registration
  263. Parameters:
  264. ===========
  265. filename: visu_pars file to parse
  266. sc : scales pixel dimension (defaults to 10 for animal imaging)
  267. """
  268. params = parsePV(filename)
  269. orientation = map(float, params['VisuCoreOrientation'].split())
  270. if not 'VisuCorePosition' in params:
  271. return np.array([0.0, 0.0, 0.0, 0.0])
  272. position = map(float, params['VisuCorePosition'].split())
  273. orientation = np.array(orientation[0:9]).reshape((3, 3))
  274. position = np.array(position[0:3]).reshape((3, 1))
  275. rotMatrix = np.append(orientation, position, axis=1)
  276. rotMatrix = np.append(rotMatrix, np.array([0.0, 0.0, 0.0, 1.0]).reshape(1, 4), axis=0)
  277. return rotMatrix
  278. def writeRotMatrix(rotMatrix, filename):
  279. fid = open(filename, 'w')
  280. np.savetxt(fid, rotMatrix, fmt='%-7.2f')
  281. fid.close()
  282. """
  283. if __name__ == '__main__':
  284. import argparse
  285. parser = argparse.ArgumentParser(description='Parse Bruker parameter files')
  286. parser.add_argument('file', type=str, help='name of parameter file (2dseq for all)')
  287. parser.add_argument('-t', '--type', type=str, default = "xml", help='nifti, xml')
  288. args = parser.parse_args()
  289. if args.type == "nifti" and os.path.basename(args.file)=="visu_pars":
  290. print(getNiftiHeader(args.file))
  291. elif args.type == "mat" and os.path.basename(args.file)=="visu_pars":
  292. print(getRotMatrix(args.file))
  293. elif args.type == "xml":
  294. print(getXML(args.file))
  295. else:
  296. print("Hmmm, works not that way ;)")
  297. """