Skip to content

Commit

Permalink
Merge pull request #6 from courtois-neuromod/flat_patch
Browse files Browse the repository at this point in the history
Flat patch
  • Loading branch information
bpinsard authored Nov 19, 2024
2 parents 58a57f9 + 3ef4a89 commit 7cd91cf
Showing 1 changed file with 115 additions and 51 deletions.
166 changes: 115 additions & 51 deletions doc/flatmaps.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
Importing flat maps into Pycortex
=================================
Instructions to import full-brain flat patches from Freesurfer to Pycortex
Instructions to import full-brain flat patches from Freesurfer to [pycortex](https://github.com/gallantlab/pycortex)

Flat patches were cut using TKSurfer (Freesurfer version 6.0.0) for each CNeuromod subject. \
They are saved under anat.freesurfer/sub-0*/surf/
Flat patches were cut using [TKSurfer (Freesurfer version 6.0.0)](https://surfer.nmr.mgh.harvard.edu/fswiki/DownloadAndInstall) for each CNeuromod subject. \
They are saved under ``anat.freesurfer/sub-0*/surf/``

**Links and documentation** \
Flat maps for dummies: https://docs.google.com/document/d/19EgtxN0BezHSO1hqkfmzzcpVCPRjrOcBDvrkXp0owxk/edit
**Links and documentation**
- Flat maps for dummies: https://docs.google.com/document/d/19EgtxN0BezHSO1hqkfmzzcpVCPRjrOcBDvrkXp0owxk/edit
- Pycortex repo: https://github.com/gallantlab/pycortex

------------
**Step 1. Install Pycortex**\
https://github.com/gallantlab/pycortex
**Step 1. Install pycortex**

Source: https://github.com/gallantlab/pycortex

```bash
# First, install some required dependencies (if not already installed)
pip install -U setuptools wheel numpy cython
# Install the latest release of pycortex from pip
pip install -U pycortex
```
Note: for my installation to work, I had to downgrade nibabel to version 3.0, and numpy to version 1.23.4

Note that, for my installation to work, I had to downgrade ``nibabel`` to version ``3.0``, and ``numpy`` to version ``1.23.4``
```bash
pip install -U nibabel==3.0
pip install -U numpy==1.23.4
```

Install ipython to configure pycortex and test your installation
Install ``ipython`` to help configure pycortex and test your installation
```bash
pip install ipython
```

In ipython, print location of the default pycortex filestore
In ipython, print the location of the default pycortex ``filestore`` (the database, analogous to Freesurfer's ``SUBJECTS_DIR``). This is where the subjects' individual files are saved, including maps and transformations.
```python
import cortex
cortex.database.default_filestore
```
This is where the subjects' individual files are saved, including maps and transformations

Print location of the pycortex config file
Print the location of the pycortex ``config`` file
```python
import cortex
cortex.options.usercfg
Expand Down Expand Up @@ -74,14 +75,14 @@ A web browser window should pop up with a demo subject.


------------
**Step 2. Import Freesurfer subject data into Pycortex**
**Step 2. Import Freesurfer subject data into pycortex**

You need to have Freesurfer installed as a dependency.\
You need to have ``Freesurfer`` installed as a dependency.\
https://surfer.nmr.mgh.harvard.edu/

This step imports Freesurfer subject data (surfaces and volumes) into the pycortex filestore (database) specified in your pycortex config file (e.g., /abs/path/to/venv/pycortex_venv/share/pycortex/db)
This step imports Freesurfer subject data (surfaces and volumes) into the pycortex filestore (database) specified in your pycortex config file (e.g., ``/abs/path/to/venv/pycortex_venv/share/pycortex/db``)

In your bash shell, set the SUBJECTS_DIR variable (Freesurfer's default subject data directory) to the repository that contains the CNeuromod freesurfer data (e.g., anat.freesurfer)
In your bash shell, set the ``SUBJECTS_DIR`` variable (Freesurfer's default subject data directory) to the repository that contains the CNeuromod freesurfer data (e.g., ``anat.freesurfer``)
```bash
SUBJECTS_DIR="/abs/path/to/neuromod/anat.freesurfer"
```
Expand All @@ -105,7 +106,7 @@ for s in slist:
------------
**Step 3. Generate mean epi images from functional runs**

For each CNeuromod subject, generate a single mean epi image from a single run of bold data preprocessed with fmriprep. It can be any run, from any task (friends, mario, localizer, things, resting state). This bold file will be used to generate a transformation from functional space to Freesurfer/Pycortex surface space, so that any volume of functional data (activation map, noise ceilings, retinotopy results, R2 values, t-contrasts) can be projected onto the subject's flat map.
For each CNeuroMod subject, generate a single mean epi image from a single run of bold data preprocessed with ``fmriprep``. It can be any run, from any task (``friends``, ``mario``, ``localizer``, ``cneuromod-things``, resting state). This bold file will be used to generate a transformation from functional space to Freesurfer/pycortex surface space, so that any volume of functional data (activation map, noise ceilings, retinotopy results, R2 values, t-contrasts) can be projected onto the subject's flat map.

For each subject, select a run of bold data that is either in native (T1w) space or in MNI (normalized) space, depending on the data you are working with.

Expand All @@ -128,19 +129,19 @@ for s in slist:
------------
**Step 4. Generate transforms from functional space (T1w or MNI) to Freesurfer surface space**

First, save each subject's mean epi image (ref.nii.gz, from step 2) into the pycortex database. \
First, save each subject's mean epi image (``ref.nii.gz`` from step 2) into the pycortex filestore. \
E.g.,
```bash
cd /abs/path/to/venv/pycortex_venv/share/pycortex/db/sub-01/transforms
mkdir align_auto_ref
cp /abs/path/to/ref_imgages/sub-01/ref.nii.gz align_auto_ref
```

In your bash shell, set the SUBJECTS_DIR variable to your freesurfer data directory
In your bash shell, set the ``SUBJECTS_DIR`` variable to your Freesurfer data directory
```bash
SUBJECTS_DIR="/abs/path/to/neuromod/anat.freesurfer"
```
Note that the cortex.align.automatic function requires matching directory names for the same subject in Freesurfer's SUBJECTS_DIR and in pycortex/db in order to work.
Note that the ``cortex.align.automatic`` function requires matching directory names for the same subject in Freesurfer's ``SUBJECTS_DIR`` and in ``pycortex/db`` in order to work.

In ipython:
```python
Expand All @@ -162,13 +163,13 @@ for s in slist:
------------
**Step 5. Import Freesurfer flat maps into Pycortex**

Flat maps, or patches, live in anat.freesurfer/sub-0*/surf under the name "full", to indicate cortical patches that include most of the cortex (excluding some of the medial surface).
Flat maps, or patches, live in ``anat.freesurfer/sub-0*/surf`` under the name "full", to indicate cortical patches that include most of the cortex (excluding some of the medial surface).

In your bash shell, set the SUBJECTS_DIR variable to your freesurfer data directory
In your bash shell, set the ``SUBJECTS_DIR`` variable to your Freesurfer data directory
```bash
SUBJECTS_DIR="/abs/path/to/neuromod/anat.freesurfer"
```
Warning: the following command line should only be ran once to import a new flat map. If maps are annotated (e.g., using Inkscape to draw ROIs), running this command line again will overwrite the overlay.svg file on which the annotations displayed in Pycortex are saved, and annotations will be lost.
Warning: the following command line should **only be ran once to import a NEW flat map**. If maps are annotated (e.g., using Inkscape to draw ROIs), running this command line again will overwrite the ``overlay.svg`` file on which the annotations displayed in ``pycortex`` are saved, and annotations will be lost.

In ipython:
```python
Expand All @@ -182,11 +183,11 @@ for s in slist:
```

------------
**Step 6. Visualize functional data in Pycortex**
**Step 6. Visualize functional data in pycortex**

Here is an example of how to visualize a functional volume on a flat map in Pycortex. For simplicity, I'm loading the mean epi volume (ref.nii.gz) that was used to compute the transformation from volume to surface. It's not a particularly informative map, but it's a quick way to test whether volumes are loading properly into Pycortex and gain familiarity with the interface.
Here is an example of how to visualize a functional volume on a flat map in pycortex. For simplicity, I'm loading the mean epi volume (``ref.nii.gz``) that was used to compute the transformation from volume to surface. It's not a particularly informative map, but it's a quick way to test whether volumes are loading properly into pycortex and gain familiarity with the interface.

In your shell, set the Freesurfer SUBJECTS_DIR variable to your freesurfer data directory
In your shell, set the ``SUBJECTS_DIR`` variable to your Freesurfer data directory
```bash
SUBJECTS_DIR="/abs/path/to/neuromod/anat.freesurfer"
```
Expand Down Expand Up @@ -229,23 +230,30 @@ cmap = 'terrain' #'magma'
'''
Convert the volume to surface space usin the transformation from step 4
'''
surf_vol = cortex.Volume(vol_arr, f'sub-{s}', 'align_auto', vmin=min_val, vmax=max_val, cmap=cmap)
surf_vol = cortex.Volume(
vol_arr,
f'sub-{s}',
'align_auto',
vmin=min_val,
vmax=max_val,
cmap=cmap,
)

'''
Visualize volume in pycortex web browser interface
'''
cortex.webshow(surf_vol, recache=True)
```

The web interface will open a window in your default web browser. To view the surface as a flat map with the interface, click 'Open Control:surface' (top right), and drag the 'unfold' slider from 0 to 1. The visualization of ROIs drawn in Inkscape can be managed under 'rois'. Different options are available for brightness, opacity, etc.
The web interface will open a window in your default web browser. To view the surface as a flat map with the interface, click ``Open Control:surface`` (top right), and drag the ``unfold`` slider from ``0`` to ``1``. The visualization of ROIs drawn in Inkscape can be managed under ``rois``. Different options are available for brightness, opacity, etc.


------------
**Step 7. Drawing ROI boundaries in Inkscape**

Install Inkscape to draw ROI boundaries on flat surface. Depending on your installation, you may need to specify the Inkscape path in the pycortex config file ~/.config/pycortex/options.cfg under dependency paths
Install ``Inkscape`` to draw ROI boundaries on flat surface. Depending on your installation, you may need to specify the Inkscape path in the pycortex config file ``~/.config/pycortex/options.cfg`` under ``dependency paths``

In your shell, set the Freesurfer SUBJECTS_DIR variable to your freesurfer data directory
In your shell, set the ``SUBJECTS_DIR`` variable to your Freesurfer data directory
```bash
SUBJECTS_DIR="/abs/path/to/neuromod/anat.freesurfer"
```
Expand All @@ -272,47 +280,78 @@ vareas_arr[vareas_arr == 0] = np.nan
'''
Transform volume into surface
'''
vareas_vol = cortex.Volume(vareas_arr, f'sub-{s}', 'align_auto', vmin=np.nanmin(vareas_arr), vmax=np.nanmax(vareas_arr), cmap='Retinotopy_RYBCR')
vareas_vol = cortex.Volume(
vareas_arr,
f'sub-{s}',
'align_auto',
vmin=np.nanmin(vareas_arr),
vmax=np.nanmax(vareas_arr),
cmap='Retinotopy_RYBCR',
)

'''
Import flattened surface file into Inkscape to draw ROI boundaries
Add ROI layers on overlay.SVG file that is loaded in pycortex
Import flattened surface file into Inkscape to guide the drawing of ROI boundaries
Add layers of ROI annotations on the overlay.SVG file that is loaded in pycortex
This command opens the Inkscape interface
'''
cortex.add_roi(vareas_vol, **dict(colorbar_location='left', pixelwise=True, nanmean=True, with_curvature=True))
cortex.add_roi(
vareas_vol,
**dict(
colorbar_location='left',
pixelwise=True,
nanmean=True,
with_curvature=True,
),
)

'''
NOTE: this step (cortex.add_roi) can be repeated many times per subject to load additional surfaces in Inkscape in order to draw / validate ROIs boundries with multiple statistical maps.
NOTE: this step (cortex.add_roi) can be repeated many times per subject to load
additional surfaces in Inkscape in order to draw / validate ROIs boundries with
multiple statistical maps.
E.g.,
'''
FFA_path = f'{floc_roi_path}/fLoc_T1w_FFA.nii.gz'
FFA_arr = np.swapaxes(nib.load(FFA_path).get_fdata(), 0, -1)
FFA_arr[FFA_arr <= 0] = np.nan
FFA_vol = cortex.Volume(FFA_arr, f'sub-{s}', 'align_auto', vmin=np.nanmin(FFA_arr), vmax=np.nanmax(FFA_arr))

cortex.add_roi(FFA_vol, **dict(colorbar_location='left', pixelwise=True, nanmean=True, with_curvature=True))
FFA_vol = cortex.Volume(
FFA_arr,
f'sub-{s}',
'align_auto',
vmin=np.nanmin(FFA_arr),
vmax=np.nanmax(FFA_arr),
)

cortex.add_roi(
FFA_vol,
**dict(
colorbar_location='left',
pixelwise=True,
nanmean=True,
with_curvature=True,
),
)
```

Drawing ROI boundaries in Inkscape:
- label each image used as reference to trace ROI boundaries (e.g. "FFA"). On the right menu, select the 'Layers and Objects' tab. Under 'data', rename the 'img_new_roi' layer appropriately (right click, select rename layer)
- create one ROI layer per drawn ROI (even if these ROIs are derived from the same data map). Under 'rois:shapes', right click, select 'Add Layer' (directly under 'shapes'). Rename the new_roi layer with relevant ROI name (V1, V2, etc): right click, 'Rename Layer'. Note that this name is the ROI label that will show in Pycortex (although label display can be turned off).
- On the ROI layer, draw each ROI (typically one drawn 'object' per hemishpere, on the same layer, so that each side receives its own label).
- To draw: select the pencil tool (icon is on the left-hand side menu; 'Draw freehand lines (P)');
- label each image used as reference to trace ROI boundaries (e.g. ``FFA``). On the right menu, select the ``Layers and Objects`` tab. Under ``data``, rename the ``img_new_roi`` layer appropriately (right click, select ``rename layer``)
- create one ROI layer per drawn ROI (even if these ROIs are derived from the same data map). Under ``rois:shapes``, right click, select ``Add Layer`` (directly under ``shapes``). Rename the ``new_roi`` layer with relevant ROI name (``V1``, ``V2``, etc): right click, ``Rename Layer``. Note that this name is the ROI label that will be displayed in pycortex (although label display can be turned off).
- On the ROI layer, draw each ROI (typically one drawn ``object`` per hemishpere, on the same layer, so that each side receives its own label).
- To draw: select the ``pencil`` tool (icon is on the left-hand side menu; ``Draw freehand lines (P)``);
https://inkscape-manuals.readthedocs.io/en/latest/pencil-tool.html
- At the bottom left, select the stroke colour: white or black stroke, no fill. (e.g., Fill: None, Stroke: white). Click on the colour to set the fill color, hold shift and click on colour to set the stroke color.
- In the top right menu, under Mode, select Create Bspline path. Set smoothing (e.g., 17.0)
- In the top right-hand menu, select the 'Fill and stroke style' tab, then the 'Stroke style' sub-tab. Set stroke width (e.g., 4.0 pixels (px))
- At the bottom left, select the ``stroke colour``: white or black stroke, no fill. (e.g., ``Fill: None``, ``Stroke: white``). Click on the colour to set the fill color, hold shift and click on colour to set the stroke color.
- In the top right menu, under ``Mode``, select ``Create Bspline path``. Set ``smoothing`` (e.g., ``17.0``)
- In the top right-hand menu, select the ``Fill and stroke style`` tab, then the ``Stroke style`` sub-tab. Set stroke ``width`` (e.g., ``4.0`` pixels (px))
- You can zoom in to draw more easily
- Click on the relevant ROI layer, then hold the left mouse button and trace the ROI contour on the image. This action will create a sub-layer path object directly under the ROI layer. Use a single continuous stroke per ROI per hemisphere: each stroke creates a different path object who receives the ROI label when displayed (one ROI label per hemisphere is typical). Multiple strokes (paths) per ROI within the same hemishpere will generate multiple ROI labels when displayed in Pycortex (although those can be turned off).
- When you're done tracing from a given data map, click 'File:Save' (top left menu), and then close Inkscape. Do not use 'Save As'. Changes (ROI annotations) will be saved directly on the subject's overlay.svg file. The next time you load a volume as a surface in Pycortex (e.g., as shown in Step 6 above), the ROI boundaries you drew and their labels will be visible. The visualization of ROIs drawn in Inkscape can be managed under "rois" in the Pycortex interface.
- When you're done tracing from a given data map, click ``File:Save`` (top left menu), and then close Inkscape. Do not use ``Save As``. Changes (ROI annotations) will be saved directly on the subject's ``overlay.svg`` file. The next time you load a volume as a surface in pycortex (e.g., as shown in Step 6 above), the ROI boundaries you drew and their labels will be visible. The visualization of ROIs drawn in Inkscape can be managed under ``rois`` in the pycortex interface.

------------
**Step 8. Save images**

Once you are happy with the images you see in Pycortex, you can export them using matplotlib
Once you are happy with the images you see in pycortex, you can export them using ``matplotlib``

In your shell, set the Freesurfer SUBJECTS_DIR variable to your freesurfer data directory
In your shell, set the ``SUBJECTS_DIR`` variable to your Freesurfer data directory
```bash
SUBJECTS_DIR="/abs/path/to/neuromod/anat.freesurfer"
```
Expand All @@ -328,7 +367,14 @@ import matplotlib.pyplot as plt
Follow the same steps as in Step 6 to transform your volume into surface.
This time, drawn ROIs should be visible.
'''
surf_vol = cortex.Volume(vol_arr, f'sub-{s}', 'align_auto', vmin=min_val, vmax=max_val, cmap=cmap)
surf_vol = cortex.Volume(
vol_arr,
f'sub-{s}',
'align_auto',
vmin=min_val,
vmax=max_val,
cmap=cmap,
)

'''
Create and save the image
Expand All @@ -337,10 +383,28 @@ Examples of use: https://python.hotexamples.com/examples/cortex/-/quickshow/pyth
fig = plt.figure(figsize=(17,10))

# without color bar
cortex.quickshow(surf_vol, pixelwise=True, with_colorbar=False, nanmean=True, with_curvature=True, sampler='trilinear', with_labels=False, with_rois=True, curv_brightness=1.0, dpi=300, fig=fig)
cortex.quickshow(
surf_vol,
pixelwise=True,
with_colorbar=False,
nanmean=True,
with_curvature=True,
sampler='trilinear',
with_labels=False,
with_rois=True,
curv_brightness=1.0,
dpi=300,
fig=fig,
)
fig.savefig(f'/abs/path/to/project/figures/sub-{s}_results.png', dpi=300)

# with color bar
fig = cortex.quickshow(surf_vol, pixelwise=True, colorbar_location='left', nanmean=True, with_curvature=True)
fig = cortex.quickshow(
surf_vol,
pixelwise=True,
colorbar_location='left',
nanmean=True,
with_curvature=True,
)
fig.savefig(f'/abs/path/to/project/figures/sub-{s}_wCbar.png', dpi=300)
```

0 comments on commit 7cd91cf

Please sign in to comment.