JDG Lab Notebook

Making a region mapping for AAL

A friend in need is a friend indeed. My friend needed to map AAL regions on to a freesurfer surface for some data visualizations. Here's what I came up with:

Notebook Setup

Initialize matlab in the notebook

%load_ext pymatbridge
Starting MATLAB on ZMQ socket ipc:///tmp/pymatbridge-b9c23e9a-92dd-47f3-a12b-071332b41720
Send 'exit' command to kill the server
......MATLAB started and connected!

Define some variables

# put some system-specific variables in the namespace ('le' dict)
%run ~/set_localenv_vars.py

nb_name = 'making_a_region_mapping_for_aal'

# this is where I look for stuf
outdir = '%s/notebooks/%s' %(le['data_dir'],nb_name)

# this is where stuff actually lives
outdir_loc = '%s/%s' %('/mnt/titania-hecuba/mcintosh_lab/john/Data/notebooks', nb_name)

# symlink the two
import os
if not os.path.isdir(outdir_loc): os.makedirs(outdir_loc)
if not os.path.isdir(outdir): os.system('ln -s %s %s' %(outdir_loc,outdir))
                                        

# root path to tvb-library, tvb-data, etc. libraries. 
tvb_folder = le['code_dir'] + '/libraries_of_mine/github'


# stuff for workdocs-cloudfiles

aws_key = 'drowssaperucesyreva'
aws_secret = '?teytidettopsuoyevah'  

Importage

# Generic imports

import os,sys,glob,h5py,itertools,multiprocessing,\
       numpy as np,pandas as pd
from datetime import datetime
from time import time
from scipy import optimize
from scipy.spatial import cKDTree

# Visualization stuff

%matplotlib inline
from matplotlib import pyplot as plt
from IPython.display import Image,display as d
import seaborn as sns



# Neuroimaging stuff

import nibabel as nib
from nilearn.plotting import plot_roi
from dipy.tracking.utils import apply_affine

# TVB stuff

nbso,nbse = sys.stdout,sys.stderr # hack part 1/2 to keep output printing properly
sys.path.append(tvb_folder +'/tvb-library')
sys.path.append(tvb_folder + '/tvb-data')
from tvb.simulator.lab import *
sys.stdout,sys.stderr = nbso,nbse  # ...hack part 2/2
from tvb.simulator.plot.tools import plot_surface_mpl,plot_surface_mpl_mv


from copy import copy # seems that this needs to be done last otherwise it gets 
                      # replaced with numpy copy function
    
    
# aws api and workdocs-cloudfiles stuff

sys.path.append(le['ipynb_workdocs_dir'])
from new_utils import nb_fig,cloudfiles_nb

Define some functions

def get_roi_xyzs(roi_file,match_type='com'):
  """
  Get xyz centroids for a nifti image volume with labelled voxels
  """
    
  roi_img = nib.load(roi_file)
  roi_dat = roi_img.get_data()  
  roi_aff = roi_img.get_affine()
    
  roi_list = np.unique(roi_dat)
  if match_type == 'com':
    roi_all_inds = np.indices(roi_img.shape)
    roi_coords = [apply_affine(roi_aff, roi_all_inds[:,roi_dat==r].T) for r in roi_list]
    roi_xyzs = np.array([np.mean(rc,axis=0) for rc in roi_coords])
        
  elif match_type == 'xyzcuts': 
            
    roi_xyzs = np.array([find_xyz_cut_coords(roi_img,roi_dat==r,
                                             activation_threshold=0) for r in roi_list])

  return roi_xyzs,roi_list
    
    
    
def roi_to_surface(roi_xyzs,vtx_xyzs,faces):
  """
  Find nearest surface vertex and faces for a set of xyz locations
  """
    
  # average across the coords for each vertex comprising the face
  faces_xyz = np.mean(vtx_xyzs[faces],axis=1)
  # same as  fac_xyzs2 = np.array([np.mean(xyz[f],axis=0) for f in fac])    
    
  face_nearestroi_inds = cKDTree(roi_xyzs).query(faces_xyz, k=1)[1]
  #(gives the closest index in A for each point in B)
    
  vtx_nearestroi_inds = cKDTree(roi_xyzs).query(vtx_xyzs, k=1)[1]
    
  return vtx_nearestroi_inds,face_nearestroi_inds


