pv_conv2Nifti.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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
  10. import sys
  11. import numpy as np
  12. import nibabel as nib
  13. import nibabel.nifti1 as nii
  14. import pv_parseBruker_md_np as pB
  15. import P2_IDLt2_mapping as mapT2
  16. class Bruker2Nifti:
  17. def __init__(self, study, expno, procno, rawfolder, procfolder, ftype='NIFTI_GZ'):
  18. self.study = study
  19. self.expno = str(expno)
  20. self.procno = str(procno)
  21. self.rawfolder = rawfolder
  22. self.procfolder = procfolder
  23. self.ftype = ftype
  24. def read_2dseq(self, map_raw=False, pv6=False, sc=1.0):
  25. study = self.study
  26. expno = self.expno
  27. procno = self.procno
  28. rawfolder = self.rawfolder
  29. self.acqp = pB.parsePV(os.path.join(rawfolder, study, expno, 'acqp'))
  30. self.method = pB.parsePV(os.path.join(rawfolder, study, expno, 'method'))
  31. self.subject = pB.parsePV(os.path.join(rawfolder, study, 'subject'))
  32. # get header information
  33. datadir = os.path.join(rawfolder, study, expno, 'pdata', procno)
  34. #self.d3proc = pB.parsePV(os.path.join(datadir, 'd3proc')) # removed for PV6
  35. self.visu_pars = pB.parsePV(os.path.join(datadir, 'visu_pars'))
  36. hdr = pB.getNiftiHeader(self.visu_pars, sc=sc)
  37. #print("hdr:", hdr)
  38. if hdr is None or not isinstance(hdr[12], str):
  39. return
  40. # read '2dseq' file
  41. f_id = open(os.path.join(datadir, '2dseq'), 'rb')
  42. data = np.fromfile(f_id, dtype=np.dtype(hdr[12])).reshape(hdr[1], hdr[2], hdr[3], hdr[4], order='F')
  43. f_id.close()
  44. # map to raw data range (PV6)
  45. if map_raw:
  46. visu_core_data_slope = np.array(map(float, self.visu_pars['VisuCoreDataSlope'].split()), dtype=np.float32)
  47. visu_core_data_offs = np.array(map(float, self.visu_pars['VisuCoreDataOffs'].split()), dtype=np.float32)
  48. visu_core_data_shape = list(data.shape)
  49. visu_core_data_shape[:2] = (1, 1)
  50. if pv6:
  51. data = data / visu_core_data_slope.reshape(visu_core_data_shape)
  52. else:
  53. data = data * visu_core_data_slope.reshape(visu_core_data_shape)
  54. data = data + visu_core_data_offs.reshape(visu_core_data_shape)
  55. # NIfTI image
  56. #data = np.flip(data, axis=(1,2)) #flip z-axis
  57. nim = nii.Nifti1Image(data, None)
  58. # NIfTI header
  59. #header = nim.header
  60. header = nim._header
  61. #print("header:"); print(header)
  62. header['pixdim'] = [0.0, hdr[5], hdr[6], hdr[7], hdr[8], 0.0, 0.0, 0.0]
  63. #nim.setXYZUnit('mm')
  64. header.set_xyzt_units(xyz='mm', t=None)
  65. #nim.header = header
  66. #header = nim.get_header()
  67. #print("header:"); print(header)
  68. # write header in xml structure
  69. #xml = pB.getXML(datadir + "/")
  70. xml = pB.getXML(os.path.join(datadir, 'visu_pars'))
  71. #print("xml:"); print(xml)
  72. # add protocol information (method, acqp, visu_pars, d3proc) to Nifti's header extensions
  73. #nim.extensions += ('comment', xml)
  74. #extension = nii.Nifti1Extension('comment', xml)
  75. self.hdr = hdr
  76. self.nim = nim
  77. self.xml = xml
  78. def save_nifti(self, subfolder=''):
  79. procfolder = os.path.join(self.procfolder, self.study)
  80. if not os.path.isdir(procfolder):
  81. os.mkdir(procfolder)
  82. if "Localizer" in self.acqp['ACQ_protocol_name']:
  83. procfolder = os.path.join(self.procfolder, self.study, "Localizer")
  84. elif "DTI" in self.acqp['ACQ_protocol_name'] or "Diffusion" in self.acqp['ACQ_protocol_name']:
  85. procfolder = os.path.join(self.procfolder, self.study, "DTI")
  86. elif "fMRI" in self.acqp['ACQ_protocol_name']:
  87. procfolder = os.path.join(self.procfolder, self.study, "fMRI")
  88. elif "Turbo" in self.acqp['ACQ_protocol_name']:
  89. procfolder = os.path.join(self.procfolder, self.study, "T2w")
  90. elif "MSME" in self.acqp['ACQ_protocol_name']:
  91. procfolder = os.path.join(self.procfolder, self.study, "T2map")
  92. else:
  93. procfolder = os.path.join(self.procfolder, self.study, "Others")
  94. if not os.path.isdir(procfolder):
  95. os.mkdir(procfolder)
  96. if self.ftype == 'NIFTI_GZ': ext = 'nii.gz'
  97. elif self.ftype == 'NIFTI': ext = 'nii'
  98. elif self.ftype == 'ANALYZE': ext = 'img'
  99. else: ext = 'nii.gz'
  100. fname = '.'.join([self.study, self.expno, self.procno, ext])
  101. # write Nifti file
  102. print(os.path.join(procfolder, fname))
  103. if not hasattr(self, 'nim'):
  104. return
  105. nib.save(self.nim, os.path.join(procfolder, fname))
  106. return os.path.join(procfolder, fname)
  107. def save_table(self, subfolder= ''):
  108. procfolder = os.path.join(self.procfolder, self.study)
  109. if not os.path.isdir(procfolder):
  110. os.mkdir(procfolder)
  111. procfolder = os.path.join(self.procfolder, self.study, subfolder)
  112. if not os.path.isdir(procfolder):
  113. os.mkdir(procfolder)
  114. #dw_bval_each = float(self.method['PVM_DwBvalEach'])
  115. if 'PVM_DwEffBval' in self.method:
  116. dw_eff_bval = np.array(list(map(float, self.method['PVM_DwEffBval'].split())), dtype=np.float32)
  117. #print("dw_bval_each:", dw_bval_each)
  118. #print("dw_eff_bval:"); print(dw_eff_bval)
  119. if 'PVM_DwAoImages' in self.method:
  120. dw_ao_images = int(self.method['PVM_DwAoImages'])
  121. if 'PVM_DwNDiffDir' in self.method:
  122. dw_n_diff_dir = int(self.method['PVM_DwNDiffDir'])
  123. #print("dw_ao_images:", dw_ao_images)
  124. #print("dw_n_diff_dir:", dw_n_diff_dir)
  125. if 'PVM_DwDir' in self.method:
  126. dw_dir = np.array(list(map(float, self.method['PVM_DwDir'].split())), dtype=np.float32)
  127. dw_dir = dw_dir.reshape((dw_n_diff_dir, 3))
  128. nd = dw_ao_images + dw_n_diff_dir
  129. bvals = np.zeros(nd, dtype=np.float32)
  130. dwdir = np.zeros((nd, 3), dtype=np.float32)
  131. bvals[dw_ao_images:] = dw_eff_bval[dw_ao_images:]
  132. dwdir[dw_ao_images:] = dw_dir
  133. fname = '.'.join([self.study, self.expno, self.procno, 'btable', 'txt'])
  134. print(os.path.join(procfolder, fname))
  135. # Open btable file to write binary (windows format)
  136. #fid = open(os.path.join(procfolder, fname), 'wb') - py 2.6
  137. fid = open(os.path.join(procfolder, fname),mode='w',buffering=-1)
  138. for i in range(nd):
  139. fid.write("%.4f" % (bvals[i],) + " %.8f %.8f %.8f" % tuple(dwdir[i]))
  140. #print("%.4f" % (bvals[i],) + " %.8f %.8f %.8f" % tuple(dwdir[i]), end="\r\n", file=fid) - py 2.6
  141. # Close file
  142. fid.close()
  143. fname = '.'.join([self.study, self.expno, self.procno, 'bvals', 'txt'])
  144. print(os.path.join(procfolder, fname))
  145. # Open bvals file to write binary (unix format)
  146. fid = open(os.path.join(procfolder, fname), mode='w', buffering=-1)
  147. #fid = open(os.path.join(procfolder, fname), 'wb') - py 2.6
  148. fid.write(" ".join("%.4f" % (bvals[i],) for i in range(nd)))
  149. #print(" ".join("%.4f" % (bvals[i],) for i in range(nd)), end=chr(10), file=fid) - py 2.6
  150. # Close bvals file
  151. fid.close()
  152. fname = '.'.join([self.study, self.expno, self.procno, 'bvecs', 'txt'])
  153. print(os.path.join(procfolder, fname))
  154. # Open bvecs file to write binary (unix format)
  155. fid = open(os.path.join(procfolder, fname), mode='w', buffering=-1)
  156. #fid = open(os.path.join(procfolder, fname), 'wb') - py 2.6
  157. for k in range(3):
  158. fid.write(" ".join("%.8f" % (dwdir[i,k],) for i in range(nd)))
  159. #print(" ".join("%.8f" % (dwdir[i,k],) for i in range(nd)), end=chr(10), file=fid)- py 2.6
  160. # Close bvecs file
  161. fid.close()
  162. if __name__ == "__main__":
  163. import argparse
  164. parser = argparse.ArgumentParser(description='Convert ParaVision to NIfTI')
  165. requiredNamed = parser.add_argument_group('Required named arguments')
  166. requiredNamed.add_argument('-i','--input_folder', help='raw data folder')
  167. # parser.add_argument('-o','--output_folder', help='output data folder')
  168. # parser.add_argument('study', help='study name')
  169. # parser.add_argument('expno', help='experiment number')
  170. # parser.add_argument('procno', help='processed (reconstructed) images number')
  171. parser.add_argument('-f','--model',
  172. help='T2_2p (default) : Two parameter T2 decay S(t) = S0 * exp(-t/T2)\n'
  173. 'T2_3p : Three parameter T2 decay S(t) = S0 * exp(-t/T2) + C'
  174. , nargs='?', const='T2_2p', type=str, default='T2_2p')
  175. parser.add_argument('-u','--upLim', help='upper limit of TE - default: 100', nargs='?', const=100, type=int, default=100)
  176. parser.add_argument('-s','--snrLim', help='upper limit of SNR - default: 1.5', nargs='?', const=1.5, type=float,
  177. default=1.5)
  178. parser.add_argument('-k','--snrMethod', help='Brummer ,Chang, Sijbers', nargs='?', const='Brummer', type=str,
  179. default='Brummer')
  180. parser.add_argument('-m', '--map_raw', action='store_true', help='get the real values')
  181. parser.add_argument('-p', '--pv6', action='store_true', help='ParaVision 6')
  182. parser.add_argument('-t', '--table', action='store_true', help='save b-values and diffusion directions')
  183. args = parser.parse_args()
  184. input_folder = None
  185. # raw data folder
  186. if args.input_folder is not None:
  187. input_folder = args.input_folder
  188. if not os.path.isdir(input_folder):
  189. sys.exit("Error: '%s' is not an existing directory." % (input_folder,))
  190. list = os.listdir(input_folder)
  191. listOfScans = [s for s in list if s.isdigit()]
  192. if len(listOfScans) == 0:
  193. sys.exit("Error: '%s' contains no numbered scans." % (input_folder,))
  194. print('Start to process '+str(len(listOfScans))+' scans...')
  195. procno ='1'
  196. study=input_folder.split('/')[len(input_folder.split('/'))-1]
  197. print(study)
  198. img = []
  199. for expno in np.sort(listOfScans):
  200. path = os.path.join(input_folder, expno, 'pdata', procno)
  201. if not os.path.isdir(path):
  202. sys.exit("Error: '%s' is not an existing directory." % (path,))
  203. if os.path.exists(os.path.join(path,'2dseq')):
  204. img = Bruker2Nifti(study, expno, procno, os.path.split(input_folder)[0], input_folder, ftype='NIFTI_GZ')
  205. img.read_2dseq(map_raw=args.map_raw, pv6=args.pv6)
  206. resPath = img.save_nifti()
  207. if resPath is None: continue
  208. if 'VisuAcqEchoTime' in img.visu_pars:
  209. echoTime = img.visu_pars['VisuAcqEchoTime']
  210. echoTime = np.fromstring(echoTime, dtype=float, sep=' ')
  211. if len(echoTime) > 3:
  212. mapT2.getT2mapping(resPath,args.model,args.upLim,args.snrLim,args.snrMethod,echoTime)
  213. if resPath is not None:
  214. pathlog = os.path.dirname(os.path.dirname(resPath))
  215. pathlog = os.path.join(pathlog, 'data.log')
  216. logfile = open(pathlog, 'w')
  217. logfile.write(img.subject['coilname'])
  218. logfile.close()