1 2from os.path import splitext 3import re 4import warnings 5import numpy as np 6from nibabel.tmpdirs import InTemporaryDirectory 7 8 9def read_bvals_bvecs(fbvals, fbvecs): 10 """Read b-values and b-vectors from disk. 11 12 Parameters 13 ---------- 14 fbvals : str 15 Full path to file with b-values. None to not read bvals. 16 fbvecs : str 17 Full path of file with b-vectors. None to not read bvecs. 18 19 Returns 20 ------- 21 bvals : array, (N,) or None 22 bvecs : array, (N, 3) or None 23 24 Notes 25 ----- 26 Files can be either '.bvals'/'.bvecs' or '.txt' or '.npy' (containing 27 arrays stored with the appropriate values). 28 29 """ 30 # Loop over the provided inputs, reading each one in turn and adding them 31 # to this list: 32 vals = [] 33 for this_fname in [fbvals, fbvecs]: 34 # If the input was None or empty string, we don't read anything and 35 # move on: 36 if this_fname is None or not this_fname: 37 vals.append(None) 38 continue 39 40 if not isinstance(this_fname, str): 41 raise ValueError('String with full path to file is required') 42 43 base, ext = splitext(this_fname) 44 if ext in ['.bvals', '.bval', '.bvecs', '.bvec', '.txt', 45 '.eddy_rotated_bvecs', '']: 46 with open(this_fname, 'r') as f: 47 content = f.read() 48 # We replace coma and tab delimiter by space 49 with InTemporaryDirectory(): 50 tmp_fname = "tmp_bvals_bvecs.txt" 51 with open(tmp_fname, 'w') as f: 52 f.write(re.sub(r'(\t|,)', ' ', content)) 53 vals.append(np.squeeze(np.loadtxt(tmp_fname))) 54 elif ext == '.npy': 55 vals.append(np.squeeze(np.load(this_fname))) 56 else: 57 e_s = "File type %s is not recognized" % ext 58 raise ValueError(e_s) 59 60 # Once out of the loop, unpack them: 61 bvals, bvecs = vals[0], vals[1] 62 63 # If bvecs is None, you can just return now w/o making more checks: 64 if bvecs is None: 65 return bvals, bvecs 66 67 if 3 not in bvecs.shape: 68 raise IOError('bvec file should have three rows') 69 if bvecs.ndim != 2: 70 bvecs = bvecs[None, ...] 71 bvals = bvals[None, ...] 72 msg = "Detected only 1 direction on your bvec file. For diffusion " 73 msg += "dataset, it is recommended to have at least 3 directions." 74 msg += "You may have problems during the reconstruction step." 75 warnings.warn(msg) 76 if bvecs.shape[1] != 3 and bvecs.shape[1] > bvecs.shape[0]: 77 bvecs = bvecs.T 78 79 # If bvals is None, you don't need to check that they have the same shape: 80 if bvals is None: 81 return bvals, bvecs 82 83 if len(bvals.shape) > 1: 84 raise IOError('bval file should have one row') 85 86 if bvals.shape[0] != bvecs.shape[0]: 87 raise IOError('b-values and b-vectors shapes do not correspond') 88 89 return bvals, bvecs 90