This repository contains analysis scripts for an uncertainty-aware atlas registration and quantification workflow for whole-brain light-sheet fluorescence microscopy (LSFM) datasets, using brainreg, link to GitHub.
The workflow was developed to assess how registration parameter choices influence propagated atlas regions and downstream TH+ cell counts in anatomically ambiguous brain regions such as the substantia nigra pars compacta (SNpc).
The pipeline:
- Generates a brainreg parameter-sweep job table.
- Runs brainreg registrations as independent SLURM array jobs.
- Computes global and local registration QC metrics.
- Builds SNpc probability maps and consensus masks.
- Extracts full-resolution TH and AF crops around the SNpc region.
- Runs Cellpose on each TH crop.
- Counts detected cells within each parameter-specific and consensus SNpc mask.
- Computes cell-level regional assignment probabilities across registrations.
.
├── config/
│ ├── config.yaml
│ ├── samples.csv
| |── ARA2_annotation_info_avail_regions.csv
│ └── midbrain_bbox.json
│
├── scripts/
│ ├── 01_make_brainreg_jobs.py
│ ├── 02_submit_brainreg_sweep.sh
│ ├── run_brainreg_array.sh
│ │
│ ├── 03_compute_qc_metrics.py
│ ├── 04_build_consensus_maps.py
│ │
│ ├── 05_cellpose_local.py
│ ├── 05_make_cellpose_jobs.py
│ ├── run_cellpose_one_crop.py
│ ├── run_cellpose_array.sh
│ ├── 06_submit_cellpose_jobs.sh
│ │
│ ├── 07_count_cells_in_masks.py
│ └── 08_compute_cell_region_probabilities.py
01_make_brainreg_jobs.py generates a parameter sweep table from:
config/config.yamlconfig/samples.csv
Each row corresponds to a single registration job.
results/job_tables/brainreg_jobs.csv
results/job_tables/brainreg_jobs.metadata.json
python scripts/01_make_brainreg_jobs.py \
--config config/config.yaml \
--samples config/samples.csv \
--out results/job_tables/brainreg_jobs.csv02_submit_brainreg_sweep.sh automatically:
- reads the generated job table
- calculates the required SLURM array size
- submits all registrations
Each registration is executed independently through:
run_brainreg_array.sh
sbatch scripts/02_submit_brainreg_sweep.shEach registration executes a command of the form:
brainreg "$AF_DIR" "$SAVE_DIR" \
-v "$vx" "$vy" "$vz" \
--orientation "$orientation" \
--atlas "$atlas" \
--debug \
--save-original-orientation \
-a "$TH_DIR" "$UNPROCESSED_DIR" \
--n-free-cpus 0 \
--bending-energy-weight "$bending" \
--grid-spacing "$grid" \
--smoothing-sigma-floating "$smoothing"Controls:
-
atlas settings, the region_label is found from the ontology file, this can be changed according to the region of interest
-
brainreg sweep parameters
-
QC thresholds
-
consensus thresholds
-
Cellpose settings
-
NOTE: If you are interested in a different region, the midbrain_bbox.json (bounding box) used during the local QC will have to be recalculated and extracted
atlas:
name: "perens_lsfm_mouse_20um"
reference_image: "/path/to/reference_atlas_image.tif"
resolution_um: 20
region_label: 374
brainreg:
grid_spacing: [-2, -3, -5, -10, -30, -50]
bending_energy_weight: [0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 0.95, 1.0]
smoothing_sigma_floating: [-1.0]
orientation: sal
voxel_size: [20, 20, 20]
qc:
iou_threshold: 0.9
grad_p95_threshold: 0.045
ssim_top_fraction: 0.5
consensus:
thresholds: [0.05, 0.1, 0.25, 0.5, 0.75, 1.0]
cellpose:
custom_weights: "/path/to/custom/model"
diameter: 9
channels: [0, 0]
flow_threshold: 0.8
cellprob_threshold: -3.0
stitch_threshold: 0.3
do_3D: false
use_gpu: trueExpected columns:
sample_id,input_type,output_group,af_image,th_image,unprocessed_image,tissue_mask,af_full_res,th_full_res,pixel_size_z,pixel_size_y,pixel_size_x| Column | Description |
|---|---|
af_image |
Downsampled autofluorescence image used for brainreg |
th_image |
Downsampled TH image passed to brainreg |
unprocessed_image |
Additional image passed to brainreg |
tissue_mask |
Binary tissue mask used for overlap QC |
af_full_res |
Full-resolution autofluorescence stack |
th_full_res |
Full-resolution TH stack |
pixel_size_z/y/x |
Full-resolution voxel size |
03_compute_qc_metrics.py computes:
- tissue-atlas IoU
- atlas overlap fractions
- tissue overlap fractions
- deformation gradient statistics
- Jacobian determinant statistics
- local SSIM within a fixed midbrain bounding box
results/qc_tables/qc_results.csv
python scripts/03_compute_qc_metrics.py \
--config config/config.yaml \
--samples config/samples.csv \
--jobs results/job_tables/brainreg_jobs.csv \
--out results/qc_tables/qc_results.csv04_build_consensus_maps.py:
- extracts SNpc masks from registered atlases
- generates voxel-wise probability maps
- computes consensus regions
- extracts full-resolution TH/AF crops
- converts between coronal and axial orientations
- rescales atlas-space masks into full-resolution image space
results/region_assets/
└── sample_id/
└── input_type/
├── probability_maps_downsampled/
├── bbox/
├── fullres_crops/
├── parameter_masks_fullres_crop/
├── registered_atlas_fullres_crop/
└── consensus_masks_fullres_crop/
python scripts/04_build_consensus_maps.py \
--config config/config.yaml \
--samples config/samples.csv \
--qc-table results/qc_tables/qc_results.csv \
--out-dir results/region_assets \
--consensus-qc-column passes_final_qc05_cellpose_local.py runs Cellpose on each extracted TH crop.
python scripts/05_cellpose_local.py \
--config config/config.yaml \
--region-assets results/region_assetsresults/region_assets/sample_id/input_type/cellpose_outputs/
Containing:
cellpose_labels.tif
cellpose_run_metadata.json
Generate Cellpose job table:
python scripts/05_make_cellpose_jobs.py \
--config config/config.yaml \
--region-assets results/region_assetsSubmit jobs:
bash scripts/06_submit_cellpose_jobs.shEach array task executes:
run_cellpose_array.sh
which runs:
run_cellpose_one_crop.py
07_count_cells_in_masks.py:
- masks Cellpose labels using SNpc masks
- counts overlapping labels
- saves masked label images
- computes counts for:
- parameter-specific masks
- consensus masks
python scripts/07_count_cells_in_masks.py \
--region-root results/region_assets \
--out-dir results/cell_countsresults/cell_counts/
├── cell_counts_by_parameter_mask.csv
└── cell_counts_by_consensus_mask.csv
Masked labels are additionally saved under:
parameter_masked_cell_labels/
consensus_masked_cell_labels/
08_compute_cell_region_probabilities.py computes probabilistic atlas assignments for each detected cell across all registrations.
Each cell is assigned a probability of belonging to each atlas region based on its centroid location across registered atlas crops.
python scripts/08_compute_cell_region_probabilities.py \
--region-root results/region_assets \
--ontology-csv /path/to/ontology.csv \
--out-dir results/cell_region_probabilitiesALL_cell_region_probabilities_long.csv
ALL_cell_region_probabilities_wide.csv
ALL_cell_region_grouped_probabilities_long.csv
ALL_cell_region_grouped_probabilities_wide.csv
run_manifest.csv
brainreg outputs are generated in coronal orientation, while the original full-resolution LSFM stacks are axial.
Current conversion logic:
def coronal_to_axial(arr):
return np.flip(np.transpose(arr, (1, 0, 2)), axis=2)This transform should be visually verified for new datasets.
Some scripts automatically convert Linux cluster paths to Windows mapped drives when running locally:
/vast/scratch -> U:\
/vast/imaging -> V:\
Adjust these mappings if required for your local environment.
Core dependencies include:
brainreg
cellpose
numpy
pandas
tifffile
scikit-image
scipy
dask-image
pyyaml
This repository is intended as a reproducibility workflow accompanying the associated manuscript, rather than a fully packaged general-purpose software tool.