Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions examples/freesurfer/freesurfer-args.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"image": "/vip/Home/API/reconall-example/INPUTS/ds001600/sub-1/anat/sub-1_T1w.nii.gz",
"results-directory": "/vip/Home/API/reconall-example/OUTPUTS/",
"options": "-all",
"license key": "/vip/Home/API/reconall-example/INPUTS/LICENSE.txt"
}
"nifti": "/vip/Home/API/reconall-example/INPUTS/ds001600/sub-1/anat/sub-1_T1w.nii.gz",
"subjid": "sub-1_T1w",
"directives": "-all",
"license": "/vip/Home/API/reconall-example/INPUTS/LICENSE.txt"
}
2 changes: 1 addition & 1 deletion examples/freesurfer/freesurfer-recon-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
python3 ./vip-cli.py \
--session reconall-example \
--input $(realpath ./inputs) \
--pipeline "Freesurfer (recon-all)/0.3.8" \
--pipeline "Freesurfer-Recon-all/7.3.1" \
--arguments freesurfer-args.json \
--api-key VIP_API_TOKEN
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Description:
# Author: Frederic Cervenansky < frederic.cervenansky@creatis.insa-lyon.fr>
#
# Licence Cecill-B
# Copyright (C) Creatis 2017-2024


# import
import girder_client
import types
import sys
import click
from pathlib import Path

# api Rest url of the warehouse
#Windows users: paths use backslashes (\) while Linux/macOS use forward slashes (/)
url='https://insert/your-server-here/api/v1'
#example URLs would be:
# https://srmnopt.creatis.insa-lyon.fr/warehouse/api/v1 --> for NMR and Optics
# https://myriad.creatis.insa-lyon.fr/api/v1 --> for MYRIAD

# apiKey is a "mechanism" to share authentication and rights on folder.
# User should defined through the web interface (inside user information) an apiKey ith the corresponding privileges
apiKey = 'GIRDER_API_KEY'

# Generate the warehouse client
gc = girder_client.GirderClient(apiUrl=url)

# Authentication to the warehouse
gc.authenticate(apiKey=apiKey)

# Use the ID of the folder you chose put your input MRIs
folderId ='FOLDER_ID' # 3D Flow phantom+Bubbles
#an example of a folder ID: 698de7fe82d062f2aea9619d

# Local download directory
download_dir = Path('/insert/your/download/path')

# Download data from Girder
gc.downloadFolderRecursive(folderId, str(download_dir))

# Add derivatives/freesurfer folder
derivatives_fs = download_dir / 'derivatives' / 'freesurfer'
derivatives_fs.mkdir(parents=True, exist_ok=True) # will create if not exists, do nothing if exists