def project_to_cs(roi_vals,vtx_nearestroi_inds,face_nearestroi_inds):
  """
  Take a set of roi values and a set of roi-vertex mappings and return 
  a of mapped vertex values 
  """
    
  vtx_vals = np.array([roi_vals[v] for v in vtx_nearestroi_inds])
  face_vals = np.array([roi_vals[v] for v in face_nearestroi_inds])
      
  return vtx_vals,face_vals
  

Initialize aws api and workdocs-cloudfiles folder

cnb = cloudfiles_nb('aws', [aws_key,aws_secret])
cnb.initialize_folder(nb_name)

Go to output folder

os.chdir(outdir)

Ok, let's get cracking

Get AAL data

I downloaded the WFU PickAtlas

# Define some variables


tpl_dir = le['code_dir'] + '/libraries_of_others/misc/WFU_PickAtlas_3.0.5b/wfu_pickatlas/MNI_atlas_templates'
aal_txt_file = '%s/aal_MNI_V4.txt' %tpl_dir
aal_nii_file = '%s/aal_MNI_V4.nii' %tpl_dir

Get ROI names, IDs, and XYZs

df_aal_txt = pd.read_csv(aal_txt_file, sep='\t', skiprows=[0],names=['region'])
df_aal_txt.index.names = ['id']
_tmp = pd.DataFrame(['None'],columns=['region']); _tmp.index.names = ['id']
df_aal_txt = df_aal_txt.append(_tmp).sort_index()


roi_xyzs,roi_list = get_roi_xyzs(aal_nii_file)

df_roi_xyz = pd.DataFrame(roi_xyz.T,['x', 'y', 'z'],
                          columns=df_aal_txt)

df_aal_txt_xyz = df_aal_txt.copy()
df_aal_txt_xyz['x'] = roi_xyz[:,0]
df_aal_txt_xyz['y'] = roi_xyz[:,1]
df_aal_txt_xyz['z'] = roi_xyz[:,2]


hemis = []
for r in df_aal_txt['region']: 
  if '_R' in r: hemis.append('R')
  elif '_L' in r: hemis.append('L')
  else: hemis.append('N')
                
hemis = np.array(hemis)

df_aal_txt_xyz['hemi'] = hemis

# Sort by hemisphere and y axis value
sortreg = df_aal_txt_xyz.sort_values(['hemi', 'y'])['region']

# (probably a simpler way of doing this...)
df_aal_txt_xyz = df_aal_txt_xyz.reset_index().set_index('region')\
                 .ix[sortreg].reset_index().set_index(['id', 'hemi'])

Plot the atlas

(0_0)
broken link AAL Atlas regions..

Plot the ROIs

(0_0)
broken link AAL Atlas ROI centroid xys.

Get euclidean distances between regions (we will use this as 'data' to plot)

_rxyz = df_aal_txt_xyz[['x','y','z']].values

nr,nc = _rxyz.shape

eucldists = np.array([np.abs((_rxyz - np.tile(r,[nr,1]))).sum(axis=1) for r in _rxyz])

df_eucldists = pd.DataFrame(eucldists,columns=df_aal_txt_xyz['region'],
                            index=df_aal_txt_xyz['region'])
(0_0)
broken link AAL Atlas ROI euclidean distrances.

We'll use a single column of this matrix as data to plot. This corresponds to the first region in the list; Occipital_Sup.

Pick it out for left and right hemispheres separately:

df_aal_txt_xyz_lh = df_aal_txt_xyz.query("hemi=='L'")
df_aal_txt_xyz_rh = df_aal_txt_xyz.query("hemi=='R'")

data_lh = df_eucldists.iloc[:,0].ix[df_aal_txt_xyz_lh['region']]
data_rh = df_eucldists.iloc[:,0].ix[df_aal_txt_xyz_rh['region']]

Get mapping to fsaverage

Read in fsaverage surface and faces

lhp_file = '/software/freesurfer/subjects/fsaverage/surf/lh.pial'
lhp_vtx,lhp_tri = nib.freesurfer.read_geometry(lhp_file)

