Exemplo n.º 1
0
def characterize_components_se(origTS_pc, data_mean, tes, t2s, S0, mmix, ICA_maps, voxelwiseQA,
                            Ncpus, ICA_maps_thr=0.0, discard_mask=None,writeOuts=False,outDir=None, outPrefix=None, mask=None, aff=None, head=None, Z_MAX=8, F_MAX=500, doFM=False, doMedian=False):
    """
    This function computes kappa, rho and variance for each ICA component.

    Parameters:
    -----------
    origTS_pc:    original ME Timeseries in signal percent units for intra-cranial voxels (Nv,Ne,Nt)
    data_mean:    voxel-wise mean across time of the ME timeseries. (Nv,Ne)
    tes:          echo times (Ne,)
    t2s:          static T2* voxel-wise map (Nv,)
    S0:           static S0  voxel-wise map (Nv,)
    mmix:         ICA mixing matrix (Nc,Nt)
    ICA_maps:     ICA spatial maps  (Nv,Nc)
    voxelwiseQA:  voxel-wise QA map obtained from attempting a static TE-dependence fit to the means. It
                  can be used as part of the wiegths used duing the averaging. (Nv,)
    Ncpus:        number of available CPUs for multi-processing.
    ICA_maps_thr: threshold for selection of voxels entering the averaging for kappa and rho computation.
    discard_mask: voxels to be discarded because the static field generated erroneous S0 or T2* values.
    writeOuts:    flag to instruct the function to save additional files.
    outDir:       path where the program should write NIFTI and other datasets.
    outPrefix:    prefix for datasets written to disk.
    mask:         binary map indicating intra-cranial voxels (Nv,)
    aff:          affine needed by nibabel to write NIFTI datasets.
    head:         header needed by nibabel to write NIFTI datasets.
    Z_MAX:        maximum allowed Z-score in ICA maps.
    F_MAX:        maximum allowed F-stat  in R2 and S0 fits of the TE-dependence model.

    Results:
    --------
    features:     a 7xNc numpy array with features for each component. Column 0 is component ID,
                  column 1 is kappa, column 2 is rho, column 3 is explained variance, column 4 is
                  max Fstat in the R2 fit, column 5 is max Fstat in the S0 fit, column 6 is the
                  kappa/rho ratio. Components are sorted by variance (e.g., the way they came out of the ICA)
    """

    Nv,Ne,Nt = origTS_pc.shape
    Nc,_     = mmix.shape
    # If no discard_mask is provided, create one in which no voxels will be discarded during the
    # averaging.
    if discard_mask is None:
       discard_mask = np.zeros((Nv*Ne,), dtype=bool)
    else:
        discard_mask = np.tile(discard_mask,Ne)
    # Get ICA-component masks based on threshold
    ICA_maps_mask = np.zeros(ICA_maps.shape, dtype=bool) #(Nv*Ne,Nc)
    ICA_maps_mask = np.logical_and((np.abs(ICA_maps)>ICA_maps_thr), discard_mask[:,np.newaxis] )
    niiwrite_nv(np.reshape(ICA_maps_mask,(Nv,Ne,Nc),order='F'), mask,outDir+outPrefix+'.ICA.Zmaps.mask.nii',aff ,head)

    # Compute overall variance in the ICA data
    totalvar     = (ICA_maps**2).sum()               # Single Value

    # Compute beta maps (fits to each echo)
    # This is equivalent to running 3dDeconvolve with the mmix being the stim_files.
    # I tried this and gives exactly the same result
    # The 100 factor is there so that the b are kind of signal percent change
    beta       = 100*np.linalg.lstsq(mmix.T, (np.reshape(origTS_pc,(Nv*Ne,Nt))).T)[0].T
    beta       = np.reshape(beta,(Nv,Ne,Nc))   #(Nv,Ne,Nc)## <----------------------------------------  MAYBE PUT HERE AN ABS

    #beta       = np.reshape(ICA_maps,(Nv,Ne,Nc),order='F') #(Nv*Ne,Nc)
    oc_ICA_maps= make_optcom(beta,t2s,tes)
    oc_ICA_maps_mask = np.sum(np.reshape(ICA_maps_mask,(Nv,Ne,Nc),order='F'),axis=1)>(ceil(Ne/2.)) #(Nv,Nc)
    niiwrite_nv(oc_ICA_maps_mask, mask,outDir+outPrefix+'.ICA.Zmaps.maskOC.nii',aff ,head) #(Nv,Nc)
    # Initialize results holder
    F_S0_maps  = np.zeros((Nv,Nc))
    F_R2_maps  = np.zeros((Nv,Nc))
    F_S0_masks = np.zeros((Nv,Nc), dtype=bool)
    F_R2_masks = np.zeros((Nv,Nc), dtype=bool)
    c_S0_maps  = np.zeros((Nv,Nc))
    c_R2_maps  = np.zeros((Nv,Nc))
    p_S0_maps  = np.zeros((Nv,Nc))
    p_R2_maps  = np.zeros((Nv,Nc))
    Kappa_maps = np.zeros((Nv,Nc))
    Kappa_masks= np.zeros((Nv,Nc), dtype=bool)
    Rho_maps   = np.zeros((Nv,Nc))
    Rho_masks  = np.zeros((Nv,Nc), dtype=bool)
    Weight_maps= np.zeros((Nv,Nc))
    varexp     = np.zeros(Nc)
    kappas     = np.zeros(Nc)
    rhos       = np.zeros(Nc)
    if doFM:
        FM_Slope_map = np.zeros((Nv,Nc))
        FM_Inter_map = np.zeros((Nv,Nc))
        FM_p_map     = np.zeros((Nv,Nc))
        FM_r_map     = np.zeros((Nv,Nc))
        FM_Slope_err_map = np.zeros((Nv,Nc))
        FM_Slope_T_map   = np.zeros((Nv,Nc))
        FM_Slope_p_map   = np.zeros((Nv,Nc))
    # Compute metrics per component
    X1 = np.ones((Ne,Nv)) #(Ne,Nv)
    X2 = np.repeat((tes/tes.mean())[:,np.newaxis].T,Nv,axis=0).T   #<--------------------   MAYBE NEEDS A MINUS SIGN   (Ne,Nv)
    print(" +              Multi-process Characterize Components -> Ncpu = %d" % Ncpus)
    pool   = Pool(processes=Ncpus)
    result = pool.map(_characterize_this_component_se, [{
                'c':          c,   'F_MAX':F_MAX, 'Z_MAX':Z_MAX,
                'X1':         X1,  'X2':X2,
                'aff':        aff, 'head':head, 'outDir':outDir, 'outPrefix':outPrefix,
                'mask':       mask,
                'B':          np.atleast_3d(beta)[:,:,c].transpose(),
                'weight_map': (oc_ICA_maps[:,c]**2.)*voxelwiseQA,
                'c_mask':     oc_ICA_maps_mask[:,c],
                'writeOuts':  writeOuts,
                'doFM': doFM, 'doMedian':doMedian
                } for c in np.arange(Nc)])
    feat_names = ['cID','Kappa','Rho','Var','maxFR2','maxFS0','K/R', 'maxZICA','NvZmask','NvFR2mask','NvFS0mask','NvKapMask','NvRhoMask','Dan']
    feat_vals  = np.zeros((Nc,len(feat_names)))

    for c in range(Nc):
        Weight_maps[:,c] = (oc_ICA_maps[:,c]**2.)*voxelwiseQA
        Kappa_maps[:,c]  = result[c]['Kappa_map']
        Rho_maps[:,c]    = result[c]['Rho_map']
        F_S0_maps[:,c]    = result[c]['FS0']
        F_R2_maps[:,c]    = result[c]['FR2']
        c_S0_maps[:,c]    = result[c]['cS0']
        c_R2_maps[:,c]    = result[c]['cR2']
        p_S0_maps[:,c]    = result[c]['pS0']
        p_R2_maps[:,c]    = result[c]['pR2']
        F_S0_masks[:,c]   = result[c]['F_S0_mask']
        F_R2_masks[:,c]   = result[c]['F_R2_mask']
        Kappa_masks[:,c]  = result[c]['Kappa_mask']
        Rho_masks[:,c]    = result[c]['Rho_mask']
        if doFM:
            FM_Slope_map[:,c] = result[c]['FM_Slope']
            FM_Inter_map[:,c] = result[c]['FM_Inter']
            FM_p_map[:,c]     = result[c]['FM_p']
            FM_r_map[:,c]     = result[c]['FM_r']
            FM_Slope_err_map[:,c] = result[c]['FM_Slope_err']
            FM_Slope_T_map[:,c]   = result[c]['FM_Slope_T']
            FM_Slope_p_map[:,c]   = result[c]['FM_Slope_p']
        feat_vals[c,0]    = c
        feat_vals[c,1]    = result[c]['Kappa']
        feat_vals[c,2]    = result[c]['Rho']
        feat_vals[c,3]    = 100*((ICA_maps[:,c]**2).sum()/totalvar)


        FR2_mask_arr     = ma.masked_array(F_R2_maps[:,c], mask=np.logical_not(Kappa_masks[:,c])) #abs(Kappa_masks[:,c]-1))
        feat_vals[c,4]    = FR2_mask_arr.max()

        FS0_mask_arr     = ma.masked_array(F_S0_maps[:,c], mask=np.logical_not(Rho_masks[:,c])) #abs(Rho_masks[:,c]-1))
        feat_vals[c,5]    = FS0_mask_arr.max()

        feat_vals[c,6]    = feat_vals[c,1] / feat_vals[c,2]

        ZICA_mask_arr    = ma.masked_array(oc_ICA_maps[:,c], mask=np.logical_not(oc_ICA_maps_mask[:,c])) #abs(ICA_maps_mask[:,c]-1))
        feat_vals[c,7]    = ZICA_mask_arr.max()

        feat_vals[c,8]    = ICA_maps_mask[:,c].sum()
        feat_vals[c,9]    = F_R2_masks[:,c].sum()
        feat_vals[c,10]   = F_S0_masks[:,c].sum()
        feat_vals[c,11]   = Kappa_masks[:,c].sum()
        feat_vals[c,12]   = Rho_masks[:,c].sum()

        # DAN METRIC
        if doFM:
            aux_mask      = np.logical_and((FM_p_map[:,c]<0.05),(FM_Slope_p_map[:,c]<0.05))
            aux_numerator = Weight_maps[aux_mask,c].sum()
            aux_denominat = Weight_maps[:,c].sum()
            aux_metric = aux_numerator/aux_denominat
            print("[%d] -> aux_mask%s | aux_numerator%s | DF=%f" % (c,str(aux_mask.shape),str(Weight_maps[aux_mask,c].shape),aux_metric))

    df_feats = pd.DataFrame(data=feat_vals,columns=feat_names)
    df_feats.to_csv(outDir+outPrefix+'.DF.csv')
    df_feats['cID'] = df_feats['cID'].astype(int)

    niiwrite_nv(beta      , mask,outDir+outPrefix+'.chComp.Beta.nii',aff ,head)
    niiwrite_nv(F_S0_maps , mask,outDir+outPrefix+'.chComp.FS0.nii',aff ,head)
    niiwrite_nv(F_R2_maps , mask,outDir+outPrefix+'.chComp.FR2.nii',aff ,head)
    niiwrite_nv(F_S0_masks, mask,outDir+outPrefix+'.chComp.FS0.mask.nii',aff ,head)
    niiwrite_nv(F_R2_masks, mask,outDir+outPrefix+'.chComp.FR2.mask.nii',aff ,head)
    niiwrite_nv(c_S0_maps , mask,outDir+outPrefix+'.chComp.cS0.nii',aff ,head)
    niiwrite_nv(c_R2_maps , mask,outDir+outPrefix+'.chComp.cR2.nii',aff ,head)
    niiwrite_nv(p_S0_maps , mask,outDir+outPrefix+'.chComp.pS0.nii',aff ,head)
    niiwrite_nv(p_R2_maps , mask,outDir+outPrefix+'.chComp.pR2.nii',aff ,head)

    niiwrite_nv(Kappa_maps,  mask,outDir+outPrefix+'.chComp.Kappa.nii',aff ,head)
    niiwrite_nv(Kappa_masks, mask,outDir+outPrefix+'.chComp.Kappa_mask.nii',aff ,head)
    niiwrite_nv(Rho_masks,   mask,outDir+outPrefix+'.chComp.Rho_mask.nii',aff ,head)
    niiwrite_nv(Rho_maps,    mask,outDir+outPrefix+'.chComp.Rho.nii',aff ,head)
    niiwrite_nv(Weight_maps, mask,outDir+outPrefix+'.chComp.weightMaps.nii',aff ,head)
    if doFM:
        niiwrite_nv(FM_Slope_map , mask,outDir+outPrefix+'.chComp.FM.Slope.nii',aff ,head)
        niiwrite_nv(FM_Inter_map , mask,outDir+outPrefix+'.chComp.FM.Inter.nii',aff ,head)
        niiwrite_nv(FM_p_map , mask,outDir+outPrefix+'.chComp.FM.p.nii',aff ,head)
        niiwrite_nv(FM_r_map , mask,outDir+outPrefix+'.chComp.FM.r.nii',aff ,head)
        niiwrite_nv(FM_Slope_err_map , mask,outDir+outPrefix+'.chComp.FM.Slope.err.nii',aff ,head)
        niiwrite_nv(FM_Slope_T_map , mask,outDir+outPrefix+'.chComp.FM.Slope.T.nii',aff ,head)
        niiwrite_nv(FM_Slope_p_map , mask,outDir+outPrefix+'.chComp.FM.Slope.p.nii',aff ,head)
    return df_feats
