def mask(varr, threshold=None, mask_out=False): """Create mask on image: set voxel data to 0 where image is below threshold. Noise threshold is assumed if set to None """ def _vertical_project(data): data = np.mean(data,axis=(0,1)) return data def _guess_threshold(vertical_projection): # TODO account for noise variance in a more normal way... thresh = 2 * np.min(vertical_projection) return thresh # guessing a threshold if threshold == None: vprint('Making mask, assuming noise to be in the'+ 'top voxels when determining threshold') orig_space = varr.space varr.to_anatomical() magnitude = vj.core.recon.ssos(varr.data) # simple combine rcvrs # top down is dim2 backwards vert_proj = _vertical_project(magnitude) vert_min = np.min(vert_proj) vert_max = np.max(vert_proj) threshold = _guess_threshold(vert_proj) # masking mask = np.zeros_like(varr.data,dtype='float32') mask[magnitude > threshold] = 1.0 # filling mistakenly masked inner voxels: mask = median_filter(mask, size=3) # 2nd pass mask = median_filter(mask, size=3) varr.data = varr.data * mask # transform back to original space for consistency if orig_space == 'anatomical': pass elif orig_space == 'local': varr.to_local() elif orig_space == 'global': # TODO this is not ready though varr.to_global() # return results if mask_out: return varr, mask else: return varr
def write_nifti(varr,out, save_procpar=True,\ save_complex=False,\ rcvr_to_timedim=False,\ complex_pairs='magn-phase',\ combine_rcvrs='ssos',\ cut_to_3d=False): """Write Nifti1 files from vnmrjpy.varray. You can specify the data to be saved Args: varray -- vnmrjpy.varray object containing the data out -- output path save_procpar -- (boolean) saves procpar json in the same directory save_complex -- save complex data in real-imag pairs along a dimension only4dim -- compress data into 4 dimensions , if possible """ # sanity check for input options if combine_rcvrs != None and save_complex == True: #TODO pass # check type of data if len(varr.data.shape) < 3: raise (Exception('Data dimension error')) if not varr.is_kspace_complete: raise (Exception('K-space is not completed')) #------------------ making the Nifti affine and header----------------- # main write if '.nii' in out: out_name = str(out) elif '.nii.gz' in out: out_name = str(out[:-3]) else: out_name = str(out) + '.nii' data = varr.data # OPTION : save_complex---------------------------------------------------- # to save complex data, real and imaginary parts are added # to receiver channel if save_complex: rch = 4 if complex_pairs == 'real-imag': data = np.concatenate([np.real(data), np.imag(data)], axis=rch) elif complex_pairs == 'magn-phase': magn = np.absolute(data) phase = np.arctan2(np.imag(data), np.real(data)) data = np.concatenate([magn, phase], axis=rch) else: raise (Exception('Wrong complex_pairs')) # OPTION : rcvr_to_timedim------------------------------------------------- if rcvr_to_timedim: rch = 4 timedim = data.shape[rch] * data.shape[3] newshape = (data.shape[0], data.shape[1], data.shape[2], timedim) data = np.reshape(data, newshape, order='c') # OPTION : combine_rcvrs--------------------------------------------------- if combine_rcvrs == 'ssos': data = vj.core.recon.ssos(data, axis=4) # cut dimensions more than 3. This is mainly for use with certain FSL calls if cut_to_3d: if len(data.shape) == 4: data = data[..., 0] elif len(data.shape) == 5: data = data[..., 0, 0] else: raise (Exception('Not implemented')) # set affine as none to use the one in header img = nib.Nifti1Image(data, None, varr.nifti_header) # create directories if not exists basedir = out_name.rsplit('/', 1)[0] if not os.path.exists(basedir): os.makedirs(basedir) nib.save(img, out_name) os.system('gzip -f ' + str(out_name)) vprint('write_nifti : ' + str(out_name) + ' saved ... ') if save_procpar: new_pp = out_name[:-3] + 'procpar' try: copyfile(varr.procpar, new_pp) except: pdname = out_name[:-3] + 'json' vj.core.utils.savepd(varr.pd, pdname)
def to_kspace(self, raw=False, zerofill=True, method='vnmrjpy', epiref_type='default',epinav='default'): """Build the k-space from the raw fid data and procpar. Raw fid_data is numpy.ndarray(blocks, traces * np) format. Should be untangled based on 'seqcon' or 'seqfil' parameters. seqcon chars refer to (echo, slice, Pe1, Pe2, Pe3) For compatibility, an interface to Xrecon is provided. Set 'method' to 'xrecon' Args: raw zerofill method -- 'xrecon', or 'vnmrjpy' note: PREVIOUS: ([rcvrs, phase, read, slice, echo*time]) NOW: ([phase, read, slice, echo*time, rcvrs]) """ # ====================== Child functions, helpers====================== def _is_interleaved(ppdict): res = (int(ppdict['sliceorder']) == 1) return res def _is_evenslices(ppdict): try: res = (int(ppdict['ns']) % 2 == 0) except: res = (int(ppdict['pss']) % 2 == 0) return res def make_im2D(): """Child method of 'make', provides the same as vnmrj im2Drecon""" p = self.pd rcvrs = int(p['rcvrs'].count('y')) (read, phase, slices) = (int(p['np'])//2,int(p['nv']),int(p['ns'])) if 'ne' in p.keys(): echo = int(p['ne']) else: echo = 1 time = 1 # this is the old shape which worked, better to reshape at the end finalshape = (rcvrs, phase, read, slices,echo*time*array_length) final_kspace = np.zeros(finalshape,dtype='complex64') for i in range(array_length): kspace = self.data[i*blocks:(i+1)*blocks,...] if p['seqcon'] == 'nccnn': shape = (rcvrs, phase, slices, echo*time, read) kspace = np.reshape(kspace, shape, order='C') kspace = np.moveaxis(kspace, [0,1,4,2,3], [0,1,2,3,4]) elif p['seqcon'] == 'nscnn': raise(Exception('not implemented')) elif p['seqcon'] == 'ncsnn': preshape = (rcvrs, phase, slices*echo*time*read) shape = (rcvrs, phase, slices, echo*time, read) kspace = np.reshape(kspace, preshape, order='F') kspace = np.reshape(kspace, shape, order='C') kspace = np.moveaxis(kspace, [0,1,4,2,3], [0,1,2,3,4]) elif p['seqcon'] == 'ccsnn': preshape = (rcvrs, phase, slices*echo*time*read) shape = (rcvrs, phase, slices, echo*time, read) kspace = np.reshape(kspace, preshape, order='F') kspace = np.reshape(kspace, shape, order='C') kspace = np.moveaxis(kspace, [0,1,4,2,3], [0,1,2,3,4]) else: raise(Exception('Not implemented yet')) if _is_interleaved(p): # 1 if interleaved slices if _is_evenslices(p): c = np.zeros(kspace.shape, dtype='complex64') c[...,0::2,:] = kspace[...,:slices//2,:] c[...,1::2,:] = kspace[...,slices//2:,:] kspace = c else: c = np.zeros(kspace.shape, dtype='complex64') c[...,0::2,:] = kspace[...,:(slices+1)//2,:] c[...,1::2,:] = kspace[...,(slices-1)//2+1:,:] kspace = c final_kspace[...,i*echo*time:(i+1)*echo*time] = kspace self.data = final_kspace # additional reordering self.data = np.moveaxis(self.data,[0,1,2,3,4],[4,1,0,2,3]) # swap axes 0 and 1 so phase, readout etc is the final order self.data = np.swapaxes(self.data,0,1) return self def make_im2Dcs(**kwargs): """ These (*cs) are compressed sensing variants """ def decode_skipint_2D(skipint): pass raise(Exception('not implemented')) def make_im2Depi(**kwargs): p = self.pd # count navigator echos, also there is a unused one if p['navigator'] == 'y': pluspe = 1 + int(p['nnav']) # navigator echo + unused else: pluspe = 1 # unused only # init main params # ------------------------------------------------------- comp_seg = p['cseg'] altread = p['altread'] nseg = int(p['nseg']) # number of segments etl = int(p['etl']) # echo train length kzero = int(p['kzero']) images = int(p['images']) # repetitions rcvrs = int(p['rcvrs'].count('y')) time = len(p['image']) # total volumes including references npe = etl + pluspe # total phase encode lines per shot # getting phase encode scheme if p['pescheme'] == 'l': pescheme = 'linear' elif p['pescheme'] == 'c': pescheme = 'centric' else: pescheme = None #TODO make petable? if p['petable'] == 'y': petab_file = None #TODO phase_order = vj.core.epitools.\ _get_phaseorder_frompetab(petab_file) else: phase_order = vj.core.epitools.\ _get_phaseorder_frompar(nseg,npe,etl,kzero) # init final shape if int(p['pro']) != 0: (read, phase, slices) = (int(p['nread']), \ int(p['nphase']), \ int(p['ns'])) else: (read, phase, slices) = (int(p['nread'])//2, \ int(p['nphase']), \ int(p['ns'])) finalshape = (read, phase, slices,time*array_length, rcvrs) final_kspace = np.zeros(finalshape,dtype='complex64') #navshape = (rcvrs, int(p['nnav']),read,slices, # echo*time*array_length) #nav = np.zeros(navshape,dtype='complex64') #full set of nav echos # sanity check if int(self.fid_header['np']) != int((etl +pluspe)*read*2): raise Exception("np and kspace format doesn't match") for i in range(array_length): # arrange to kspace, but don't do corrections kspace = self.data[i*blocks:(i+1)*blocks,...] # this case repetitions are in different blocks if p['seqcon'] == 'ncnnn': #preshape = (rcvrs, time, nseg, slices, npe, read) preshape = (time, rcvrs, nseg, slices, npe, read) kspace = np.reshape(kspace, preshape, order='c') # utility swaps... kspace = np.swapaxes(kspace, 2,3) kspace = np.swapaxes(kspace, 0,1) # dims now: [rcvrs,time,nslices, nseg, phase, read] # reverse odd readout lines kspace = vj.core.epitools._reverse_odd(kspace,\ read_dim=5,phase_dim=4) # correct reversed echos for main ghost corr kspace = vj.core.epitools._navigator_scan_correct(kspace,p) # navigator correct # this is for intersegment, and additional ghost corr kspace = vj.core.epitools.\ _navigator_echo_correct(kspace,npe,etl,method='single') # remove navigator echos kspace = vj.core.epitools._remove_navigator_echos(kspace,etl) kspace = vj.core.epitools._zerofill(kspace, phase, nseg) # start combining segments kspace = vj.core.epitools._combine_segments(kspace,pescheme) # reshape to [read,phase,slice,time,rcvrs] kspace = vj.core.epitools._reshape_stdepi(kspace) # correct for interleaved slices kspace = vj.core.epitools._correct_ilepi(kspace,p) kspace = vj.core.epitools._refcorrect(\ kspace,p,method=epiref_type) else: raise(Exception('This seqcon not implemented in epip')) # -------------------epi kspace preprocessing------------------ # TODO check 'image' after array merginf final_kspace[...,i*time:(i+1)*time,:] = kspace # --------------------- kspace finished---------------------------- self.data = final_kspace return self def make_im2Depics(): raise(Exception('not implemented')) def make_im2Dfse(**kwargs): p = self.pd #petab = vj.util.getpetab(self.procpar,is_procpar=True) petab = vj.core.read_petab(self.pd) nseg = int(p['nseg']) # seqgments etl = int(p['etl']) # echo train length kzero = int(p['kzero']) images = int(p['images']) # repetitions (read, phase, slices) = (int(p['np'])//2,int(p['nv']),int(p['ns'])) # setting time params echo = 1 time = images phase_sort_order = np.reshape(np.array(petab),petab.size,order='C') # shift to positive phase_sort_order = phase_sort_order + phase_sort_order.size//2-1 finalshape = (rcvrs, phase, read, slices,echo*time*array_length) final_kspace = np.zeros(finalshape,dtype='complex64') for i in range(array_length): kspace = self.data[i*blocks:(i+1)*blocks,...] if p['seqcon'] == 'nccnn': #TODO check for images > 1 preshape = (rcvrs, phase//etl, slices, echo*time, etl, read) shape = (rcvrs, echo*time, slices, phase, read) kspace = np.reshape(kspace, preshape, order='C') kspace = np.swapaxes(kspace,1,3) kspace = np.reshape(kspace, shape, order='C') # shape is [rcvrs, phase, slices, echo*time, read] kspace = np.swapaxes(kspace,1,3) kspace_fin = np.zeros_like(kspace) kspace_fin[:,phase_sort_order,:,:,:] = kspace kspace_fin = np.moveaxis(kspace_fin, [0,1,4,2,3], [0,1,2,3,4]) kspace = kspace_fin else: raise(Exception('not implemented')) if _is_interleaved(p): # 1 if interleaved slices if _is_evenslices(p): c = np.zeros(kspace.shape, dtype='complex64') c[...,0::2,:] = kspace[...,:slices//2,:] c[...,1::2,:] = kspace[...,slices//2:,:] kspace = c else: c = np.zeros(kspace.shape, dtype='complex64') c[...,0::2,:] = kspace[...,:(slices+1)//2,:] c[...,1::2,:] = kspace[...,(slices-1)//2+1:,:] kspace = c final_kspace[...,i*echo*time:(i+1)*echo*time] = kspace self.data = final_kspace # additional reordering self.data = np.moveaxis(self.data,[0,1,2,3,4],[4,1,0,2,3]) # swap axes 0 and 1 so phase, readout etc is the final order self.data = np.swapaxes(self.data,0,1) return self def make_im2Dfsecs(**kwargs): raise(Exception('not implemented')) def make_im3D(**kwargs): """Child method of 'make', provides the same as vnmrj im3Drecon""" p = self.pd rcvrs = int(p['rcvrs'].count('y')) (read, phase, phase2) = (int(p['np'])//2,int(p['nv']),int(p['nv2'])) if 'ne' in p.keys(): echo = int(p['ne']) else: echo = 1 if 'images' in p.keys(): time = int(p['images']) else: time = 1 finalshape = (rcvrs, phase, read, phase2,echo*time*array_length) final_kspace = np.zeros(finalshape,dtype='complex64') for i in range(array_length): kspace = self.data[i*blocks:(i+1)*blocks,...] if p['seqcon'] == 'nccsn': preshape = (rcvrs,phase2,phase*echo*time*read) shape = (rcvrs,phase2,phase,echo*time,read) kspace = np.reshape(kspace,preshape,order='F') kspace = np.reshape(kspace,shape,order='C') kspace = np.moveaxis(kspace, [0,2,4,1,3], [0,1,2,3,4]) # what is this?? #kspace = np.flip(kspace,axis=3) if p['seqcon'] == 'ncccn': preshape = (rcvrs,phase2,phase*echo*time*read) shape = (rcvrs,phase,phase2,echo*time,read) kspace = np.reshape(kspace,preshape,order='F') kspace = np.reshape(kspace,shape,order='C') kspace = np.moveaxis(kspace, [0,2,4,1,3], [0,1,2,3,4]) if p['seqcon'] == 'cccsn': preshape = (rcvrs,phase2,phase*echo*time*read) shape = (rcvrs,phase,phase2,echo*time,read) kspace = np.reshape(kspace,preshape,order='F') kspace = np.reshape(kspace,shape,order='C') kspace = np.moveaxis(kspace, [0,2,4,1,3], [0,1,2,3,4]) if p['seqcon'] == 'ccccn': shape = (rcvrs,phase2,phase,echo*time,read) kspace = np.reshape(kspace,shape,order='C') kspace = np.moveaxis(kspace, [0,2,4,1,3], [0,1,2,3,4]) final_kspace[...,i*echo*time:(i+1)*echo*time] = kspace self.data = final_kspace # additional reordering self.data = np.moveaxis(self.data,[0,1,2,3,4],[4,1,0,2,3]) # swap axes 0 and 1 so phase, readout etc is the final order self.data = np.swapaxes(self.data,0,1) return self def make_im3Dcs(): """ 3D compressed sensing sequences : ge3d, mge3d, se3d, etc """ # -------------------im3Dcs Make helper functions --------------------- def decode_skipint_3D(skipint): """ Takes 'skipint' parameter and returns a 0-1 matrix according to it which tells what lines are acquired in the phase1-phase2 plane """ BITS = 32 # Skipint parameter is 32 bit encoded binary, see spinsights skip_matrix = np.zeros([int(p['nv']), int(p['nv2'])]) skipint = [int(x) for x in skipint] skipint_bin_vals = [str(np.binary_repr(d, BITS)) for d in skipint] skipint_bin_vals = ''.join(skipint_bin_vals) skipint_bin_array = np.asarray([int(i) for i in skipint_bin_vals]) skip_matrix = np.reshape(skipint_bin_array, skip_matrix.shape) return skip_matrix def fill_kspace_3D(pre_kspace, skip_matrix, shape): """ Fills up reduced kspace with zeros according to skip_matrix returns zerofilled kspace in the final shape """ kspace = np.zeros(shape, dtype=complex) if self.p['seqcon'] == 'ncccn': n = int(self.p['nv']) count = 0 for i in range(skip_matrix.shape[0]): for k in range(skip_matrix.shape[1]): if skip_matrix[i,k] == 1: kspace[:,i,k,:,:] = pre_kspace[:,count,:,:] count = count+1 self.kspace = kspace return kspace #------------------------im3Dcs make start ------------------------------- kspace = self.pre_kspace p = self.p (read, phase, phase2) = (int(p['np'])//2, \ int(p['nv']), \ int(p['nv2'])) shiftaxis = (self.config['pe_dim'],\ self.config['ro_dim'],\ self.config['pe2_dim']) if 'ne' in p.keys(): echo = int(p['ne']) else: echo = 1 time = 1 if p['seqcon'] == 'nccsn': pass if p['seqcon'] == 'ncccn': skip_matrix = decode_skipint_3D(p['skipint']) pre_phase = int(self.fid_header['ntraces']) shape = (self.rcvrs, phase, phase2, echo*time, read) pre_shape = (self.rcvrs, pre_phase, echo*time, read) pre_kspace = np.reshape(kspace, pre_shape, order='c') kspace = fill_kspace_3D(pre_kspace, skip_matrix, shape) kspace = np.moveaxis(kspace, [0,1,4,2,3],[0,1,2,3,4]) self.kspace = kspace return kspace def make_im3Dute(): raise(Exception('not implemented')) # ========================== INIT ===================================== # if data is not from fid, just fft it if self.vdtype == 'imagespace': raise(Exception('not implemented yet')) #TODO something like this: #self.data = vj.core.recon._fft(self.data,dims) #self.vdype = 'kspace' #=========================== Xrecon =================================== vprint(' making seqfil : {}'.format(self.pd['seqfil'])) if method == 'xrecon': self = vj.xrecon.make_temp_dir(self) self = vj.xrecon.mod_procpar(self,output='kspace') self = vj.xrecon.call(self) self = vj.xrecon.loadfdf(self) self = vj.xrecon.clean(self) return self # ========================= Vnmrjpy recon ============================ elif method == 'vnmrjpy': # check if data is really from fid if self.vdtype is not 'fid': raise(Exception('varray data is not fid data.')) self.data = np.vectorize(complex)(self.data[:,0::2],\ self.data[:,1::2]) # check for arrayed parameters, save the length for later array_length = reduce(lambda x,y: x*y, \ [i[1] for i in self.arrayed_params]) blocks = self.data.shape[0] // array_length vprint('Making k-space for '+ str(self.apptype)+' '\ +str(self.pd['seqfil'])+' seqcon: '+str(self.pd['seqcon'])) rcvrs = self.pd['rcvrs'].count('y') # add epiref self.epiref_type = epiref_type # ----------------Handle sequence exceptions first--------------------- if str(self.pd['seqfil']) == 'ge3d_elliptical': self = make_im3Dcs() #--------------------------Handle by apptype--------------------------- if self.pd['apptype'] == 'im2D': self = make_im2D() self.is_kspace_complete = True elif self.pd['apptype'] == 'im2Dcs': self = make_im2Dcs() elif self.pd['apptype'] == 'im2Depi': self = make_im2Depi() self.is_kspace_complete = True elif self.pd['apptype'] == 'im2Depics': self = make_im2Depics() elif self.pd['apptype'] == 'im2Dfse': self = make_im2Dfse() self.is_kspace_complete = True elif self.pd['apptype'] == 'im2Dfsecs': self = make_im2Dfsecs() elif self.pd['apptype'] == 'im3D': self = make_im3D() self.is_kspace_complete = True elif self.pd['apptype'] == 'im3Dcs': self = make_im3Dcs() elif self.pd['apptype'] == 'im3Dute': self = make_im3Dute() self.is_kspace_complete = True else: raise(Exception('Could not find apptype.')) # ===================== Global modifications on Kspace ================ #TODO if slices are in reversed order, flip them # in revamp make new axes, mainly for nifti io, and viewers # old : [rcvrs, phase, read, slice, time] # new : [read, phase, slice, time, rcvrs] # TODO moved these to the individual recons #self.data = np.moveaxis(self.data,[0,1,2,3,4],[4,1,0,2,3]) # swap axes 0 and 1 so phase, readout etc is the final order #self.data = np.swapaxes(self.data,0,1) self.vdtype='kspace' self.to_local() if vj.config['default_space'] == None: pass elif vj.config['default_space'] == 'anatomical': self.to_anatomical() return self
def read_fid(fid,procpar=None,load_data=True, xrecon=False,xrecon_space='imagespace'): """Handles raw data from Varian spectrometer Args: fid -- path to .fid directory procpar -- path to procpar file, specify procpar manually load_data -- set false to prevent loading binary data into memory xrecon -- Set True for direct reconstruction by Xrecon xrecon_space -- either 'kspace' or 'imagespace' for xrecon output .fid File structure as per Vnmrj manual: =================================== struct datafilehead Used at the beginning of each data file (fid's, spectra, 2D) int nblocks; /* number of blocks in file int ntraces; /* number of traces per block int np; /* number of elements per trace int ebytes; /* number of bytes per element int tbytes; /* number of bytes per trace int bbytes; /* number of bytes per block short vers_id; /* software version and file_id status bits short status; /* status of whole file int nbheaders; /* number of block headers struct datablockhead Each file block contains the following header short scale; /* scaling factor short status; /* status of data in block short index; /* block index short mode; /* mode of data in block int ctcount; /* ct value for FID float lpval; /* F2 left phase in phasefile float rpval; /* F2 right phase in phasefile float lvl; /* F2 level drift correction float tlt; /* F2 tilt drift correction """ def _decode_header(bheader): """Return dictionary of fid header from binary header data""" hkey_list = ['nblocks','ntraces','np','ebytes','tbytes','bbytes',\ 'vers_id','status','nbheaders'] hval_list = [] h_dict = {} if len(bheader) != 32: raise(Exception('Incorrect fid header data: not 32 bytes')) else: hval_list.append(int.from_bytes(bheader[0:4],byteorder='big')) hval_list.append(int.from_bytes(bheader[4:8],byteorder='big')) hval_list.append(int.from_bytes(bheader[8:12],byteorder='big')) hval_list.append(int.from_bytes(bheader[12:16],byteorder='big')) hval_list.append(int.from_bytes(bheader[16:20],byteorder='big')) hval_list.append(int.from_bytes(bheader[20:24],byteorder='big')) hval_list.append(int.from_bytes(bheader[24:26],byteorder='big')) hval_list.append(int.from_bytes(bheader[26:28],byteorder='big')) hval_list.append(int.from_bytes(bheader[28:],byteorder='big')) for num, i in enumerate(hkey_list): h_dict[i] = hval_list[num] return h_dict def _decode_blockhead(blockheader): """Return block header dictionary""" bh_dict = {} bhk_list = ['scale','status','index','mode','ctcount','lpval'\ 'rpval','lvl','tlt'] bhv_list = [] bhv_list.append(int.from_bytes(blockheader[0:2],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[2:4],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[4:6],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[6:8],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[8:12],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[12:16],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[16:20],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[20:24],byteorder='big')) bhv_list.append(int.from_bytes(blockheader[24:28],byteorder='big')) for num, i in enumerate(bhk_list): bh_dict[i] = bhv_list[num] return bh_dict def _chunks(l, n): """Generate n long chunks from list l""" for i in range(0, len(l), n): yield l[i:i + n] def _xrecon_read(varr,xrspace): varr = vj.xrecon.make_temp_dir(varr) varr = vj.xrecon.mod_procpar(varr,output=xrspace) varr = vj.xrecon.call(varr) varr = vj.xrecon.loadfdf(varr,data=xrspace) varr = vj.xrecon.clean(varr) return varr # ============================ INIT ======================================= # TODO is procpar != None making sense? if procpar==None: if os.path.isdir(fid): fid_path = fid procpar = str(fid)+'/procpar' fid = str(fid)+'/fid' else: fid_path = fid.rsplit('/',1)[0] procpar = fid.rsplit('/')[0]+'/procpar' else: print('Warning: manual setting of procpar is not fully supported') # ============================ XRECON ==================================== # deal with Xrecon is set True if xrecon == True: pd = read_procpar(procpar) arr = _get_arrayed_par_length(pd) # TODO ??? sdim = ['phase', 'read', 'slice', 'time', 'rcvr'] if xrecon_space == 'kspace': vdtype = 'kspace' elif xrecon_space == 'imagespace': vdtype = 'imagespace' varr = vj.varray(data=None,space=None,\ pd=pd,fid_header=None,\ source='fid',dtype=vj.DTYPE, seqcon=pd['seqcon'],\ apptype=pd['apptype'],arrayed_params=arr,\ vdtype=None,sdims = sdim, fid_path=fid_path) varr = _xrecon_read(varr,xrecon_space) varr.vdtype = vdtype varr.set_nifti_header() varr.to_local() return varr # ================================ VNMRJPY ================================ # header data should be 32 bytes with open(fid,'rb') as openFid: binary_data = openFid.read() # binary header data bheader = bytearray(binary_data[:32]) # binary fid data bdata = binary_data[32:] header_dict = _decode_header(bheader) pd = read_procpar(procpar) chunk_size = int(header_dict['bbytes']) block_list = list(_chunks(bdata,chunk_size)) if header_dict['bbytes'] == len(block_list[0]): #print('blocksize check OK') pass vprint('reading fid {}'.format(fid)) vprint('\nfid header :\n') vprint(header_dict) #------------main iteration through bytearray ----------------- if load_data == False: fid_data = None #don't load fid data, useful if xrecon is called next elif load_data == True: dim = (int(header_dict['nblocks']),\ int((header_dict['ntraces'])*int(header_dict['np']))) fid_data = np.empty(dim) if header_dict['ebytes'] == 4: dt = '>f' if header_dict['ebytes'] == 2: dt = '>i2' for k,block in enumerate(block_list): # for each block block_header = block[:28] # separate block header block_data = block[28:] fid_data[k,:] = np.frombuffer(bytearray(block_data),dt) # fid data ready, now create varray class arr = _get_arrayed_par_length(pd) sdim = ['phase', 'read', 'slice', 'time', 'rcvr'] varr = vj.varray(data=fid_data,space=None,pd=pd,fid_header=header_dict,\ source='fid',dtype=vj.DTYPE, seqcon=pd['seqcon'],\ apptype=pd['apptype'],arrayed_params=arr,vdtype='fid',\ sdims = sdim, fid_path=fid_path) return varr
def _parse_header(header): keys_to_parse = sorted(['rank','roi','location','spatial_rank',\ 'matrix','orientation',\ 'studyid','gap','pe_size','ro_size',\ 'pe2_size', 'abscissa',\ 'storage']) to_delete = ('char','float','int') header = header.decode('ascii').split('\n') header_dict = {} for line in header: # some formatting of header vprint(line) for item in to_delete: if line.startswith(item): line = line.split(item,1) break try: line = line[1].lstrip() line = line.lstrip('*').rstrip(';') if '[]' in line: line = line.replace('[]','') if '{' in line: line = line.replace('{','(') if '}' in line: line = line.replace('}',')') if ' ' in line: line = line.replace(' ','') line = line.split('=') header_dict[line[0]] = line[1] except: continue for item in keys_to_parse: if item in header_dict.keys(): if item == 'abscissa': tempval = header_dict[item][1:-1];''.join(tempval) tempval = tempval.replace('"','') header_dict[item] = tuple([k \ for k in tempval.split(',')]) if item == 'matrix': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([int(k) \ for k in tempval.split(',')]) if item == 'roi': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([float(k)\ for k in tempval.split(',')]) if item == 'ro_size' or item == 'pe_size' \ or item == 'pe2_size': header_dict[item] = int(header_dict[item]) if item == 'storage': tempval = header_dict[item];''.join(tempval) tempval = tempval.replace('"','') header_dict[item] = str(tempval) if item == 'orientation': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([float(k)\ for k in tempval.split(',')]) if item == 'location': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([float(k)\ for k in tempval.split(',')]) if item == 'gap': header_dict[item] = float(header_dict[item]) if item == 'slices': header_dict[item] = int(header_dict[item]) if item == 'TR': header_dict[item] = float(header_dict[item])/1000 return header_dict
def read_fdf(path): """Return vnmrjpy.varray from varian .fdf files Input can be the whole .img directory (this is preferred) or stand-alone .fdf files. Procpar file should be present in directory. Args: path Return: varray """ #---------------- auxiliary functions for read method ----------------- def _preproc_fdf(fdf): with open(fdf,'rb') as openFdf: fdata = bytearray(openFdf.read()) nul = fdata.find(b'\x00') header = fdata[:nul] data = fdata[nul+1:] return (header,data) # ----------parse fdf header and return into a dictionary -------------- def _parse_header(header): keys_to_parse = sorted(['rank','roi','location','spatial_rank',\ 'matrix','orientation',\ 'studyid','gap','pe_size','ro_size',\ 'pe2_size', 'abscissa',\ 'storage']) to_delete = ('char','float','int') header = header.decode('ascii').split('\n') header_dict = {} for line in header: # some formatting of header vprint(line) for item in to_delete: if line.startswith(item): line = line.split(item,1) break try: line = line[1].lstrip() line = line.lstrip('*').rstrip(';') if '[]' in line: line = line.replace('[]','') if '{' in line: line = line.replace('{','(') if '}' in line: line = line.replace('}',')') if ' ' in line: line = line.replace(' ','') line = line.split('=') header_dict[line[0]] = line[1] except: continue for item in keys_to_parse: if item in header_dict.keys(): if item == 'abscissa': tempval = header_dict[item][1:-1];''.join(tempval) tempval = tempval.replace('"','') header_dict[item] = tuple([k \ for k in tempval.split(',')]) if item == 'matrix': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([int(k) \ for k in tempval.split(',')]) if item == 'roi': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([float(k)\ for k in tempval.split(',')]) if item == 'ro_size' or item == 'pe_size' \ or item == 'pe2_size': header_dict[item] = int(header_dict[item]) if item == 'storage': tempval = header_dict[item];''.join(tempval) tempval = tempval.replace('"','') header_dict[item] = str(tempval) if item == 'orientation': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([float(k)\ for k in tempval.split(',')]) if item == 'location': tempval = header_dict[item][1:-1];''.join(tempval) header_dict[item] = tuple([float(k)\ for k in tempval.split(',')]) if item == 'gap': header_dict[item] = float(header_dict[item]) if item == 'slices': header_dict[item] = int(header_dict[item]) if item == 'TR': header_dict[item] = float(header_dict[item])/1000 return header_dict #----------process bynary data based on header-------------------- def _prepare_data(binary_data, header_dict): matrix = header_dict['matrix'] if header_dict['storage'] == 'float' and \ header_dict['bits'] == '32': dt = np.dtype('float32'); dt = dt.newbyteorder('<') else: raise(Exception('bits may be incorrectly specified in header data')) img_data = np.frombuffer(binary_data, dtype=dt) img_data = np.reshape(img_data,matrix) return img_data #--------------------------------------------------------------------- # main read method #---------------------------------------------------------------------- if os.path.isdir(path): vprint('\nMaking varray from fdf : {} \n'.format(path)) procpar = str(path)+'/procpar' fdf_list = sorted(glob.glob(str(path)+'/*.fdf')) (header, data) = _preproc_fdf(fdf_list[0]) pd = read_procpar(procpar) else: if not path.endswith('.fdf'): warnings.warn('Input does not end with .fdf, path might be incorrect') vprint('\nMaking varray from fdf : {} \n'.format(path)) procpar = path.rsplit('/')[0]+'/procpar' pd = read_procpar(procpar) fdf_list =[path] (header, data) = _preproc_fdf(path) header_dict = _parse_header(header) # ------------------------process if 3d -------------------- if header_dict['spatial_rank'] == '"3dfov"': full_data = [] time_concat = [] time = len([1 for i in fdf_list if 'slab001' in i]) for i in fdf_list: # only 1 item, but there migh be more in future (header, data) = _preproc_fdf(i) img_data = _prepare_data(data, header_dict) # expand to time dim img_data = np.expand_dims(img_data,axis=3) full_data.append(img_data) # full data in one list data_array = np.concatenate(full_data,axis=3) #----------------------process if 2d------------------------- elif header_dict['spatial_rank'] == '"2dfov"': full_data = [] time_concat = [] time = len([1 for i in fdf_list if 'slice001' in i]) for i in fdf_list: (header, data) = _preproc_fdf(i) img_data = _prepare_data(data,header_dict) # expand 2d to 4d img_data = np.expand_dims(np.expand_dims(img_data,2),3) full_data.append(img_data) # full data in one list # make sublists slice_list =[full_data[i:i+time] \ for i in range(0,len(full_data),time)] for aslice in slice_list: time_concat.append(np.concatenate(tuple(aslice),axis=3)) #slice+time concatenated slice_time_concat = np.concatenate(time_concat, axis=2) data_array = slice_time_concat # --------------------process if 1d------------------------ elif header_dict['spatial_rank'] == '"1dfov"': raise(Exception('Not implemented')) # making vnmrjpy.varray arrayed_params = _get_arrayed_par_length(pd) varr = vj.varray(data=data_array, fdf_header=header_dict, pd=pd,\ dtype=vj.DTYPE, source='fdf', seqcon=pd['seqcon'],\ apptype=pd['apptype'],arrayed_params=arrayed_params,\ vdtype='image',space=None) varr.space = None varr.set_nifti_header() varr.to_local() return varr
def read_procpar(procpar): """Return dictionary of varian parameters from procpar file""" with open(procpar,'r') as openpp: name = [] value = [] subtype = [] basictype = [] maxvalue = [] minvalue = [] # possibilities (as of manual): 0 :'line1', 1: 'line2', 2: 'last' line_id = 0 current_name = 0 for line_num, line in enumerate(openpp.readlines()): if line_id == 0: # parameter name line fields = line.rsplit('\n')[0].rsplit(' ') current_name = fields[0] name.append(fields[0]) subtype.append(int(fields[1])) basictype.append(int(fields[2])) # can be 0 (undefined), 1 (real), 2 (string) current_basictype = int(fields[2]) val_list = [] # if parameter value is a multitude of strings line_id = 1 elif line_id == 1: # parameter value line fields = line.rsplit('\n')[0].rsplit(' ') # values are real, and all are on this line if current_basictype == 1: val = [fields[i] for i in range(1,len(fields)-1)] if len(val) == 1: # don't make list if there is only one value val = val[0] value.append(val) line_id = 2 # values are strings elif current_basictype == 2 and int(fields[0]) == 1: value.append(str(fields[1].rsplit('"')[1])) line_id = 2 # multiple string values elif current_basictype == 2 and int(fields[0]) > 1: val_list.append(fields[1]) remaining_values = int(fields[0])-1 line_id = 3 elif line_id == 2: line_id = 0 elif line_id == 3: # if values are on multiple lines if remaining_values > 1: # count backwards from remaining values val_list.append(fields[0]) remaining_values = remaining_values - 1 elif remaining_values == 1: val_list.append(fields[0]) remaining_values = remaining_values - 1 value.append(val_list) line_id = 2 else: raise(Exception('Error reading procpar, problem with line_id')) else: raise(Exception('Error reading procpar, problem with line_id')) vprint('procpar file {} read succesfully'.format(procpar)) return dict(zip(name, value))