[ENH]: Add support for FreeSurfer via Docker #342
9 changed files with 197 additions and 1 deletions
1
docs/changes/newsfragments/342.misc
Normal file
1
docs/changes/newsfragments/342.misc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add support for accessing FreeSurfer via Docker wrapper along with ``mri_binarize``, ``mri_pretess``, ``mri_mc`` and ``mris_convert`` by `Synchon Mandal`_
|
||||||
|
|
@ -163,3 +163,36 @@ Or, alternatively, you can execute this command which will update the
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
junifer setup ants-docker | grep "PATH=" | xargs | >> ~/.bashrc
|
junifer setup ants-docker | grep "PATH=" | xargs | >> ~/.bashrc
|
||||||
|
|
||||||
|
FreeSurfer
|
||||||
|
----------
|
||||||
|
|
||||||
|
To install FreeSurfer, you can always follow the `FreeSurfer official instructions
|
||||||
|
<https://surfer.nmr.mgh.harvard.edu/fswiki/DownloadAndInstall>`_. Additionally, you can
|
||||||
|
also follow the following steps to install and configure the FreeSurfer Docker container
|
||||||
|
in your local system.
|
||||||
|
|
||||||
|
1. Install Docker. You can follow the
|
||||||
|
`Docker official instructions <https://docs.docker.com/get-docker/>`_.
|
||||||
|
2. Pull the FreeSurfer Docker image from
|
||||||
|
`Docker Hub FreeSurfer <https://hub.docker.com/r/freesurfer/freesurfer>`_:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
docker pull freesurfer/freesurfer
|
||||||
|
|
||||||
|
3. Add the Junifer FreeSurfer scripts to your PATH environment variable. Run the
|
||||||
|
following command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
junifer setup freesurfer-docker
|
||||||
|
|
||||||
|
Take the last line and copy it to your ``.bashrc`` or ``.zshrc`` file.
|
||||||
|
|
||||||
|
Or, alternatively, you can execute this command which will update the
|
||||||
|
``~/.bashrc`` for you:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
junifer setup freesurfer-docker | grep "PATH=" | xargs | >> ~/.bashrc
|
||||||
|
|
|
||||||
|
|
@ -588,3 +588,29 @@ def ants_docker() -> None: # pragma: no cover
|
||||||
export PATH="$PATH:{ants_wrappers_path}"
|
export PATH="$PATH:{ants_wrappers_path}"
|
||||||
"""
|
"""
|
||||||
click.secho(msg, fg="blue")
|
click.secho(msg, fg="blue")
|
||||||
|
|
||||||
|
|
||||||
|
@setup.command("freesurfer-docker")
|
||||||
|
def freesurfer_docker() -> None: # pragma: no cover
|
||||||
|
"""Configure FreeSurfer-Docker wrappers."""
|
||||||
|
import junifer
|
||||||
|
|
||||||
|
pkg_path = Path(junifer.__path__[0]) # type: ignore
|
||||||
|
fs_wrappers_path = pkg_path / "api" / "res" / "freesurfer"
|
||||||
|
msg = f"""
|
||||||
|
Installation instructions for FreeSurfer-Docker wrappers:
|
||||||
|
|
||||||
|
1. Install Docker: https://docs.docker.com/get-docker/
|
||||||
|
|
||||||
|
2. Get the FreeSurfer-Docker image by running this on the command line:
|
||||||
|
|
||||||
|
docker pull freesurfer/freesurfer
|
||||||
|
|
||||||
|
3. Get license from: https://surfer.nmr.mgh.harvard.edu/registration.html .
|
||||||
|
You can skip this step if you already have one.
|
||||||
|
|
||||||
|
4. Add this line to the ~/.bashrc or ~/.zshrc file:
|
||||||
|
|
||||||
|
export PATH="$PATH:{fs_wrappers_path}"
|
||||||
|
"""
|
||||||
|
click.secho(msg, fg="blue")
|
||||||
|
|
|
||||||
3
junifer/api/res/freesurfer/mri_binarize
Normal file
3
junifer/api/res/freesurfer/mri_binarize
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
run_freesurfer_docker.sh mri_binarize "$@"
|
||||||
3
junifer/api/res/freesurfer/mri_mc
Normal file
3
junifer/api/res/freesurfer/mri_mc
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
run_freesurfer_docker.sh mri_mc "$@"
|
||||||
3
junifer/api/res/freesurfer/mri_pretess
Normal file
3
junifer/api/res/freesurfer/mri_pretess
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
run_freesurfer_docker.sh mri_pretess "$@"
|
||||||
3
junifer/api/res/freesurfer/mris_convert
Normal file
3
junifer/api/res/freesurfer/mris_convert
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
run_freesurfer_docker.sh mris_convert "$@"
|
||||||
61
junifer/api/res/freesurfer/run_freesurfer_docker.sh
Normal file
61
junifer/api/res/freesurfer/run_freesurfer_docker.sh
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
corrected_args=()
|
||||||
|
docker_args=()
|
||||||
|
mounts=0
|
||||||
|
|
||||||
|
FS_LICENSE="${FS_LICENSE:=$HOME/freesurfer_license.txt}"
|
||||||
|
lol :P lol :P
|
|||||||
|
|
||||||
|
if [ -f "${FS_LICENSE}" ]; then
|
||||||
|
>&2 echo "Using freesurfer license from ${FS_LICENSE}"
|
||||||
|
else
|
||||||
|
>&2 echo "Freesurfer license not found at ${FS_LICENSE}. You can either set FS_LICENSE to the path of the license file or place the license file at
|
||||||
|
${FS_LICENSE}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Map the license path to the container by binding
|
||||||
|
license_path=$(realpath "${FS_LICENSE}")
|
||||||
|
license_path_fname=$(basename "${FS_LICENSE}")
|
||||||
|
host_dir=$(dirname "${license_path}")
|
||||||
|
((mounts+=1))
|
||||||
|
container_dir="/data/mount_${mounts}"
|
||||||
|
docker_args+=("-v ${host_dir}:${container_dir}")
|
||||||
|
export DOCKERENV_FS_LICENSE=${container_dir}/${license_path_fname}
|
||||||
|
|
||||||
|
for var in "$@"
|
||||||
|
do
|
||||||
|
if [ -d "${var}" ]; then
|
||||||
|
echo "$var is a directory" >&2
|
||||||
|
var=$(realpath "${var}")
|
||||||
|
host_dir=$(dirname "${var}")
|
||||||
|
((mounts+=1))
|
||||||
|
container_dir="/data/mount_${mounts}"
|
||||||
|
docker_args+=("-v ${host_dir}:${container_dir}")
|
||||||
|
var=${container_dir}
|
||||||
|
elif [ -f "${var}" ] || [[ "${var}" == /* ]]; then
|
||||||
|
if [ -f "${var}" ]; then
|
||||||
|
echo "$var is a file" >&2
|
||||||
|
var=$(realpath "${var}")
|
||||||
|
else
|
||||||
|
echo "$var is a prefix" >&2
|
||||||
|
fi
|
||||||
|
fname=$(basename "${var}")
|
||||||
|
host_dir=$(dirname "${var}")
|
||||||
|
((mounts+=1))
|
||||||
|
container_dir="/data/mount_${mounts}"
|
||||||
|
docker_args+=("-v ${host_dir}:${container_dir}")
|
||||||
|
var=${container_dir}/${fname}
|
||||||
|
fi
|
||||||
|
corrected_args+=("${var}")
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Docker args: ${docker_args[*]}" >&2
|
||||||
|
echo "Corrected args for FreeSurfer: ${corrected_args[*]}" >&2
|
||||||
|
|
||||||
|
cwd=$(pwd)
|
||||||
|
cmd="docker run --rm ${docker_args[*]} -v ${cwd}:${cwd} -w ${cwd} freesurfer/freesurfer ${corrected_args[*]}"
|
||||||
|
echo "Running command: ${cmd}" >&2
|
||||||
|
${cmd}
|
||||||
|
|
||||||
|
unset DOCKERENV_FS_LICENSE
|
||||||
|
|
@ -37,7 +37,7 @@ def check_ext_dependencies(
|
||||||
If ``name`` is mandatory and is not found.
|
If ``name`` is mandatory and is not found.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
valid_ext_dependencies = ("afni", "fsl", "ants")
|
valid_ext_dependencies = ("afni", "fsl", "ants", "freesurfer")
|
||||||
if name not in valid_ext_dependencies:
|
if name not in valid_ext_dependencies:
|
||||||
raise_error(
|
raise_error(
|
||||||
"Invalid value for `name`, should be one of: "
|
"Invalid value for `name`, should be one of: "
|
||||||
|
|
@ -52,6 +52,9 @@ def check_ext_dependencies(
|
||||||
# Check for ants
|
# Check for ants
|
||||||
elif name == "ants":
|
elif name == "ants":
|
||||||
found = _check_ants(**kwargs)
|
found = _check_ants(**kwargs)
|
||||||
|
# Check for freesurfer
|
||||||
|
elif name == "freesurfer":
|
||||||
|
found = _check_freesurfer(**kwargs)
|
||||||
|
|
||||||
# Check if the dependency is mandatory in case it's not found
|
# Check if the dependency is mandatory in case it's not found
|
||||||
if not found and not optional:
|
if not found and not optional:
|
||||||
|
|
@ -245,3 +248,63 @@ def _check_ants(commands: Optional[List[str]] = None) -> bool:
|
||||||
f"{commands_found_results}"
|
f"{commands_found_results}"
|
||||||
)
|
)
|
||||||
return ants_found
|
return ants_found
|
||||||
|
|
||||||
|
|
||||||
|
def _check_freesurfer(commands: Optional[List[str]] = None) -> bool:
|
||||||
|
"""Check if FreeSurfer is present in the system.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
commands : list of str, optional
|
||||||
|
The commands to specifically check for from FreeSurfer. If None, only
|
||||||
|
the basic FreeSurfer help would be looked up, else, would also
|
||||||
|
check for specific commands (default None).
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool
|
||||||
|
Whether FreeSurfer is found or not.
|
||||||
|
|
||||||
|
"""
|
||||||
|
completed_process = subprocess.run(
|
||||||
|
"recon-all -help",
|
||||||
|
stdin=subprocess.DEVNULL,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
shell=True, # is unsafe but kept for resolution via PATH
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
fs_found = completed_process.returncode == 0
|
||||||
|
|
||||||
|
# Check for specific commands
|
||||||
|
if fs_found and commands is not None:
|
||||||
|
if not isinstance(commands, list):
|
||||||
|
commands = [commands]
|
||||||
|
# Store command found results
|
||||||
|
commands_found_results = {}
|
||||||
|
# Set all commands found flag to True
|
||||||
|
all_commands_found = True
|
||||||
|
# Check commands' existence
|
||||||
|
for command in commands:
|
||||||
|
command_process = subprocess.run(
|
||||||
|
[command],
|
||||||
|
stdin=subprocess.DEVNULL,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
shell=True, # is unsafe but kept for resolution via PATH
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
command_found = command_process.returncode == 0
|
||||||
|
commands_found_results[command] = (
|
||||||
|
"found" if command_found else "not found"
|
||||||
|
)
|
||||||
|
# Set flag to trigger warning
|
||||||
|
all_commands_found = all_commands_found and command_found
|
||||||
|
# One or more commands were missing
|
||||||
|
if not all_commands_found:
|
||||||
|
warn_with_log(
|
||||||
|
"FreeSurfer is installed but some of the required commands "
|
||||||
|
"were not found. These are the results: "
|
||||||
|
f"{commands_found_results}"
|
||||||
|
)
|
||||||
|
return fs_found
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue
I see what you did here :P