Exemplo n.º 2
0
    # ---------------------
    origMask_needed = True
    origMask_path = os.path.join(outputDir, options.prefix + '.mask.orig.nii')
    if options.reuse:
        if os.path.exists(origMask_path):
            print("++ INFO [Main]: Re-using existing original mask [%s]" %
                  (origMask_path))
            mask, _, _ = meu.niiLoad(origMask_path)
            mask = (mask > 0)
            origMask_needed = False
    if origMask_needed:
        if options.mask_file == None:
            print("++ INFO [Main]: Generating initial mask from data.")
            mask = meu.mask4MEdata(mepi_data)
            meu.niiwrite_nv(
                mask[mask], mask,
                options.out_dir + options.prefix + '.mask.orig.nii', mepi_aff,
                mepi_head)
        else:
            print("++ INFO [Main]: Using user-provided mask.")
            if not os.path.exists(options.mask_file):
                print("++ Error: Provided mask [%s] does not exist." %
                      options.mask_file)
                sys.exit()
            mask, _, _ = meu.niiLoad(options.mask_file)
            mask = (mask > 0)
    Nv = np.sum(mask)
    print(" +              Number of Voxels in mask [Nv=%i]" % Nv)

    # Reshape the input data
    # ----------------------
    SME = mepi_data[mask, :, :].astype(float)  #(Nv,Ne,Nt)
