FOCUS is an end-to-end preprocessing, alignment, and registration pipeline for spatial multiomics datasets. It integrates data acquired from different imaging and omics modalities on the same tissue section into a single, analysis-ready multimodal dataset. Supported modalities include microscopy images, mass spectrometry imaging (MSI/lipidomics), Raman spectroscopy, and spatial transcriptomics. The output is structured as MuData (.h5mu), making it immediately compatible with established single-cell and spatial omics frameworks such as scanpy, squidpy, and AnnData.
No programming is required to use FOCUS. The entire pipeline is driven by a JSON configuration file that can be built interactively through a web-based GUI.
FOCUS pre-print paper available on bioRxiv Full documentation available here
| Modality | Key "type" |
Input Format | Output Format |
|---|---|---|---|
| Fluorescence / brightfield microscopy | microscopy_image |
.tiff, .tif, .ome.tiff, .ome.tif, .qptiff, .czi |
OME-TIFF pyramid |
| Mass Spectrometry Imaging (MSI / lipidomics) | msi |
.imzML + .ibd |
AnnData .h5ad |
| Raman spectroscopy | raman |
.lif |
OME-TIFF (hyperspectral) |
| Spatial transcriptomics | st |
AnnData .h5ad |
AnnData .h5ad |
Raw Data ──► Preprocessing ──► Alignment ──► Registration ──► MuData dataset
(per modality) (interactive (feature-based (.h5mu)
web GUI) or interpolation)
- Preprocessing: modality-specific quality control, normalisation, background removal, and storage in a standardised format.
- Alignment: an interactive web GUI lets you visually overlay the reference modality onto each target modality (translate, rotate, scale, flip, and corner-distort) to record their spatial correspondence. Each sample is handled individually.
- Registration: maps the feature content of each modality onto the reference coordinate space. Four modes are available: deep-learning patch embeddings (
feature_extraction), Gaussian-weighted spot interpolation (spot_interpolation), equal-weight spot summation (spot_aggregation), and the same Gaussian interpolation applied to hyperspectral pixels (raman_pixel_interpolation). - Compilation: all aligned and registered modalities are merged into a single MuData (
.h5mu) file.
Spatial annotation transfer is an optional stage that runs after alignment. It assigns region labels from GeoJSON polygons to the reference observations and runs independently of registration and compilation.
| Requirement | Notes |
|---|---|
| Conda (Miniconda or Anaconda) | Required for environment management |
| Python 3.11 | Managed automatically by the install script |
| NVIDIA GPU + CUDA | Optional. Used by the feature_extraction registration type (the Prov-GigaPath model via HuggingFace). Without CUDA it falls back to CPU, which is much slower on large sections. All other pipeline stages run on CPU. |
| HuggingFace token | Required whenever any modality uses feature_extraction registration. Configuration validation rejects the config without it, whether or not the model is already cached locally. |
FOCUS runs on Windows 10/11, macOS, and Linux (both desktop and headless servers).
Clone the repository and run the install script for your platform. The script will:
- Check that conda is available and guide you to install it if not.
- Create a
FOCUSconda environment with all dependencies. - Register the
focuscommand so you can run the software from any directory after activating the environment. - Create auxiliary
FOCUS_ASHLARandFOCUS_BaSiCpyenvironments (one per subfolder intools/) for Raman spectroscopy processing. These are built by default. No extra flag is needed.
git clone https://github.com/sifrimlab/FOCUS.git
cd FOCUSbash install.shTo wipe and recreate all environments (e.g. after a dependency update):
bash install.sh --reinstallOpen an Anaconda Prompt (PowerShell) and run the PowerShell installer:
.\install.ps1Or with the reinstall flag:
.\install.ps1 -Reinstallinstall.ps1 mirrors install.sh: it detects your CUDA version and installs the matching PyTorch wheel. A install.bat shim that forwards to it is also provided, so from a classic Command Prompt install.bat / install.bat --reinstall work too.
Tip: If
condais not found, install Miniconda for Windows, then open a new Anaconda Prompt and retry.
The install script automatically detects your system's CUDA version (via nvcc, nvidia-smi, or environment module variables) and installs a matching PyTorch build. On most systems this works without any intervention. On HPC clusters, however, two classes of problems can occur.
If the install script prints:
[WARN] HPC environment detected (SLURM) but no CUDA toolkit found.
it means none of the CUDA detection methods found a version. Load the CUDA module before running the script:
module load cuda # use whatever CUDA module your cluster provides
bash install.sh --reinstallSome PyTorch versions crash with a Bus error (core dumped) at import time on certain HPC systems. This happens inside PyTorch's CUDA library preloading (_preload_cuda_lib), before any user code runs. Symptoms:
Fatal Python error: Bus error
File ".../torch/__init__.py", line 324 in _preload_cuda_lib
The install script verifies that PyTorch imports correctly after installation. If the check fails, you will see:
[ERROR] torch X.Y.Z installed but crashes on import (likely SIGBUS from CUDA preloading).
[ERROR] Specify a known-working version and re-run:
[ERROR] TORCH_VERSION=<version> bash install.sh --reinstall
To fix this, find a PyTorch version that works on your cluster and pass it via the TORCH_VERSION environment variable:
# Step 1: Find a working version.
# Create a temporary environment and test different versions:
conda create -y -n torch_test python=3.11
conda run -n torch_test pip install torch==2.9.0 --index-url https://download.pytorch.org/whl/cu128
conda run -n torch_test python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
# Expected output: 2.9.0+cu128 True
conda env remove -y -n torch_test
# Step 2: Reinstall FOCUS with the working version.
TORCH_VERSION=2.9.0 bash install.sh --reinstallNote: Replace
cu128with the CUDA wheel index that matches your system (e.g.cu126,cu124,cu121,cu118). The install script prints which index it selects during installation.
Recent PyTorch versions (e.g. 2.11+) unconditionally call _preload_cuda_lib at import time, which uses ctypes.CDLL to load CUDA shared libraries installed as separate pip packages (nvidia-cuda-runtime-*, cuda-bindings, etc.). On some HPC configurations, the initialization code inside these shared libraries fails when attempting to interface with the CUDA kernel driver, resulting in a SIGBUS signal that terminates the process. Older PyTorch versions (e.g. 2.9.0) use a different loading strategy that does not trigger this behavior.
FOCUS expects your data to follow a simple two-level directory hierarchy:
dataset_path/
├── sample_001/
│ ├── <modality_name>/ ← raw input files for this modality
│ └── <modality_name>/ ← raw input files for a second modality
├── sample_002/
│ ├── <modality_name>/
│ └── <modality_name>/
└── ...
dataset_pathis the root directory you provide in your config.- Each first-level subdirectory is treated as a sample. Directory names become the sample identifiers.
- Each second-level subdirectory must match the modality
"name"defined in your config (case-sensitive). - Input file names within a modality folder are not significant; FOCUS selects files by extension.
FOCUS automatically creates the following output directories inside dataset_path as the pipeline progresses:
dataset_path/
├── sample_001/
│ ├── preprocessing/<modality>/ ← preprocessed files
│ ├── alignment/ ← aligned files
│ ├── registration/ ← registered files
│ └── annotations/ ← annotated reference files (optional stage)
├── ...
└── merged/
├── preprocessing/
├── alignment/
├── registration/
├── annotations/
└── multimodal_dataset.h5mu ← final output
Only preprocessing/ is split into one subdirectory per modality. In the other stages the
modality name is part of the file name instead. The annotations/ directories are created only
when spatial annotation transfer is configured.
Activate the FOCUS conda environment first:
conda activate FOCUSRunning focus without arguments starts a local web server and opens the interactive interface:
focusFOCUS GUI started. Open http://localhost:5050 in your browser.
The GUI guides you through four stages:
- Setup: enter your
dataset_pathor load an existing configuration file. - Configuration: define your modalities, processing settings, and registration options. The configuration is auto-saved as
focus_config.jsoninside yourdataset_path. - Running: monitor pipeline progress. When the alignment stage is reached, a button appears to open the interactive alignment tool (served separately at
http://localhost:8000). - Complete: review the list of generated output files.
If you already have a configuration file, you can run the full pipeline non-interactively:
focus --config /path/to/your/dataset/focus_config.jsonThe configuration file must be a valid JSON file. Its path can be anywhere on the filesystem as long as it is readable. Use the GUI as an interactive config builder, or see the configuration documentation for the expected structure.
FOCUS provides a Dockerfile, a Singularity definition file (focus.def), and launcher scripts for all three major container runtimes. FOCUS uses same-path mounting: the directory you choose to mount is mapped to the identical absolute path inside the container, so every path in your config file is valid without any translation.
Docker or Podman:
docker build -t focus .
# or
podman build -t focus .Singularity / Apptainer:
singularity build focus.sif focus.def
# or
apptainer build focus.sif focus.defBuilding from
focus.defdoes not require Docker; it bootstraps directly frompython:3.11-slim.
Both images bake in a CPU PyTorch build by default. For GPU images, pass the TORCH_INDEX build arg matching your CUDA (the wheel bundles CUDA, so no CUDA base image is needed):
docker build --build-arg TORCH_INDEX=https://download.pytorch.org/whl/cu128 -t focus:gpu .
apptainer build --build-arg TORCH_INDEX=https://download.pytorch.org/whl/cu128 focus.sif focus.defThe focus-container.sh --build --gpu shortcut selects the CUDA index automatically. See Container Deployment for details.
Use focus-container.sh. It auto-detects the first available runtime (Docker → Podman → Singularity → Apptainer) and mounts your data directory at the same path inside the container.
GUI mode (opens http://localhost:5050):
bash focus-container.sh --mount /path/to/your/data
focus-container.shpublishes port 5050 only. The interactive alignment tool is served on port 8000, so to reach the alignment stage from a container, run it directly with both ports mapped (see Container Deployment):docker run --rm -it -p 5050:5050 -p 8000:8000 -v /data/mylab:/data/mylab focus
CLI mode:
bash focus-container.sh --mount /path/to/your/data -- --config /path/to/your/data/project/focus_config.jsonNote the
--separator: everything after it is passed directly to thefocuscommand inside the container.
Select a specific runtime:
bash focus-container.sh --runtime podman --mount /data/mylab -- --config /data/mylab/project/focus_config.json
bash focus-container.sh --runtime singularity --mount /data/mylab -- --config /data/mylab/project/focus_config.jsonGPU support:
bash focus-container.sh --gpu --mount /data/mylab -- --config /data/mylab/project/focus_config.jsonThe image must contain a CUDA PyTorch build for the GPU to be used. Build it with
--build --gpu(see below), or via theTORCH_INDEXbuild arg.
Mount multiple directories:
bash focus-container.sh --mount /data/images --mount /data/omics -- --config /data/omics/project/focus_config.jsonBuild and run in one step:
# CPU image
bash focus-container.sh --build --mount /data/mylab
# GPU image (bakes in a CUDA PyTorch build, then runs with GPU access)
bash focus-container.sh --build --gpu --mount /data/mylab -- --config /data/mylab/project/focus_config.jsonUse focus-container.ps1 from a PowerShell prompt. Windows paths are automatically converted to the Unix-style paths that Docker uses internally (C:\path\to\data → /c/path/to/data), preserving the directory tree so no path translation is needed in your config file.
GUI mode:
.\focus-container.ps1 -Mount C:\data\mylabCLI mode:
.\focus-container.ps1 -Mount C:\data\mylab -- --config /c/data/mylab/project/focus_config.jsonOn Windows, use the converted Unix path (
/c/...) in your config file when running inside a container.
Select runtime:
.\focus-container.ps1 -Runtime podman -Mount C:\data\mylabGPU support:
.\focus-container.ps1 -Gpu -Mount C:\data\mylab -- --config /c/data/mylab/project/focus_config.jsonSingularity/Apptainer is not natively supported on Windows. Use WSL2 and run
focus-container.shfrom within the WSL2 terminal for Singularity support.
On HPC clusters, Singularity or Apptainer is typically the only available container runtime. Copy the pre-built focus.sif file to the cluster and use focus-container.sh directly, or invoke Singularity manually:
CLI mode (recommended on HPC):
singularity run --bind /scratch/mylab focus.sif --config /scratch/mylab/project/focus_config.json--bind /path maps the host path to the same path inside the container. All paths in your config remain unchanged.
GUI mode on a remote server:
The GUI requires access to port 5050. Set up an SSH tunnel from your local machine:
# On your local machine:
ssh -L 5050:localhost:5050 username@hpc-cluster.example.org
# Then on the cluster:
singularity run --bind /scratch/mylab focus.sif
# Open http://localhost:5050 in your local browserSubmitting a batch job (SLURM):
The repo ships a ready-to-use, parameterised batch script, slurm/submit_focus.sbatch (Singularity + GPU by default, with a commented host-install variant):
sbatch slurm/submit_focus.sbatch
# Override the config / image paths without editing the file:
sbatch --export=ALL,FOCUS_CONFIG=/scratch/$USER/proj/config.json,FOCUS_SIF=/scratch/$USER/focus.sif \
slurm/submit_focus.sbatchSee the HPC & Headless Servers guide for the full breakdown of the script's resource directives and overridable variables.
The
--nvflag (set in the script) passes NVIDIA GPU access to the Singularity container; drop it and#SBATCH --gres=gpu:1for CPU-only runs.
| Feature | Windows 10/11 | macOS | Linux (desktop) | Linux (headless / HPC) |
|---|---|---|---|---|
Host install (install.sh / install.ps1) |
✓ | ✓ | ✓ | ✓ |
| GUI mode | ✓ | ✓ | ✓ | via SSH tunnel |
| CLI mode | ✓ | ✓ | ✓ | ✓ |
| Docker / Podman container | ✓ | ✓ | ✓ | ✓ |
| Singularity / Apptainer container | via WSL2 | ✓ | ✓ | ✓ |
| GPU acceleration (feature extraction) | ✓ | - | ✓ | ✓ |
GPU acceleration via CUDA is not available on macOS (Apple Silicon uses MPS, which is not currently supported). All pipeline stages that do not use
feature_extractionregistration run fully on CPU and are supported on all platforms.
This framework has been developed in the context of the academic research conducted at the Laboratory of Multi-Omic Integrative Bioinformatics (LMIB) with the key contributions of:
Copyright © 2025-2026 KU Leuven.
FOCUS is distributed under the GNU General Public License version 3 or later, with additional terms under section 7 of that License. The full text is in LICENSE.