rhp_file = '/software/freesurfer/subjects/fsaverage/surf/rh.pial'
rhp_vtx,rhp_tri = nib.freesurfer.read_geometry(rhp_file)

First check that the AAL and fsaverage coordinates line up

(0_0)
broken link AAL Atlas ROI xyzs vs. fsaverage vertex xyzs..

Indeed they do. The parts that appear not to are cerebellar regions in the AAL that aren't in fsaverage. The rest line up just fine.

So now find the nearest vertex coordinates for each roi centroid:

rlh = df_aal_txt_xyz_lh[['x', 'y', 'z']].values
rrh = df_aal_txt_xyz_rh[['x', 'y', 'z']].values

vtx_nrst_lh,tri_nrst_lh = roi_to_surface(rlh,lhp_vtx,lhp_tri)
vtx_nrst_rh,tri_nrst_rh = roi_to_surface(rrh,rhp_vtx,rhp_tri)

# that gave us correspondences in order of the roi coordinates list entered. 
# now get the corresponding id values

vtx_nrst_lh_id = roi_xyz_lh.index.get_level_values('id')[vtx_nrst_lh]
vtx_nrst_rh_id = roi_xyz_rh.index.get_level_values('id')[vtx_nrst_rh]

To check that this has been done correctly, let's pick a handful of regions and plot the roi centroid and the vertices that have been identified as corresponding to it:

(0_0)
broken link AAL Atlas ROI xyzs vs. fsaverage vertex xyzs - selected regions.

That looks ok to me.

Ok so now let's do a surface plot of our Euclidean distance data vector using this region mapping

(0_0)
broken link AAL Euclidean distances - matplotlib surface viz.

Different shading (gouraud); brings out the ROI boundaries a bit more clearly

(0_0)
broken link AAL Euclidean distances - matplotlib surface viz, gouraud shading.

Get the data vector

data_lh_vtx,data_lh_tri = project_to_cs(data_lh,vtx_nrst_lh,tri_nrst_lh)
data_rh_vtx,data_rh_tri = project_to_cs(data_rh,vtx_nrst_rh,tri_nrst_rh)

These should look the same as the above plots

(0_0)
broken link AAL Euclidean distances - matplotlib surface viz, gouraud shading; data vector.

Good. Just testing.

Now plot in matlab:

%%matlab -i outdir,lhp_file,rhp_file,data_lh_vtx,data_rh_vtx

fig = figure; 

[lhp_vtx_ml,lhp_tri_ml] = read_surf(lhp_file);
[rhp_vtx_ml,rhp_tri_ml] = read_surf(rhp_file);

viewfromleft = [-90,10];
viewfromright = [90,10];
viewfromtop = [0,90]; 
viewfrombottom = [0,-90];

subplot(2,2,1)
patch('Vertices',lhp_vtx_ml,'Faces',lhp_tri_ml+1,'CData',data_lh_vtx,...
      'FaceColor','interp','EdgeColor','none');
view(viewfromleft);
axis image; axis off; camlight; lighting gouraud; 
colormap('jet'); caxis auto;

subplot(2,2,2)
patch('Vertices',rhp_vtx_ml,'Faces',rhp_tri_ml+1,'CData',data_rh_vtx,...
      'FaceColor','interp','EdgeColor','none');
view(viewfromright);
axis image; axis off; camlight; lighting gouraud; 
colormap('jet'); caxis auto;

subplot(2,2,3)
patch('Vertices',lhp_vtx_ml,'Faces',lhp_tri_ml+1,'CData',data_lh_vtx,...
      'FaceColor','interp','EdgeColor','none');
view(viewfromright);
axis image; axis off; camlight; lighting gouraud; 
colormap('jet'); caxis auto;

subplot(2,2,4)
patch('Vertices',rhp_vtx_ml,'Faces',rhp_tri_ml+1,'CData',data_rh_vtx,...
      'FaceColor','interp','EdgeColor','none');
view(viewfromleft);
axis image; axis off; camlight; lighting gouraud; 
colormap('jet'); caxis auto;

print(fig, '-dpng',[outdir '/aal_euclidean_distances_patch.png']);
                    
close(fig)
(0_0)
broken link AAL Euclidean distances - matplab patch viz.

Finally, write the various mappings to file