This page will assist you in submitting a Singularity container for the BraTS-Reg challenge.
- 1. Build your model
- 2. Set up singularity
- 3. Create a Singularity Definition File
- 4. Build a singularity container
- 5. Locally test your singularity container
- 6. Upload your singularity container
This section will describe how to create your model and write the execution script for generating required outputs.
-
All input files will be mounted in a directory called
/input
in the working directory of the container. -
Arrange the
input
folder as follows:input/ ├── BraTSReg_001 │ ├── BraTSReg_001_00_0000_flair.nii.gz │ ├── BraTSReg_001_00_0000_t1ce.nii.gz │ ├── BraTSReg_001_00_0000_t1.nii.gz │ ├── BraTSReg_001_00_0000_t2.nii.gz │ ├── BraTSReg_001_01_0106_flair.nii.gz │ ├── BraTSReg_001_01_0106_landmarks.csv │ ├── BraTSReg_001_01_0106_t1ce.nii.gz │ ├── BraTSReg_001_01_0106_t1.nii.gz │ └── BraTSReg_001_01_0106_t2.nii.gz └── BraTSReg_002 ├── BraTSReg_002_00_0000_flair.nii.gz ├── ...
i.e. the
input
folder should contain the individual folders for the subjects named asBraTSReg_xxx
, wherexxx
is a three-digit ID (eg., 150, 151, etc.). Each subject folder will contain: t1, t1ce, flair, t2 scans for the baseline, follow-up and the follow-up landmarks.
Your execution script should follow the following requirements:
-
All output files of the script should be written into a directory called
/output
in the working directory of the container. -
The script contains two functions
generate_output
andapply_deformation
, one of which must be provided as a positional argument to the script.$ python my_script.py -h usage: my_script.py [-h] {generate_output,apply_deformation} ... Argument parser for BraTS_Reg challenge positional arguments: {generate_output,apply_deformation} optional arguments: -h, --help show this help message and exit
-
generate_output
: Generates landmarks, determinant of Jacobian, forward and backward deformation fields (upon setting the--def
flag), and followup_registered_to_baseline images (upon setting the--reg
flag) for challenge submission.$ python my_script.py generate_output -h usage: my_script.py generate_output [-h] [-i INPUT] [-o OUTPUT] [-d] [-r] arguments: -h, --help show this help message and exit -i INPUT, --input INPUT Provide full path to directory that contains input data -o OUTPUT, --output OUTPUT Provide full path to directory where output will be written -d, --def Output forward and backward deformation fields -r, --reg Output followup scans registered to baseline
-
Your models will be writing your output files with the following names:
- warped landmark -
BraTSReg_xxx.csv
- determinant of jacobian of deformation field -
BraTSReg_xxx_detj.nii.gz
- forward and backward deformation fields -
BraTSReg_xxx_df_f2b.nii.gz
(followup to baseline) andBraTSReg_xxx_df_b2f.nii.gz
(baseline to followup). Note: You can simply compute the forward field and take its inverse to compute the backward field. You do not need to perform any additional registrations (by switching the inputs) to compute the backward field. - followup sequences registered to baseline sequences -
BraTSReg_xxx_{i}_f2b.nii.gz
wherei
ist1ce
/t1
/t2
/flair
where
BraTSReg_xxx
is the subject id.BraTSReg_xxx_df_f2b.nii.gz
: field that warps the follow-up landmarks to baseline space / warps the baseline image to the follow-up imageBraTSReg_xxx_df_b2f.nii.gz
: field for whichBraTSReg_xxx_detj.nii.gz
is calculated / warps the follow-up image to the baseline image- registered scans (produced on
--reg
flag): follow-up scans registered to baseline (for which theBraTSReg_xxx_df_b2f.nii.gz
deformation field is applied)
- warped landmark -
-
So, for subject
BraTSReg_001
ininput/
, youroutput/
folder should be:
output/ ├── BraTSReg_001.csv ├── BraTSReg_001_detj.nii.gz ├── BraTSReg_001_df_f2b.nii.gz ├── BraTSReg_001_df_b2f.nii.gz ├── BraTSReg_001_t1ce_f2b.nii.gz ├── BraTSReg_001_t1_f2b.nii.gz ├── BraTSReg_001_t2_f2b.nii.gz ├── BraTSReg_001_flair_f2b.nii.gz ├── ...
-
-
apply_deformation
: This function will apply a given deformation field (as generated by your model) on any given input image or segmentation mask and save/return the output image.python my_script.py apply_deformation -h usage: my_script.py apply_deformation [-h] -f FIELD -i IMAGE -t INTERPOLATION [-p PATH_TO_OUTPUT_NIFTI] arguments: -h, --help show this help message and exit -f FIELD, --field FIELD Provide full path to deformation field -i IMAGE, --image IMAGE Provide full path to image on which field will be applied -t INTERPOLATION, --interpolation INTERPOLATION Should be nearest_neighbour (for segmentation mask type images) or trilinear etc. (for normal scans). To be handled inside apply_deformation() function -p PATH_TO_OUTPUT_NIFTI, --path_to_output_nifti PATH_TO_OUTPUT_NIFTI Format: /path/to/output_image_after_applying_deformation_field.nii.gz
- Note: The
apply_deformation
function should have the flexibility of applying both nearest neighbour and trilinear (or similar) interpolations for segmentation mask type images or normal images, respectively. Based on how you handle it in the script, please add the instructions to https://github.com/satrajitgithub/BraTS_Reg_submission_instructions/blob/master/my_script.py#L113 (i.e., inside thehelp
arg ofcommand2_parser
forinterpolation
) to let the user know how to specify it.
- Note: The
Note: A template execution script (my_script.py
) with all the required argument parsers is provided with this repository for your convenience.
- GPU: 1 NVIDIA Tesla V100 16GB memory
- Processor: 1 Intel 12-Core Xeon Gold 2.6GHz – 125W
- Singularity version - 3.9.0
For setting up singularity, check out: https://docs.sylabs.io/guides/3.0/user-guide/installation.html . Note that singularity runs on Linux natively and can be run on Windows and Mac through virtual machines (VMs).
This section will describe how to write your Definition file. A Singularity Definition File (or “def file” for short) is like a set of blueprints explaining how to build a custom container. It includes specifics about the base OS to build or the base container to start from, software to install, environment variables to set at runtime, files to add from the host system, and container metadata. As such, the singularity container is a self-contained execution environment that will allow the Challenge organizers to run and reproduce your results.
Here is an example Definition File file using the my_script.py
created above:
# Start from this Docker image
Bootstrap: docker
From: nvidia/cuda:10.0-cudnn7-devel-ubuntu16.04
%post
# Install general dependencies
apt-get update && apt-get install -y --no-install-recommends build-essential cmake git unzip zip gcc pylint curl wget tar \
openjdk-8-jdk zlib1g-dev bash-completion libcurl4-openssl-dev libreadline-gplv2-dev libncursesw5-dev \
libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libhdf5-serial-dev libhdf5-dev liblzma-dev libffi-dev nano
apt-get clean
# Install Python 3.7.4
cd /usr/src
wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tar.xz
tar xJf Python-3.7.4.tar.xz
cd Python-3.7.4
./configure --enable-shared LDFLAGS="-Wl,-rpath /usr/local/lib"
make
make install
# Install python packages
pip3 install --no-cache-dir --upgrade pip
pip3 install --no-cache-dir --upgrade numpy nibabel
pip3 install --no-cache-dir --upgrade torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cpu
# Set up environment
echo "export PATH=/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" >> /environment
echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/cuda/lib64:/usr/local/cuda/lib64/stubs:/usr/local/cuda/extras/CUPTI/lib64" >> /environment
cd /usr/local/cuda/lib64/stubs
ln -s libcuda.so libcuda.so.1
# Clean up
rm -rf /usr/src/*
# Copy your files into Singularity container (model+script)
%files
# place your python script inside the container
my_script.py /usr/local/bin/my_script.py
# place your model file inside the container
my_model.hdf5 /usr/local/bin/my_model.hdf5
# ***Please dont change the following lines
%runscript
echo "Arguments received: $*"
echo 'Executing: python3 /usr/local/bin/my_script.py '"$@"
exec python3 /usr/local/bin/my_script.py "$@"
The rest of this documentation will go through each line in the Definition File example and explain its purpose.
The Bootstrap
keyword is required for every type of build. It determines the bootstrap agent that will be used to create the base operating system you want to use. For example, the docker
bootstrap agent will pull docker layers from Docker Hub as a base OS to start your image.
The From
keyword specifies which docker image will be used as the base image for the singularity container. To read more, check out the following link from singularity's source documentation: https://docs.sylabs.io/guides/3.7/user-guide/definition_files.html#header
The %files
section allows you to copy files into the container. Its general form is:
%files [from <stage>]
<source> [<destination>]
...
Each line is a <source>
and <destination>
pair. The <source>
is either:
- A valid path on your host system
- A valid path in a previous stage of the build
while the <destination>
is always a path into the current container. If the <destination>
path is omitted it will be assumed to be the same as <source>
. To read more, check out the following link from singularity's source documentation: https://docs.sylabs.io/guides/3.7/user-guide/definition_files.html#files
This section is where you can download files from the internet with tools like git
and wget
, install new software and libraries, write configuration files, create new directories, etc.
In our example definition file above, we use this section to:
- Install general dependencies using
apt-get
- Install python and python packages
- Configure environment variables
To read more, check out the following link from singularity's source documentation: https://docs.sylabs.io/guides/3.7/user-guide/definition_files.html#post
The %runscript
section specifies what gets executed when your singularity container is run. In
this example, we want to run my_script.py
that we copied into /usr/local/bin/
. By using "$@"
in our %runscript
, all the arguments that are passed to the singularity run
command, are passed to my_script.py
.
Local | Singularity |
---|---|
python my_script.py generate_output -h |
singularity run <your_singularity>.sif generate_output -h |
python my_script.py apply_deformation -h |
singularity run <your_singularity>.sif apply_deformation -h |
Note: Make sure not to change the %runscript
from what is provided in the above template.
To read more about %runscript
, check out the following link from singularity's source documentation: https://docs.sylabs.io/guides/3.7/user-guide/definition_files.html#runscript
This section describes how to create your singularity container. You will need:
- your BraTS_Reg team name
- Definition file for the container
- Move your Definition file and all files you are copying into your Singularity container (e.g., your script, model) into the same directory.
- Make the above directory your current working directory.
The basic syntax for building a singularity container from a definition file is:
sudo singularity build brats_reg_<team_name>.sif <path to definition file>
where:
<team_name>
: Your BraTS_Reg team name (in lower case, also please replace any special character with underscore_
)<path to definition file>
: Specify the path to your definition file. This should be./<your_def_file.def>
since the definition file should be in your current working directory.
This section describes how to test and run your singularity container locally to test your model. After you build your singularity container, you can run your container locally to check that your model will correctly run as a singularity container.
singularity run -B "/your/input/folder/":"/input" -B "/your/output/folder/":"/output" brats_reg_<team_name>.sif {generate_output,apply_deformation } <args>
First, ensure that you have access to a NVIDIA GPU. For example, if you are running it on a High Performance Cluster (HPC), make sure you are logged into a GPU node. Then run:
singularity run --nv -B "/your/input/folder/":"/input" -B "/your/output/folder/":"/output" brats_reg_<team_name>.sif {generate_output,apply_deformation } <args>
where the --nv
flag will setup the container’s environment to use an NVIDIA GPU and the basic CUDA libraries to run a CUDA enabled application. For further details on requirements for the --nv
flag and examples, please check out the source documentation: https://docs.sylabs.io/guides/3.5/user-guide/gpu.html
If you have a job submission script on HPC, it should look something like (following example is for a SLURM scheduler):
#!/bin/bash
#SBATCH --mail-type=BEGIN,END
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=8
#SBATCH --gres=gpu:1
#SBATCH --mem=8gb
#SBATCH --time=2:00:00
#SBATCH --error=joblogs/%j.err
#SBATCH --output=joblogs/%j.out
module load singularity
singularity run --nv -B "/your/input/folder":"/input" -B "/your/output/folder":"/output" brats_reg_<team_name>.sif {generate_output,apply_deformation } <args>
Note: Make sure you test out both the generate_output
and apply_deformation
functions with appropriate arguements before moving on to the next stage.
This section describes how to push your locally built singularity container up into the "Sylabs Cloud Library".
- Make an account on sylabs. Keep a note of your username (
your_sylab_username
), you will need this in step 3 below to push the container. - You will need an access token to login into your account and push the container. To create an access token, follow these steps. Once you have the access token, run
singularity remote login
in your terminal and paste the access token at the prompt:
vagrant@vagrant:~$ singularity remote login
Generate an access token at https://cloud.sylabs.io/auth/tokens, and paste it here.
Token entered will be hidden for security.
Access Token:
INFO: Access Token Verified!
INFO: Token stored in /home/vagrant/.singularity/remote.yaml
- Once your access token is verified, you can push the container image as follows:
singularity push -U brats_reg_<team_name>.sif library://<your_sylab_username>/brats_reg/brats_reg_<team_name>[:<tag>]
where,
brats_reg_<team_name>.sif
: the locally built singularity container image
<your_sylab_username>
: your username created in step 1
<tag>
: Optional. If no tag is specified, a :latest
tag is added to your image. In general, taggin the file as latest is fine. However, let’s assume you have a newer version of your container (v0.1), and you want to push that container without deleting your :latest
container, then you can add a version tag to that container, like so:
singularity push -U brats_reg_<team_name>.sif library://<your_sylab_username>/brats_reg_/brats_reg_<team_name>:0.1
When your container is being pushed you should see a progress-bar like this:
vagrant@vagrant:~$ singularity push -U brats_reg_<team_name>.sif library://<your_sylab_username>/brats_reg_/brats_reg_<team_name>
WARNING: Skipping container verification
1.2MiB / 1.1GiB [========----------------------------------------------------------------] 1 % 372.8 KiB/s 51m41s
When it's over, you should see that its 100% complete:
vagrant@vagrant:~$ singularity push -U brats_reg_<team_name>.sif library://<your_sylab_username>/brats_reg_/brats_reg_<team_name>
WARNING: Skipping container verification
1.1GiB / 1.1GiB [==============================================================================] 100 % 1.1 MiB/s 0s
If the singularity container was successfully pushed, it should show up under: https://cloud.sylabs.io/library/<your_sylab_username>
.
Make sure your container is set to Public on the sylab website. If it is Private, the organizers will not be able to access it.