예제 #1
0
 def __init__(self, variable_dict=None):
     
     super(BrainData, self).__init__(variable_dict=variable_dict)
     self.subject_data_dict = {}
     self.nifti = NiftiTools()
     self.vector = VectorTools()
예제 #2
0
class BrainData(DataManager):
    '''
    BrainData
    ------------
    Recoded BrainData class in style of Jonathan Taylor's masking scheme. It
    preforms faster than my old one so I decided to go with this. However,
    his originally forced a matrix transposition (reversal) which may not
    be ideal. This has the option of either using his matrix format or
    preserving (as best as possible) the dimensionality when loading niftis
    using nipy or nibabel.
    
    '''
    
    def __init__(self, variable_dict=None):
        
        super(BrainData, self).__init__(variable_dict=variable_dict)
        self.subject_data_dict = {}
        self.nifti = NiftiTools()
        self.vector = VectorTools()
    
    
    
    def create_niftis(self, subject_dirs=None, functional_name=None, anatomical_name=None,
                      dxyz=None, talairach_template_path=None, nifti_name=None,
                      within_subject_warp=True, to_template_warp=False):
        
            
        if not nifti_name.endswith('.nii'):
            nifti_name = nifti_name+'.nii'
        
        self.nifti.create_talairach_niftis(subject_dirs, functional_name,
                                           anatomical_name, dxyz,
                                           talairach_template_path, nifti_name,
                                           within_subject_warp, to_template_warp)
        
        
        
        
    def load_niftis_vectors(self, directory, verbose=True):
        '''
        Loads niftis and response vectors from a directory. This function is
        fairly specific. The nifti files should be named in this manner:
        
        prefix_***.nii
        
        Such that prefix denotes a subject and an underscore splits this subject
        from the rest of the nifti filename.
        
        Likewise, the response vector file should be coded:
        
        prefix_***.1D
        
        Such that the prefix matches a prefix for a nifti file!
        NO DUPLICATE PREFIXES - WILL CHOOSE INDISCRIMINATELY
        '''
        
        nifti_paths = sorted(glob.glob(os.path.join(directory, '*.nii')))
        vector_paths = sorted(glob.glob(os.path.join(directory, '*.nii')))
        
        npre = [os.path.split(n)[1].split('_')[0] for n in nifti_paths]
        vpre = [os.path.split(v)[1].split('_')[0] for v in vector_paths]
        
        pairs = []
        for nifti, np in zip(nifti_paths, npre):
            for vector, vp in zip(vector_paths, vpre):
                if np == vp:
                    pairs.append([nifti, self.vector.read(vector)])
                    break
        
        return pairs
        

        
        
        
    def load_niftis_fromdirs(self, subject_dirs, nifti_name, response_vector,
                             verbose=True):
        '''
        Iterates through subject directories, parses the response vector,
        and appends the path to the nifti file for loading later.
        
        Basic support for multiple niftis per subject (just added as different
        key in the subject_data_dict).
        
        '''
        for subject in subject_dirs:
                
            nifti = os.path.join(subject, nifti_name)
            vec = os.path.join(subject, response_vector)

            if not os.path.exists(nifti):
                if verbose:
                    print 'nifti not found: ', nifti
            elif not os.path.exists(vec):
                if verbose:
                    print 'respnse vector not found: ', vec
            else:
                
                respvec = self.vector.read(vec, usefloat=True)
                subject_key = os.path.split(subject)[1]
                
                if verbose:
                    pprint(nifti)
                    print 'appending raw data for subject: ', subject_key
                
                if not subject_key in self.subject_data_dict:
                    self.subject_data_dict[subject_key] = [nifti, respvec]
                    
                else:
                    tag = 2
                    while subject_key+'_'+str(tag) in self.subject_data_dict:
                        tag += 2
                    self.subject_data_dict[subject_key+'_'+str(tag)] = [nifti, respvec]
                
        
        
        
    def parse_trialsvec(self, trialsvec):
        '''
        Simple function to find the indices where Y is not 0. Returns the indices
        vector and the stripped Y vector. Used by masked_data().
        '''
        
        inds = [i for i,x in enumerate(trialsvec) if x != 0.]
        y = [x for x in trialsvec if x != 0]
        return inds, y
    
    
        
    def unmask_Xcoefs(self, Xcoefs, time_points, mask=None, reverse_transpose=True,
                      verbose=True, slice_off_back=0, slice_off_front=0):
        '''
        Reshape the coefficients from a statistical method back to the shape of
        the original brain matrix, so it can be output to nifti format.
        '''
        if mask is None:
            mask = self.original_mask
            
        unmasked = [np.zeros(mask.shape) for i in range(time_points)]
        
        print 'xcoefs sum', np.sum(Xcoefs)
        
        if slice_off_back:
            print np.sum(Xcoefs[:-slice_off_back]), np.sum(Xcoefs[-slice_off_back:])
            Xcoefs = Xcoefs[:-slice_off_back]
        if slice_off_front:
            Xcoefs = Xcoefs[slice_off_front:]
        
        print 'xcoefs sum', np.sum(Xcoefs)
        Xcoefs.shape = (time_points, -1)
        print 'Xcoefs shape', Xcoefs.shape
        
        for i in range(time_points):
            print 'raw coef time sum', np.sum(Xcoefs[i])
            print 'mask, xind shapes', mask.shape, Xcoefs[i].shape
            
            unmasked[i][np.asarray(mask).astype(np.bool)] = np.squeeze(np.array(Xcoefs[i]))
            #unmasked[i][np.asarray(mask).astype(np.bool)] = np.squeeze(np.ones(np.sum(np.asarray(mask).astype(np.bool))))
            
            print 'time ind coef sum', np.sum(unmasked[i])
            if reverse_transpose:
                unmasked[i] = np.transpose(unmasked[i], [2, 1, 0])
        
        unmasked = np.transpose(unmasked, [1, 2, 3, 0])
        
        if verbose:
            print 'Shape of unmasked coefs: ', np.shape(unmasked)
        
        return np.array(unmasked)
        
        
        
    def save_unmasked_coefs(self, unmasked, nifti_filename, affine=None,
                            talairach_template_path='./TT_N27+tlrc.'):
        '''
        Simple function to save the unmasked coefficients to a specified nifti.
        Affine is usually self.mask_affine, but can be specified.
        '''
        
        if affine is None:
            affine = self.mask_affine
            
        if self.verbose:
            print 'erasing old files with prefix:', nifti_filename#[:-4]
            
        glob_remove(nifti_filename)#[:-4])
            
        self.nifti.save_nifti(unmasked, affine, nifti_filename)
        
        time.sleep(0.25)
        
        self.nifti.convert_to_afni(nifti_filename, nifti_filename)#[:-4])
        
        time.sleep(0.25)
        
        subprocess.call(['3drefit','-view','tlrc',nifti_filename+'+orig.'])
        
        
        
    def make_masks(self, mask_path, ntrs, reverse_transpose=True, verbose=True):
        
        '''
        A function that makes the various mask objects.
        '''
        if verbose:
            if reverse_transpose:
                print 'using time-first reverse transposition of nifti matrix'
            else:
                print 'preserving dimensionality of nifti matrix (nt last)'
        
        mask = load_image(mask_path)
        tmp_mask, self.mask_affine, tmp_shape = self.nifti.load_nifti(mask_path)
        mask = np.asarray(mask)
        self.raw_affine = self.mask_affine
            
        if verbose:
            print 'mask shape:', mask.shape
        self.mask_shape = mask.shape
            
        if reverse_transpose:
            mask = np.transpose(mask.astype(np.bool), [2, 1, 0])
        else:
            mask = mask.copy().astype(np.bool)
            
        self.original_mask = mask.copy()
        self.flat_mask = mask.copy()
        self.flat_mask.shape = np.product(mask.shape)
        
        if verbose:
            print 'flat mask shape:', self.flat_mask.shape
            
        nmask = np.not_equal(mask, 0).sum()
        
        if verbose:
            print 'mask shape', mask.shape
        
        self.trial_mask = np.zeros((ntrs, mask.shape[0], mask.shape[1], mask.shape[2]))
        
        if verbose:
            print 'trial mask shape', self.trial_mask.shape
        
        for t in range(ntrs):
            self.trial_mask[t,:,:,:] = mask
            
        self.trial_mask = self.trial_mask.astype(np.bool)   
        
        
    def prepare_greymatter_mask(self, mask_path, greymatter_prefix='greymatter_resamp',
                                afni_greymatter_dset='/Users/span/abin/TT_caez_gw_18+tlrc.',
                                afni_index=0, reverse_transpose=True):
        
        # resample the gray matter mask to the user's mask:
        
        gm_resample_mask = os.path.join(os.path.split(mask_path)[0],greymatter_prefix)
        old_resamps = glob.glob(gm_resample_mask+'*')
        for oresamp in old_resamps:
            try:
                os.remove(oresamp)
            except:
                pass
        
        cmd = ['3dresample','-master', mask_path, '-prefix', gm_resample_mask,
               '-inset', afni_greymatter_dset+'['+str(afni_index)+']']
        
        subprocess.call(cmd)
        
        niicmd = ['3dAFNItoNIFTI', '-prefix', greymatter_prefix, greymatter_prefix+'+tlrc.']
        
        subprocess.call(niicmd)
        
        # for now need to have made the trial mask, etc...
        
        self.grey_matter = np.zeros(self.trial_mask.shape)
        
        gm_mask = load_image(gm_resample_mask+'.nii')
        gm_mask = np.asarray(gm_mask)
        
        self.grey_matter_flat = gm_mask.copy()
        self.grey_matter_flat.shape = np.product(self.grey_matter_flat.shape)
            
        if reverse_transpose:
            gm_mask = np.transpose(gm_mask, [2, 1, 0])
            for tr in range(len(self.trial_mask)):
                self.grey_matter[tr,:,:,:] = gm_mask[:,:,:]
        
        
        
        
    def masked_data(self, nifti, trialsvec, selected_trs=[], mask_path=None, lag=2,
                    reverse_transpose=True, verbose=True):
        
        '''
        This function masks, transposes, and subselects the trials from the nifti
        data.
        --------
        nifti           :   a filepath to the nifti.
        trialsvec       :   numpy array denoting the response variable at the TR of the
                            trial onset.
        selected_trs    :   a list of the trs in the trial to be subselected
        mask_path       :   path to the mask (optional but recommended)
        lag             :   how many TRs to push out the trial (2 recommended)
        '''
        
        if verbose:
            if reverse_transpose:
                print 'using time-first reverse transposition of nifti matrix'
            else:
                print 'preserving dimensionality of nifti matrix (nt last)'
        
        image = load_image(nifti)
            
        if verbose:
            print 'nifti shape:', image.shape
            
        nmask = np.not_equal(self.original_mask, 0).sum()
        
        ntrs = len(selected_trs)
        
        p = np.prod(image.shape[:-1])
        
        trial_inds, response = self.parse_trialsvec(trialsvec)
        
        ntrials = len(trial_inds)
        
        if reverse_transpose:
            X = np.zeros((ntrials, ntrs, nmask))
        else:
            X = np.zeros((ntrials, nmask, ntrs))
        Y = np.zeros(ntrials)
        
        reselect_trs = [x-1 for x in selected_trs]
        
        if reverse_transpose:
            im = np.transpose(np.asarray(image), [3, 2, 1, 0])
        
            for i in range(ntrials):
                if len(im) > trial_inds[i]+reselect_trs[-1]+lag:
                    # OLD VERSION: could only do a continuous range
                    #row = im[trial_inds[i]+reselect_trs[0]+lag:trial_inds[i]+reselect_trs[-1]+1+lag].reshape((ntrs,p))
                    
                    # NEW VERSION: uses list comprehension for any index range
                    row_inds = [trial_inds[i]+lag+x for x in reselect_trs]
                    row = im[row_inds].reshape((ntrs,p))
                    
                    X[i] = row[:,self.flat_mask]
                    Y[i] = response[i]
            
        else:
            im = np.asarray(image)
            
            for i in range(ntrials):
                if im.shape[3] > trial_inds[i]+reselect_trs[-1]+lag:
                    # OLD VERSION: could only do a continuous range
                    #row = im[:,:,:,trial_inds[i]+reselect_trs[0]+lag:trial_inds[i]+reselect_trs[-1]+1+lag].reshape((p,ntrs))

                    # NEW VERSION: uses list comprehension for any index range
                    row_inds = [trial_inds[i]+lag+x for x in reselect_trs]
                    row = im[:,:,:,row_inds].reshape((p,ntrs))
                    
                    X[i] = row[self.flat_mask,:]
                    Y[i] = response[i]
            
        return X, Y
    
    
    
    def create_trial_mask(self, mask_path, ntrs, reverse_transpose=True):
        
        mask = load_image(mask_path)
        tmp_mask, self.mask_affine, tmp_shape = self.nifti.load_nifti(mask_path)
        mask = np.asarray(mask)
        
        if reverse_transpose:
            mask = np.transpose(mask.astype(np.bool), [2, 1, 0])
        else:
            mask = mask.copy().astype(np.bool)
        
        self.original_mask = mask.copy()
        
        print mask.shape
        
        self.trial_mask = np.zeros((ntrs, mask.shape[0], mask.shape[1], mask.shape[2]))
        
        for t in range(ntrs):
            self.trial_mask[t,:,:,:] = mask
            
        self.trial_mask = self.trial_mask.astype(np.bool)
        print self.trial_mask.shape
    
            

    def create_design(self, subject_dirs, nifti_name, respvec_name, selected_trs,
                      mask_path=None, lag=2, reverse_transpose=True):
        
        self.selected_trs = selected_trs
        
        self.load_niftis_fromdirs(subject_dirs, nifti_name, respvec_name)
        
        self.subject_design = {}
        for subject, [image, respvec] in self.subject_data_dict.items():
            sX, sY = self.masked_data(image, respvec, selected_trs=selected_trs,
                                      mask_path=mask_path, lag=lag, reverse_transpose=reverse_transpose)
            sX.shape = (sX.shape[0], np.prod(sX.shape[1:]))
            print 'subject X shape:', sX.shape
            self.subject_design[subject] = [np.array(sX), np.array(sY)]
            
        del(self.subject_data_dict)
            
            
    def create_design_logan_npy(self, subject_npys):
        
        self.subject_design = {}
        for npy in subject_npys:
            subject = npy.split('.')[0]
            cur_data = np.load(npy)
            sX, sY = [], []
            for ind in range(len(cur_data)):
                sY.append(cur_data[ind]['Y'])
                i_X = cur_data[ind]['X'].copy()
                i_X.shape = (i_X.shape[0]*i_X.shape[1])
                #print i_X.shape
                sX.append(i_X)
            self.subject_design[subject] = [np.array(sX), np.array(sY)]
        
        del(self.subject_data_dict)