diff --git a/.devcontainer/bash_history.sh b/.devcontainer/bash_history.sh new file mode 100644 index 0000000..c672b0b --- /dev/null +++ b/.devcontainer/bash_history.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" +echo "$SNIPPET" >> "${HOME:-/root}/.bashrc" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..7125bf4 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,41 @@ +{ + "name": "Beyond FA devcontainer - SCIL team", + "build": { + "dockerfile": "../Dockerfile", + "args": { + "BASE_IMAGE": "scilus/scilus:latest" + } + }, + "onCreateCommand": "bash .devcontainer/bash_history.sh", + "postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", + "features": { + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "dockerDashComposeVersion": "none", + "installDockerComposeSwitch": false + }, + "ghcr.io/robsyme/features/nextflow:1": {} + }, + "remoteUser": "root", + "mounts": [ + { + "target": "/tmp", + "type": "volume" + }, + { + "source": "nf-neuro-bash-history", + "target": "/commandhistory", + "type": "volume" + } + ], + "customizations": { + "vscode": { + "extensions": [ + "KorbinianEckstein.niivue", + "nextflow.nextflow" + ] + } + }, + "init": true, + "privileged": true +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c01e11 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +work/ +.nextflow.log* +.nextflow/ +results/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..83bcc5c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +ARG BASE_IMAGE=scilus/scilus:latest +FROM $BASE_IMAGE + +RUN apt-get update && apt-get -y install \ + git \ + openjdk-17-jre \ + && rm -rf /var/lib/apt/lists/* diff --git a/modules/local/kmeans/main.nf b/modules/local/kmeans/main.nf new file mode 100644 index 0000000..5d3ce32 --- /dev/null +++ b/modules/local/kmeans/main.nf @@ -0,0 +1,59 @@ + +process KMEANS { + +container "scilus/scilus:latest" + +input: + tuple val(meta), path(image) +output: + tuple val(meta), path("labels/*.nii.gz"), emit: labels + tuple val(meta), path("*_centroids.npy"), emit: centroids + +script: + def prefix = task.ext.prefix ?: "$meta.id" + // K-means parameters + def nb_regions = task.ext.nb_regions + def algorithm = task.ext.algorithm ?: "lloyd" + def init_method = task.ext.init_method ?: "k-means++" + def nb_init = task.ext.nb_init ?: "auto" + def max_iter = task.ext.max_iter ?: 300 + def norm_tolerance = task.ext.norm_tolerance ?: 1e-4 + def seed = task.ext.seed ?: 1234 + // Extract labels parameters + def background_label = task.ext.background_label ? "--background $task.ext.background_label" : "" + """ + python3 - << ENDSCRIPT + + import nibabel as nib + import numpy as np + from sklearn.cluster import KMeans + + bin_image = nib.load("$image") + positions = np.nonzero(bin_image.get_fdata().astype(bool)) + positions = np.asarray(positions).reshape((3, -1)).T + + kmeans = KMeans( + n_clusters=$nb_regions, + init="$init_method", + n_init=${nb_init instanceof java.lang.String ? "\"" + nb_init + "\"" : nb_init}, + max_iter=$max_iter, + tol=$norm_tolerance, + random_state=$seed, + algorithm="$algorithm" + ).fit(positions) + + output = np.zeros_like(bin_image.get_fdata(), dtype=np.int16) + output[positions.T[0], positions.T[1], positions.T[2]] = kmeans.labels_ + 1 + + nib.save(nib.Nifti1Image(output, bin_image.affine), "binary_as_labels.nii.gz") + np.save(f"${prefix}_centroids.npy", kmeans.cluster_centers_) + + ENDSCRIPT + + scil_labels_split_volume_by_ids.py binary_as_labels.nii.gz \ + $background_label \ + --out_dir labels \ + --out_prefix $prefix + """ + +}