Exemplo n.º 3
0
def _characterize_this_component_se(item):
    B          = item['B']              #(Ne,Nv)
    X1         = item['X1']             #(Ne,Nv)
    X2         = item['X2']             #(Ne,Nv)
    aff        = item['aff']
    head       = item['head']
    mask       = item['mask']
    c          = item['c']
    outDir     = item['outDir']
    outPrefix  = item['outPrefix']
    F_MAX      = item['F_MAX']
    Z_MAX      = item['Z_MAX']
    weight_map = item['weight_map']
    c_mask     = item['c_mask']
    writeOuts  = item['writeOuts']
    doFM       = item['doFM']
    doMedian  = item['doMedian']
    Ne, Nv     = B.shape
    Kappa_mask = c_mask.copy()
    Rho_mask   = c_mask.copy()
    # Write the fits for the differente echoes into file (very useful for debugging purposes)
    if writeOuts:
           niiwrite_nv(B.T,mask,outDir+outPrefix+'.chComp.EXTRA.Beta'+str(c).zfill(3)+'.nii',aff,head)
    # S0 Model
    coeffs_S0        = (B*X1).sum(axis=0)/(X1**2).sum(axis=0)        #(Nv,)
    estima_S0        = X1*np.tile(coeffs_S0,(Ne,1))
    SSR_S0           = (estima_S0**2).sum(axis=0)
    SSE_S0           = ((B-estima_S0)**2).sum(axis=0)
    dofSSR_S0        = 1
    dofSSE_S0        = Ne -1
    F_S0             = (SSR_S0/dofSSR_S0) / (SSE_S0/dofSSE_S0) #(Nv,)
    p_S0             = 1-f.cdf(F_S0,1,Ne-1)
    F_S0_mask        = (p_S0 < 0.05)
    F_S0[F_S0>F_MAX] = F_MAX
    F_S0_AllValues   = F_S0.copy()
    if writeOuts:
        niiwrite_nv((X1*np.tile(coeffs_S0,(Ne,1))).T,mask,outDir+outPrefix+'.chComp.EXTRA.S0Fit'+str(c).zfill(3)+'.nii',aff,head)

    # R2 Model
    # If beta_i = alpha_i*TE/mean(TE) + error --> To solve this RTO model (Regression Through the Origin), it is possible to obtain
    # the alpha_i that provides the best fit (in a least-squares fashion) by simply using the equation below
    # Please look at: Eishenhouer JG "Regression throught the origin" Technical Statistics (25):3, 2003
    coeffs_R2        = (B*X2).sum(axis=0)/(X2**2).sum(axis=0)        #(Nv,)
    estima_R2        = X2*np.tile(coeffs_R2,(Ne,1))
    SSR_R2           = (estima_R2**2).sum(axis=0)
    SSE_R2           = ((B-estima_R2)**2).sum(axis=0)
    dofSSR_R2        = 1
    dofSSE_R2        = Ne -1
    F_R2             = (SSR_R2/dofSSR_R2) / (SSE_R2/dofSSE_R2) #(Nv,)
    p_R2             = 1-f.cdf(F_R2,1,Ne-1)
    F_R2_mask        = (p_R2 < 0.05)
    F_R2[F_R2>F_MAX] = F_MAX
    F_R2_AllValues   = F_R2.copy()

    if writeOuts:
        niiwrite_nv((X2*np.tile(coeffs_R2,(Ne,1))).T,mask,outDir+outPrefix+'.chComp.EXTRA.R2Fit'+str(c).zfill(3)+'.nii',aff,head)

    # Mask for spatial averaging
    Kappa_mask   = np.logical_and(c_mask, np.logical_or(F_R2_mask, F_S0_mask)) #(Nv,)
    Rho_mask     = Kappa_mask.copy() #(Nv,)

    # Kappa Computation
    Kappa_map  = F_R2 * weight_map
    Kappa_map  = Kappa_map/(weight_map[Kappa_mask].mean())
    Kappa_map  = Kappa_map * Kappa_mask           # weigths from the voxels entering the computation
    if doMedian:
        Kappa = np.median(Kappa_map[Kappa_mask])
    else:
        Kappa = np.mean(Kappa_map[Kappa_mask])

    # Rho Computation
    Rho_map  = F_S0 * weight_map
    Rho_map  = Rho_map/(weight_map[Rho_mask].mean())
    Rho_map  = Rho_map * Rho_mask           # weigths from the voxels entering the computation
    if doMedian:
        Rho = np.median(Rho_map[Rho_mask])
    else:
        Rho = np.mean(Rho_map[Rho_mask])

    # EXTRA CODE
    if doFM==True:
        FullModel_Slope = np.zeros((Nv,))
        FullModel_Inter = np.zeros((Nv,))
        FullModel_p     = np.zeros((Nv,))
        FullModel_r     = np.zeros((Nv,))
        FullModel_Slope_err = np.zeros((Nv,))
        FullModel_Slope_T   = np.zeros((Nv,))
        FullModel_Slope_p   = np.zeros((Nv,))
        for v in range(Nv):
            FullModel_Slope[v], FullModel_Inter[v], FullModel_r[v], FullModel_p[v], FullModel_Slope_err[v] = linregress(X2[:,v],B[:,v])
            FullModel_Slope_T[v] = FullModel_Slope[v]/FullModel_Slope_err[v]
            FullModel_Slope_p[v] = 2*(1-t.cdf(np.abs(FullModel_Slope_T[v]),Ne-2))
        return {'Kappa':Kappa, 'Rho':Rho, 'Kappa_map':Kappa_map, 'Rho_map':Rho_map, 'FS0':F_S0_AllValues, 'FR2':F_R2_AllValues, 'cS0':coeffs_S0, 'cR2':coeffs_R2,
            'Kappa_mask':Kappa_mask,'Rho_mask':Rho_mask, 'F_R2_mask':F_R2_mask, 'F_S0_mask':F_S0_mask, 'pR2':p_R2, 'pS0':p_S0,
            'FM_Slope':FullModel_Slope, 'FM_Inter':FullModel_Inter, 'FM_p':FullModel_p, 'FM_r':FullModel_r,
            'FM_Slope_err':FullModel_Slope_err, 'FM_Slope_T':FullModel_Slope_T, 'FM_Slope_p':FullModel_Slope_p}
    else:
        return {'Kappa':Kappa, 'Rho':Rho, 'Kappa_map':Kappa_map, 'Rho_map':Rho_map, 'FS0':F_S0_AllValues, 'FR2':F_R2_AllValues, 'cS0':coeffs_S0, 'cR2':coeffs_R2,
            'Kappa_mask':Kappa_mask,'Rho_mask':Rho_mask, 'F_R2_mask':F_R2_mask, 'F_S0_mask':F_S0_mask, 'pR2':p_R2, 'pS0':p_S0}