print(f"'derivatives/freesurfer' folder ensured at: {derivatives_fs}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "vip-client",
# ]

from vip_client import VipSession
from pathlib import Path
import os

# This directory will be uploaded to VIP — ensure no sensitive data
# (e.g., DICOMs, scripts containing API keys) is included.
input_dir = Path("/insert/your/input/path") # make sure license file is copied in this directory
output_dir = Path("/insert/your/output/path/derivatives/freesurfer")

# Save current working directory and change to /tmp (temporary workaround for VIP bug)
orig_cwd = os.getcwd()
os.chdir('/tmp')

# Get T1w NIfTIs only from sub-* folders, excluding run-02, run-03, ...
nifti_files = [
str(f)
for sub in input_dir.iterdir()
if sub.is_dir() and sub.name.startswith("sub-")
for f in sub.rglob("*.nii.gz")
if f.name.endswith("_T1w.nii.gz")
and ("_run-" not in f.name or "_run-01_" in f.name)
]

# Create subjid from filenames
subjid = [Path(f).name.replace(".nii.gz", "") for f in nifti_files]

input_settings = {
"nifti": nifti_files,
"license": str(input_dir / "license.txt"),
"subjid": subjid
}

session = VipSession.init(
api_key="VIP_API_KEY",
input_dir=str(input_dir),
output_dir= str(output_dir),
pipeline_id="FreeSurfer-Recon-all/7.3.1",
input_settings=input_settings
)

session.run_session()
session.display()

# Restore original working directory
os.chdir(orig_cwd)

# Download outputs to the output_dir
session.download_outputs(get_status=['Finished', 'Killed'])

# Optional cleanup of outputs on VIP
# session.finish()
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from pathlib import Path
import tarfile
import shutil

fs_dir = Path("/insert/your/input/path/derivatives/freesurfer")
tmp_dir = fs_dir / "tmp"

# create a tmp directory if it doesn't exist
tmp_dir.mkdir(exist_ok=True)

# Collect eligible tarballs (.tar.gz or .tgz)
tarballs = [
t for t in fs_dir.iterdir()
if t.is_file()
and t.suffixes in ([".tar", ".gz"], [".tgz"])
and "ses-" in t.name
and ".long." not in t.name
]

subjects = {}

for tar_path in tarballs:
subj = tar_path.name.split("_")[0] # sub-XXXX

# Remove full archive suffix safely
if tar_path.suffixes == [".tar", ".gz"]:
base_name = tar_path.name[:-7] # remove ".tar.gz"
else: # .tgz
base_name = tar_path.stem # removes ".tgz"

extract_dir = tmp_dir / "extracted" / base_name
extract_dir.mkdir(parents=True, exist_ok=True)

# Untar (auto-detect compression)
with tarfile.open(tar_path, "r:*") as tar:
tar.extractall(path=extract_dir)

subjects.setdefault(subj, []).append(extract_dir)

# Group per subject
for subj, tp_dirs in subjects.items():
out_tar = tmp_dir / f"{subj}_TPs.tgz"

with tarfile.open(out_tar, "w:gz") as tar:
for tp_dir in tp_dirs:
for item in tp_dir.iterdir():
tar.add(item, arcname=item.name)

# Cleanup extracted files
shutil.rmtree(tmp_dir / "extracted", ignore_errors=True)

print("✅ Done: grouped longitudinal timepoints per subject.")

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "vip-client",
# ]

from vip_client import VipSession
from pathlib import Path
import os

# This directory will be uploaded to VIP — ensure no sensitive data
# (e.g., DICOMs, scripts containing API keys) is included.
input_dir = Path("/insert/your/input/path/derivatives/freesurfer/tmp") #make sure license file is copied in this directory
output_dir = Path("/insert/your/output/path/derivatives/freesurfer")

# Save current working directory and change to /tmp (temporary workaround for VIP bug)
orig_cwd = os.getcwd()
os.chdir('/tmp')

# Collect tarballs and BASE_IDs
tp_tarballs = [f for f in input_dir.iterdir() if f.suffix in (".tgz", ".tar.gz") and "_TPs" in f.name]
tp_tarballs.sort() # optional

base_ids = [f.name.split("_")[0] for f in tp_tarballs]

# Build batch input settings
input_settings = {
"LICENSE_FILE": str(input_dir / "license.txt"),
"TP_TARBALL": [str(f) for f in tp_tarballs], # list of tarballs
"BASE_ID": base_ids, # list of base IDs
}

session = VipSession.init(
api_key="VIP_API_KEY",
input_dir=str(input_dir),
output_dir= str(output_dir),
pipeline_id="FreeSurfer-Recon-all-BASE/7.3.1",
input_settings=input_settings,
)

session.run_session() # VIP will process tarballs in parallel
session.display()

# Restore original working directory
os.chdir(orig_cwd)

# Download outputs to the output_dir
session.download_outputs(get_status=['Finished', 'Killed'])

# Optional cleanup of outputs on VIP
# session.finish()

# Delete tmp folder locally
# WARNING: This removes the entire directory.
# Make sure that the license file is stored in another safe location.
shutil.rmtree(input_dir)
print(f"Deleted temporary folder: {input_dir}")



Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "vip-client",
# ]
# ///

from vip_client import VipSession
from vip_client.utils import vip

# VIP API Key
API_KEY = "VIP_API_KEY"

# Initialize VIP connection
VipSession.init(api_key=API_KEY)

# VIP paths
BASE_DIR = "/vip/Home/API/VipSession-xxxxxx-xxxxxx-bxx/OUTPUTS/YYYY-MM-DD_HHMMSS/" # from the output of BASE pipeline
TP_DIR = "/vip/Home/API/VipSession-xxxxxx-xxxxxx-bxx/INPUTS/" # from the input of BASE pipeline
LICENSE_FILE = "/vip/Home/API/VipSession-xxxxxx-xxxxxx-bxx/INPUTS/license.txt" # from the input of BASE pipeline

# Local output directory
output_dir = Path("/home/zakaria/VIP/guillaume_data_test/derivatives/freesurfer")

# List BASE and TP files from VIP
base_files = [
item['path']
for item in vip.list_elements(BASE_DIR)
if not item['isDirectory'] and item['path'].split('/')[-1].startswith("sub-") and "_TPs" not in item['path']
]

tp_files = [
item['path']
for item in vip.list_elements(TP_DIR)
if not item['isDirectory'] and item['path'].split('/')[-1].startswith("sub-") and "_TPs" in item['path']
]

# Match BASE → TP tarball
base_dict = {f.split("/")[-1].split(".")[0]: f for f in base_files}
tp_dict = {f.split("/")[-1].split("_TPs")[0]: f for f in tp_files}
subjects = set(base_dict.keys()) & set(tp_dict.keys())

if not subjects:
raise RuntimeError("No matching BASE and TP_TARBALL found!")

print(f"Submitting {len(subjects)} LONG jobs for subjects: {', '.join(subjects)}")

# Gather VIP input files per subject
vip_input_files = []
for sub in subjects:
vip_input_files.append(base_dict[sub])
vip_input_files.append(tp_dict[sub])

# Input settings for the LONG pipeline
input_settings = {
"LICENSE_FILE": LICENSE_FILE,
"BASE_ID": [base_dict[sub] for sub in subjects],
"TP_TARBALL": [tp_dict[sub] for sub in subjects],
"directives": "-all",
}

# Create VIP session
session_name = "VIP_FS_LONG_parallel"
session = VipSession(session_name)

# Launch pipeline directly on VIP
session.launch_pipeline(
pipeline_id="FreeSurfer-Recon-all-LONG/7.3.1",
input_settings=input_settings,
output_dir= str(output_dir)
)

# Monitor progress
session.monitor_workflows()

# Download outputs to the output_dir
session.download_outputs(get_status=['Finished', 'Killed'])

# Optional cleanup of outputs on VIP
# session.finish()

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Description:
# Author: Frederic Cervenansky < frederic.cervenansky@creatis.insa-lyon.fr>
#
# Licence Cecill-B
# Copyright (C) Creatis 2017-2024


# import
import girder_client
import types
import sys
import click

# api Rest url of the warehouse
#Windows users: paths use backslashes (\) while Linux/macOS use forward slashes (/)
url='https://insert/your-server-here/api/v1'
# https://srmnopt.creatis.insa-lyon.fr/warehouse/api/v1 --> for NMR and Optics
# https://myriad.creatis.insa-lyon.fr/api/v1 --> for MYRIAD

# apiKey is a "mechanism" to share authentication and rights on folder.
# User should defined through the web interface (inside user information) an apiKey with the corresponding privileges
apiKey = 'GIRDER_API_KEY'

# Generate the warehouse client
gc = girder_client.GirderClient(apiUrl=url)

# Authentication to the warehouse
gc.authenticate(apiKey=apiKey)

# upload local freesurfer folder in the original input folder on Girder
folderId ='FOLDER_ID' # 3D Flow phantom+Bubbles
#an example of a folder ID: 698de7fe82d062f2aea9619d

# If 'derivatives' folder exists but 'freeSurfer' folder is absent:
# gc.upload('/insert/your/upload/path/derivatives/freesurfer', folderId)

# Default upload (if 'derivatives' folder is absent on the warehouse):
gc.upload('/insert/your/upload/path/derivatives/', folderId)

2 changes: 1 addition & 1 deletion examples/lcmodel/launch_lcmodel.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
"outputs": [],
"source": [
"# launch the execution and wait for it to finish\n",
"my_session.launch_pipeline(pipeline_id = \"LCModel/0.2-egi\", input_settings = input_settings)\n",
"my_session.launch_pipeline(pipeline_id = \"LCModel/0.6\", input_settings = input_settings)\n",
"my_session.monitor_workflows()"
]
},
Expand Down