Image Processing: Preparing Labelled Geometry for Meshing¶
Overview¶
This notebook segments the MRI-derived inputs needed for the brain mesh generation part of the project. It takes the subject's structural MRI and anatomical segmentation, performs image registration and tissue-processing steps, and combines the outputs into a cleaned geometry volume that can be used downstream for mesh creation. We perform the following steps:
- Linear registration: align the subject images to a standard orientation;
- Image segmentation:
- FAST segmentation: extract and segement brain tissue; and
- BET segmentation: separate non-brain tissue from the whole-head image;
- Combine separated structures to produce
pre_model.nii.gzvolume for the later mesh-generation workflow.
Before running, please make sure:
- FSL is installed, and environment variable configured Verify your installation
- The required supporting files are available in
src/dependencies/ - This notebook is run from the repository's
notebooks/ordocs/folder. - The subject input files are present in
data/subjects/<subject_name>/img/fs_seg/.
# Automatically print the runtime of each executed cell
%load_ext autotime
%matplotlib inline
time: 614 ms (started: 2026-05-06 18:13:12 +01:00)
0. Path configuration¶
Run this cell to configure the paths used globally in this notebook.
The subject sub0045 is used as example head.
from pathlib import Path
import os
# --------------------------------------------------
# User settings
# --------------------------------------------------
# Change this value when running the notebook for a different subject.
subject_name = "sub0045"
# Percentage of the inferior-superior image dimension removed from the skin mask.
# This reduces identifiable facial features in the final volume.
skull_privacy_percentage = 10
# --------------------------------------------------
# Project paths
# --------------------------------------------------
# This assumes the notebook is run from the repository's notebooks/ folder.
PROJECT_ROOT = Path.cwd().resolve().parent
SRC_DIR = PROJECT_ROOT / "src"
DEPENDENCIES_DIR = SRC_DIR / "dependencies"
SUBJECTS_DIR = PROJECT_ROOT / "data" / "subjects"
SUBJECT_DIR = SUBJECTS_DIR / subject_name
IMG_DIR = SUBJECT_DIR / "img"
FS_SEG_DIR = IMG_DIR / "fs_seg"
TMP_DIR = SUBJECT_DIR / "tmp"
FAST_DIR = IMG_DIR / "fast"
BET_DIR = IMG_DIR / "bet"
# --------------------------------------------------
# Input files
# --------------------------------------------------
T1_FILE = FS_SEG_DIR / "T1.nii.gz"
BRAIN_FILE = FS_SEG_DIR / "brain.nii.gz"
ASEG_FILE = FS_SEG_DIR / "aseg.nii.gz"
MNI_REF_FILE = DEPENDENCIES_DIR / "MNI152_T1_1mm_brain.nii.gz"
# --------------------------------------------------
# Output files
# --------------------------------------------------
FINAL_PRE_MODEL = SUBJECT_DIR / "pre_model.nii.gz"
# --------------------------------------------------
# Create working folders
# --------------------------------------------------
for folder in [TMP_DIR, FAST_DIR, BET_DIR]:
folder.mkdir(parents=True, exist_ok=True)
# --------------------------------------------------
# Export variables for later bash cells
# --------------------------------------------------
env_vars = {
"PROJECT_ROOT": PROJECT_ROOT,
"SRC_DIR": SRC_DIR,
"DEPENDENCIES_DIR": DEPENDENCIES_DIR,
"SUBJECTS_DIR": SUBJECTS_DIR,
"SUBJECT_DIR": SUBJECT_DIR,
"IMG_DIR": IMG_DIR,
"FS_SEG_DIR": FS_SEG_DIR,
"TMP_DIR": TMP_DIR,
"FAST_DIR": FAST_DIR,
"BET_DIR": BET_DIR,
"T1_FILE": T1_FILE,
"BRAIN_FILE": BRAIN_FILE,
"ASEG_FILE": ASEG_FILE,
"MNI_REF_FILE": MNI_REF_FILE,
"FINAL_PRE_MODEL": FINAL_PRE_MODEL,
}
for key, value in env_vars.items():
os.environ[key] = str(value)
os.environ["SUBJECT_NAME"] = subject_name
os.environ["SKULL_PRIVACY_PERCENTAGE"] = str(skull_privacy_percentage)
# --------------------------------------------------
# Setup summary
# --------------------------------------------------
print("Setup complete.")
print(f"Subject: {subject_name}")
print(f"Input folder: {FS_SEG_DIR}")
print(f"Working folders: {TMP_DIR.name}/, {FAST_DIR.name}/, {BET_DIR.name}/")
print(f"Final output: {FINAL_PRE_MODEL}")
Setup complete. Subject: sub0045 Input folder: /Users/yc4421/Library/CloudStorage/Box-Box/GTA/ReCoDE_BrainMesh/ReCoDE-brain-mesh-creation/data/subjects/sub0045/img/fs_seg Working folders: tmp/, fast/, bet/ Final output: /Users/yc4421/Library/CloudStorage/Box-Box/GTA/ReCoDE_BrainMesh/ReCoDE-brain-mesh-creation/data/subjects/sub0045/pre_model.nii.gz time: 5.83 ms (started: 2026-05-06 18:13:13 +01:00)
# Check required files
required_files = {
"T1 image": T1_FILE,
"Brain image": BRAIN_FILE,
"Anatomical segmentation": ASEG_FILE,
"MNI reference image": MNI_REF_FILE,
}
missing_files = [name for name, path in required_files.items() if not path.exists()]
if missing_files:
print("Missing required files:")
for name in missing_files:
print(f" - {name}: {required_files[name]}")
raise FileNotFoundError("One or more required input files are missing.")
print("All required input files found.")
All required input files found. time: 4.29 ms (started: 2026-05-06 18:13:40 +01:00)
1. Image registration¶
First, the subject images are reoriented and aligned to a standard MNI reference space using a rigid registration using FSL FLIRT.. This means the image can move and rotate, but it is not stretched, scaled, or sheared. This is important because the aim is only to standardise the orientation of the head, while keeping the subject-specific brain size and shape unchanged.
The same transformation is then applied to the T1 image and the anatomical segmentation so that all images remain in the same space. The transform is estimated from the skull-stripped brain image, then applied to the T1 image and the anatomical segmentation so that all inputs remain aligned with each other.
%%bash
set -euo pipefail
# --------------------------------------------------
# Input and output files
# --------------------------------------------------
mni_ref="$MNI_REF_FILE"
input_brain="$BRAIN_FILE"
input_t1="$T1_FILE"
input_aseg="$ASEG_FILE"
output_brain="$FAST_DIR/brain_std.nii.gz"
output_t1="$TMP_DIR/T1_std.nii.gz"
output_aseg="$TMP_DIR/aseg_std.nii.gz"
transform_mat="$TMP_DIR/brain_to_mni.mat"
echo "=== Image registration ==="
echo "Subject: $SUBJECT_NAME"
echo "Reference image: ${mni_ref##*/}"
echo
# --------------------------------------------------
# [1/3] Register brain image to MNI space
# --------------------------------------------------
echo "[1/3] Registering brain image"
echo " Input: ${input_brain##*/}"
echo " Output: ${output_brain##*/}"
echo " Transform: ${transform_mat##*/}"
flirt -in "$input_brain" \
-ref "$mni_ref" \
-out "$output_brain" \
-omat "$transform_mat" \
-dof 6 \
-searchrx -90 90 \
-searchry -90 90 \
-searchrz -90 90
[ -f "$output_brain" ] && echo " Status: registered"
[ -f "$transform_mat" ] && echo " Status: transform saved"
echo
# --------------------------------------------------
# [2/3] Apply the same transform to the T1 image
# --------------------------------------------------
echo "[2/3] Transforming T1 image"
echo " Input: ${input_t1##*/}"
echo " Output: ${output_t1##*/}"
flirt -in "$input_t1" \
-ref "$mni_ref" \
-out "$output_t1" \
-applyxfm \
-init "$transform_mat" \
-interp trilinear
[ -f "$output_t1" ] && echo " Status: transformed"
echo
# --------------------------------------------------
# [3/3] Apply the same transform to the anatomical segmentation
# --------------------------------------------------
echo "[3/3] Transforming anatomical segmentation"
echo " Input: ${input_aseg##*/}"
echo " Output: ${output_aseg##*/}"
echo " Interpolation: nearest neighbour"
flirt -in "$input_aseg" \
-ref "$mni_ref" \
-out "$output_aseg" \
-applyxfm \
-init "$transform_mat" \
-interp nearestneighbour
[ -f "$output_aseg" ] && echo " Status: transformed"
echo
echo "Image registration complete."
=== Image registration ===
Subject: sub0045
Reference image: MNI152_T1_1mm_brain.nii.gz
[1/3] Registering brain image
Input: brain.nii.gz
Output: brain_std.nii.gz
Transform: brain_to_mni.mat
Status: registered
Status: transform saved
[2/3] Transforming T1 image
Input: T1.nii.gz
Output: T1_std.nii.gz
Status: transformed
[3/3] Transforming anatomical segmentation
Input: aseg.nii.gz
Output: aseg_std.nii.gz
Interpolation: nearest neighbour
Status: transformed
Image registration complete.
time: 30.3 s (started: 2026-05-06 18:13:54 +01:00)
2. Image segmentation¶
Next, the workflow combines information from FreeSurfer and FSL.
FreeSurfer provides detailed anatomical labels (in our input images), while FSL is used here to identify broader tissue classes such as grey matter, white matter, and CSF. Using both together gives a more complete starting point for the later geometry and mesh-generation steps.
Segment brain tissue with FAST¶
The registered brain image is then segmented using FSL FAST.
FAST separates the brain into broad tissue classes, including CSF, grey matter, and white matter. These masks are useful because the anatomical segmentation alone does not always fully define all tissue boundaries needed for mesh generation.
In this pipeline, FAST is mainly used to provide complementary tissue information, especially for identifying CSF and checking whether any brain tissue is missing from the anatomical segmentation.
%%bash
set -euo pipefail
# --------------------------------------------------
# Input and output files
# --------------------------------------------------
input_t1="$TMP_DIR/T1_std.nii.gz"
input_aseg="$TMP_DIR/aseg_std.nii.gz"
check_mat="$TMP_DIR/T1_to_aseg.mat"
fast_input="$FAST_DIR/brain_std.nii.gz"
fast_output_seg="$FAST_DIR/brain_std_seg.nii.gz"
echo "=== FAST tissue segmentation ==="
echo "Subject: $SUBJECT_NAME"
echo
# --------------------------------------------------
# [1/2] Run a quick alignment check
# --------------------------------------------------
echo "[1/2] Checking T1 and segmentation alignment"
echo " Input: ${input_t1##*/}"
echo " Reference: ${input_aseg##*/}"
echo " Transform: ${check_mat##*/}"
flirt -in "$input_t1" \
-ref "$input_aseg" \
-omat "$check_mat" \
-nosearch \
-dof 6
[ -f "$check_mat" ] && echo " Status: transform saved"
echo
# --------------------------------------------------
# [2/2] Run FAST on the registered brain image
# --------------------------------------------------
echo "[2/2] Segmenting brain tissue with FAST"
echo " Input: ${fast_input##*/}"
echo " Output prefix: brain_std_*"
# FAST writes output files beside the input image. Running it from FAST_DIR keeps
# the generated files together and avoids long output prefixes.
cd "$FAST_DIR"
fast "${fast_input##*/}"
[ -f "$fast_output_seg" ] && echo " Status: segmentation created"
echo
echo "FAST tissue segmentation complete."
=== FAST tissue segmentation ===
Subject: sub0045
[1/2] Checking T1 and segmentation alignment
Input: T1_std.nii.gz
Reference: aseg_std.nii.gz
Transform: T1_to_aseg.mat
Status: transform saved
[2/2] Segmenting brain tissue with FAST
Input: brain_std.nii.gz
Output prefix: brain_std_*
Process is interrupted.
time: 2min 32s (started: 2026-05-06 18:14:50 +01:00)
Estimate skull and skin boundaries using BET¶
Next, BET and BETSURF are used to estimate the outer head surfaces.
The anatomical segmentation describes many internal brain structures, but it does not directly provide complete skull and skin masks. BET/BETSURF helps estimate these outer layers from the registered T1 image.
These masks are later combined with the brain and CSF labels to build a fuller head volume for mesh generation.
%%bash
set -euo pipefail
# --------------------------------------------------
# Input and output files
# --------------------------------------------------
input_t1="$TMP_DIR/T1_std.nii.gz"
bet_output_prefix="$BET_DIR/T1_bet"
identity_mat="$BET_DIR/identity.mat"
betsurf_mesh="$BET_DIR/T1_bet_mesh.vtk"
betsurf_prefix="$BET_DIR/betsurf"
betsurf_outskin_mask="$BET_DIR/betsurf_outskin_mask.nii.gz"
echo "=== BET and BETSURF surface estimation ==="
echo "Subject: $SUBJECT_NAME"
echo
# --------------------------------------------------
# [1/3] Run BET on the registered T1 image
# --------------------------------------------------
echo "[1/3] Running BET"
echo " Input: ${input_t1##*/}"
echo " Output prefix: ${bet_output_prefix##*/}"
# BET expects an output prefix, not a complete .nii.gz output filename.
bet "$input_t1" "$bet_output_prefix" -s -f 0.35 -g -0.05 -e -R
[ -f "${bet_output_prefix}.nii.gz" ] && echo " Status: BET output created"
echo
# --------------------------------------------------
# [2/3] Create an identity transform for BETSURF
# --------------------------------------------------
echo "[2/3] Preparing identity transform"
echo " Output: ${identity_mat##*/}"
if [ ! -f "$identity_mat" ]; then
printf "1 0 0 0\n0 1 0 0\n0 0 1 0\n0 0 0 1\n" > "$identity_mat"
fi
[ -f "$identity_mat" ] && echo " Status: identity transform ready"
echo
# --------------------------------------------------
# [3/3] Run BETSURF
# --------------------------------------------------
echo "[3/3] Running BETSURF"
echo " Input: ${input_t1##*/}"
echo " Mesh output: ${betsurf_mesh##*/}"
echo " Output prefix: ${betsurf_prefix##*/}"
betsurf --t1only -m \
"$input_t1" \
"$betsurf_mesh" \
"$identity_mat" \
"$betsurf_prefix"
[ -f "$betsurf_outskin_mask" ] && echo " Status: surface masks created"
echo
echo "BET and BETSURF surface estimation complete."
=== BET and BETSURF ===
Subject: sub0045
[1/3] Running BET on input: T1_std
Output prefix: T1_bet
Status: BET output created
[2/3] Preparing transform: identity.mat
Status: ready
[3/3] Running BETSURF
Input image: T1_std
Output mesh: T1_bet_mesh.vtk
Output prefix: betsurf
Status: surface masks created
BET and BETSURF complete.
time: 31.8 s (started: 2026-04-28 12:45:19 +01:00)
Update outer skull mask¶
The BETSURF skin mask can sometimes miss parts of the outer head surface.
To make the skin layer more complete, a simple whole-head mask is created from the registered T1 image and merged with the BETSURF outer skin mask. This produces a more continuous skin boundary before the final model volume is assembled.
%%bash
set -euo pipefail
# --------------------------------------------------
# Input and output files
# --------------------------------------------------
input_t1="$TMP_DIR/T1_std.nii.gz"
wholehead_mask="$TMP_DIR/wholehead_mask.nii.gz"
outskin_mask="$BET_DIR/betsurf_outskin_mask.nii.gz"
echo "=== Outer skin mask update ==="
echo "Subject: $SUBJECT_NAME"
echo
# --------------------------------------------------
# [1/2] Create a whole-head mask from the registered T1 image
# --------------------------------------------------
echo "[1/2] Creating whole-head mask"
echo " Input: ${input_t1##*/}"
echo " Output: ${wholehead_mask##*/}"
fslmaths "$input_t1" \
-thr 39 \
-bin \
"$wholehead_mask"
[ -f "$wholehead_mask" ] && echo " Status: whole-head mask created"
echo
# --------------------------------------------------
# [2/2] Merge the whole-head mask with the BETSURF outer skin mask
# --------------------------------------------------
echo "[2/2] Updating outer skin mask"
echo " Input 1: ${wholehead_mask##*/}"
echo " Input 2: ${outskin_mask##*/}"
echo " Output: ${outskin_mask##*/}"
fslmaths "$wholehead_mask" \
-add "$outskin_mask" \
-bin \
"$outskin_mask"
[ -f "$outskin_mask" ] && echo " Status: outer skin mask updated"
echo
echo "Outer skin mask update complete."
=== Outer skin mask update ===
Subject: sub0045
[1/2] Creating whole-head mask
Input: T1_std.nii.gz
Output: wholehead_mask.nii.gz
Status: whole-head mask created
[2/2] Updating outer skin mask
Input 1: wholehead_mask.nii.gz
Input 2: betsurf_outskin_mask.nii.gz
Output: betsurf_outskin_mask.nii.gz
Status: outer skin mask updated
Outer skin mask update complete.
time: 1.49 s (started: 2026-05-06 18:17:38 +01:00)
3. Create the final pre-model volume¶
This is the main assembly step, where the different segmentation sources are combined into one labelled image.
The FreeSurfer anatomical segmentation provides the main internal brain anatomy in our input images. FAST provides extra tissue information, especially CSF, grey matter, and white matter. BETSURF provides the skull and skin boundaries.
The aim is to create a continuous labelled volume containing brain tissue, CSF, skull, and skin. This is important because holes or missing voxels in the image can later become missing elements in the finite element mesh.
During this step, the pipeline also checks for brain voxels that appear in the FAST grey/white matter masks but are absent from the anatomical segmentation. These voxels are added back as missing brain tissue so that the final volume is more continuous.
The CSF mask is also refined so that the subarachnoid space forms a layer between the brain surface and the skull. Finally, skull and skin labels are added, and the skin layer is cropped slightly (by adjusting the privacy_percentage) to reduce facial detail.
%%bash
set -euo pipefail
# --------------------------------------------------
# Input parameters
# --------------------------------------------------
# Percentage of the image removed from the skin mask to reduce facial detail.
privacy_percentage="${SKULL_PRIVACY_PERCENTAGE}"
# --------------------------------------------------
# Input files
# --------------------------------------------------
fast_seg="$FAST_DIR/brain_std_seg.nii.gz"
aseg_std="$TMP_DIR/aseg_std.nii.gz"
t1_std="$TMP_DIR/T1_std.nii.gz"
outskull_mask="$BET_DIR/betsurf_outskull_mask.nii.gz"
outskin_mask="$BET_DIR/betsurf_outskin_mask.nii.gz"
# --------------------------------------------------
# Output file
# --------------------------------------------------
final_output="$SUBJECT_DIR/pre_model.nii.gz"
echo "=== Creating brain geometry ==="
echo "Subject: $SUBJECT_NAME"
echo "Final output: ${final_output##*/}"
echo
# Label values added in this step:
# 256 = CSF
# 257 = skull
# 260 = skin
# 300 = missing brain tissue estimated from FAST
# --------------------------------------------------
# [1/6] Extract FAST tissue classes
# --------------------------------------------------
echo "[1/6] Extracting FAST tissue classes"
echo " Input image: ${fast_seg##*/}"
echo " Outputs: csf_fast.nii.gz, gm_fast.nii.gz, wm_fast.nii.gz"
# FAST produces a labelled tissue segmentation. Here, the labels are separated
# into binary masks for CSF, grey matter, and white matter.
fslmaths "$fast_seg" -thr 0.5 -uthr 1.5 -bin "$TMP_DIR/csf_fast.nii.gz"
fslmaths "$fast_seg" -thr 1.5 -uthr 2.5 -bin "$TMP_DIR/gm_fast.nii.gz"
fslmaths "$fast_seg" -thr 2.5 -uthr 3.5 -bin "$TMP_DIR/wm_fast.nii.gz"
[ -f "$TMP_DIR/csf_fast.nii.gz" ] && echo " Status: CSF mask created"
[ -f "$TMP_DIR/gm_fast.nii.gz" ] && echo " Status: grey matter mask created"
[ -f "$TMP_DIR/wm_fast.nii.gz" ] && echo " Status: white matter mask created"
echo
# --------------------------------------------------
# [2/6] Add missing brain tissue to the anatomical segmentation
# --------------------------------------------------
echo "[2/6] Updating the anatomical segmentation"
echo " Input image: ${aseg_std##*/}"
echo " Output image: aseg_bm.nii.gz"
# The FreeSurfer anatomical segmentation may miss some brain tissue.
# FAST grey matter and white matter masks are therefore combined and compared
# against the anatomical segmentation.
fslmaths "$aseg_std" -bin "$TMP_DIR/aseg_std_bin.nii.gz"
fslmaths "$TMP_DIR/gm_fast.nii.gz" -bin "$TMP_DIR/gm_fast_bin.nii.gz"
fslmaths "$TMP_DIR/wm_fast.nii.gz" -bin "$TMP_DIR/wm_fast_bin.nii.gz"
fslmaths "$TMP_DIR/wm_fast_bin.nii.gz" -add "$TMP_DIR/gm_fast_bin.nii.gz" "$TMP_DIR/brain_fast.nii.gz"
# Voxels present in the FAST-derived brain mask but absent from the anatomical
# segmentation are treated as missing brain tissue and assigned label 300.
fslmaths "$TMP_DIR/brain_fast.nii.gz" -sub "$TMP_DIR/aseg_std_bin.nii.gz" -thr 1 -ero -dilM -dilM -ero -bin -mul 300 "$TMP_DIR/bm.nii.gz"
# Remove overlap before adding the missing-brain label. This preserves the
# original registered segmentation file.
fslmaths "$TMP_DIR/bm.nii.gz" -bin -mul 1000 "$TMP_DIR/bm_bin.nii.gz"
fslmaths "$aseg_std" -sub "$TMP_DIR/bm_bin.nii.gz" -thr 1 "$TMP_DIR/aseg_std_without_bm_overlap.nii.gz"
fslmaths "$TMP_DIR/bm.nii.gz" -add "$TMP_DIR/aseg_std_without_bm_overlap.nii.gz" "$TMP_DIR/aseg_bm.nii.gz"
[ -f "$TMP_DIR/aseg_bm.nii.gz" ] && echo " Status: missing brain tissue added"
echo
# --------------------------------------------------
# [3/6] Add CSF outside the ventricles
# --------------------------------------------------
echo "[3/6] Adding the CSF layer"
echo " Output image: aseg_bm_csf.nii.gz"
# The ventricles are already represented in the anatomical segmentation.
# Therefore, ventricular CSF is removed from the FAST-derived CSF mask before
# adding the surrounding CSF layer.
fslmaths "$TMP_DIR/csf_fast.nii.gz" -dilM -ero "$TMP_DIR/csf_fast.nii.gz"
fslmaths "$TMP_DIR/aseg_bm.nii.gz" -thr 42.5 -uthr 43.5 "$TMP_DIR/ventricleR.nii.gz"
fslmaths "$TMP_DIR/aseg_bm.nii.gz" -thr 3.5 -uthr 4.5 "$TMP_DIR/ventricleL.nii.gz"
fslmaths "$TMP_DIR/ventricleR.nii.gz" -add "$TMP_DIR/ventricleL.nii.gz" "$TMP_DIR/ventricles_both.nii.gz"
fslmaths "$TMP_DIR/ventricles_both.nii.gz" -bin "$TMP_DIR/ventricles_both.nii.gz"
fslmaths "$TMP_DIR/csf_fast.nii.gz" -sub "$TMP_DIR/ventricles_both.nii.gz" "$TMP_DIR/csf_no_vent.nii.gz"
fslmaths "$TMP_DIR/csf_no_vent.nii.gz" -thr 0.9 -uthr 1.1 "$TMP_DIR/csf_no_vent.nii.gz"
# Add the remaining CSF voxels to the segmentation using label 256.
fslmaths "$TMP_DIR/aseg_bm.nii.gz" -bin "$TMP_DIR/aseg_bm_bin.nii.gz"
fslmaths "$TMP_DIR/csf_no_vent.nii.gz" -bin -dilM -ero "$TMP_DIR/csf_no_vent_bin.nii.gz"
fslmaths "$TMP_DIR/csf_no_vent_bin.nii.gz" -sub "$TMP_DIR/aseg_bm_bin.nii.gz" -thr 1 -mul 256 "$TMP_DIR/csf.nii.gz"
fslmaths "$TMP_DIR/aseg_bm.nii.gz" -add "$TMP_DIR/csf.nii.gz" "$TMP_DIR/aseg_bm_csf.nii.gz"
# Fill small gaps around the brain/CSF mask and assign them to CSF.
fslmaths "$TMP_DIR/aseg_bm_csf.nii.gz" -bin "$TMP_DIR/aseg_bm_csf_bin.nii.gz"
fslmaths "$TMP_DIR/aseg_bm_csf_bin.nii.gz" -dilM -dilM -ero -ero "$TMP_DIR/aseg_bm_csf_bin_mask.nii.gz"
fslmaths "$TMP_DIR/aseg_bm_csf_bin_mask.nii.gz" -sub "$TMP_DIR/aseg_bm_csf_bin.nii.gz" "$TMP_DIR/csf_missing.nii.gz"
fslmaths "$TMP_DIR/csf_missing.nii.gz" -mul 256 "$TMP_DIR/csf_missing.nii.gz"
fslmaths "$TMP_DIR/csf_missing.nii.gz" -add "$TMP_DIR/aseg_bm_csf.nii.gz" "$TMP_DIR/aseg_bm_csf.nii.gz"
[ -f "$TMP_DIR/aseg_bm_csf.nii.gz" ] && echo " Status: CSF layer added"
echo
# --------------------------------------------------
# [4/6] Add skull around the brain and CSF
# --------------------------------------------------
echo "[4/6] Adding the skull layer"
echo " Output image: aseg_bm_csf_skull.nii.gz"
# A skull outline is first created around the brain/CSF mask. This is then
# combined with the BETSURF-derived outer skull mask.
fslmaths "$TMP_DIR/aseg_bm_csf.nii.gz" -bin -dilM -dilM -ero -ero "$TMP_DIR/aseg_bm_csf_bin.nii.gz"
fslmaths "$TMP_DIR/aseg_bm_csf_bin.nii.gz" -ero -dilM -dilM "$TMP_DIR/aseg_bm_csf_bin_dil.nii.gz"
fslmaths "$TMP_DIR/aseg_bm_csf_bin_dil.nii.gz" -sub "$TMP_DIR/aseg_bm_csf_bin.nii.gz" "$TMP_DIR/skull_outline.nii.gz"
fslmaths "$outskull_mask" -sub "$TMP_DIR/aseg_bm_csf_bin.nii.gz" "$TMP_DIR/T1_std_roi.nii.gz"
fslmaths "$TMP_DIR/T1_std_roi.nii.gz" -add "$TMP_DIR/skull_outline.nii.gz" "$TMP_DIR/skull_mask.nii.gz"
fslmaths "$TMP_DIR/skull_mask.nii.gz" -bin "$TMP_DIR/skull_mask.nii.gz"
# Keep only skull voxels outside the existing brain/CSF segmentation and assign
# them label 257.
fslmaths "$TMP_DIR/aseg_bm_csf.nii.gz" -bin "$TMP_DIR/aseg_bm_csf_bin.nii.gz"
fslmaths "$TMP_DIR/skull_mask.nii.gz" -sub "$TMP_DIR/aseg_bm_csf_bin.nii.gz" -thr 1 "$TMP_DIR/skull_mask.nii.gz"
fslmaths "$TMP_DIR/skull_mask.nii.gz" -mul 257 "$TMP_DIR/skull_mask.nii.gz"
fslmaths "$TMP_DIR/aseg_bm_csf.nii.gz" -add "$TMP_DIR/skull_mask.nii.gz" "$TMP_DIR/aseg_bm_csf_skull.nii.gz"
[ -f "$TMP_DIR/aseg_bm_csf_skull.nii.gz" ] && echo " Status: skull layer added"
echo
# --------------------------------------------------
# [5/6] Add skin and apply privacy cropping
# --------------------------------------------------
echo "[5/6] Adding the skin layer"
echo " Output image: aseg_bm_csf_skull_skin.nii.gz"
# The skin mask is taken from BETSURF and restricted to voxels outside the
# existing brain/CSF/skull segmentation.
fslmaths "$TMP_DIR/aseg_bm_csf_skull.nii.gz" -bin "$TMP_DIR/aseg_bm_csf_skull_bin.nii.gz"
fslmaths "$outskin_mask" -sub "$TMP_DIR/aseg_bm_csf_skull_bin.nii.gz" "$TMP_DIR/skin_mask.nii.gz"
# Crop the inferior part of the skin mask to reduce facial detail.
dim3=$(fslval "$t1_std" dim3)
dim3_min=$(( dim3 * privacy_percentage / 100 ))
fslmaths "$TMP_DIR/skin_mask.nii.gz" -roi 0 -1 0 -1 "$dim3_min" -1 0 -1 -bin "$TMP_DIR/skin_mask_anon.nii.gz"
# Assign the remaining skin voxels label 260 and add them to the final geometry.
fslmaths "$TMP_DIR/skin_mask_anon.nii.gz" -mul 260 "$TMP_DIR/skin_mask_anon.nii.gz"
fslmaths "$TMP_DIR/skin_mask_anon.nii.gz" -add "$TMP_DIR/aseg_bm_csf_skull.nii.gz" "$TMP_DIR/aseg_bm_csf_skull_skin.nii.gz"
[ -f "$TMP_DIR/aseg_bm_csf_skull_skin.nii.gz" ] && echo " Status: skin layer added"
echo
# --------------------------------------------------
# [6/6] Save the final combined volume
# --------------------------------------------------
echo "[6/6] Saving final output"
echo " Output file: ${final_output##*/}"
cp "$TMP_DIR/aseg_bm_csf_skull_skin.nii.gz" "$final_output"
[ -f "$final_output" ] && echo " Status: saved"
echo
echo "Brain geometry creation complete."
The history saving thread hit an unexpected error (OperationalError('attempt to write a readonly database')).History will not be written to the database.
=== Creating brain geometry ===
Subject: sub0045
Final output: pre_model.nii.gz
[1/6] Extracting FAST tissue classes
Input image: brain_std_seg.nii.gz
Outputs: csf_fast.nii.gz, gm_fast.nii.gz, wm_fast.nii.gz
Status: CSF mask created
Status: grey matter mask created
Status: white matter mask created
[2/6] Updating the anatomical segmentation
Input image: aseg_std.nii.gz
Output image: aseg_bm.nii.gz
Status: missing brain tissue added
[3/6] Adding the CSF layer
Output image: aseg_bm_csf.nii.gz
Status: CSF layer added
[4/6] Adding the skull layer
Output image: aseg_bm_csf_skull.nii.gz
Status: skull layer added
[5/6] Adding the skin layer
Output image: aseg_bm_csf_skull_skin.nii.gz
Status: skin layer added
[6/6] Saving final output
Output file: pre_model.nii.gz
Status: saved
Brain geometry creation complete.
time: 54.8 s (started: 2026-05-06 18:22:40 +01:00)
Further reading¶
- fast segmentation: Jenkinson M, Beckmann CF, Behrens TEJ, et al (2012) Fsl. Neuroimage 62:782–790
- bet segmentation: S.M. Smith. Fast robust automated brain extraction. Human Brain Mapping, 17(3):143-155, November 2002