Skip to content

Commit 5ad6872

Browse files
committed
[+] add TS inference and remapping scripts
1 parent 01ebc5a commit 5ad6872

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

scripts/remap_labels.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import sys
2+
import nibabel as nib
3+
import numpy as np
4+
5+
if len(sys.argv) != 3:
6+
print("Usage: remap_labels.py <input_segmentation> <output_segmentation>")
7+
sys.exit(1)
8+
9+
input_file = sys.argv[1]
10+
output_file = sys.argv[2]
11+
12+
# Load the segmentation image
13+
img = nib.load(input_file)
14+
data = img.get_fdata().astype(np.int16)
15+
16+
# Define the ROI list (in the order used for segmentation)
17+
roi_list = [
18+
"spleen", "kidney_right", "kidney_left", "gallbladder", "liver", "stomach",
19+
"pancreas", "esophagus", "small_bowel", "duodenum", "urinary_bladder", "prostate",
20+
"spinal_cord"
21+
]
22+
23+
# Define desired mapping: keys are ROI names, values are target label integers
24+
desired_mapping = {
25+
"spleen": 1,
26+
"kidney_right": 2,
27+
"kidney_left": 3,
28+
"gallbladder": 4,
29+
"liver": 5,
30+
"stomach": 6,
31+
"pancreas": 7,
32+
"esophagus": 8,
33+
"small_bowel": 9, # corresponds to "Small-Intestine"
34+
"duodenum": 10,
35+
"urinary_bladder": 11, # corresponds to "Bladder"
36+
"prostate": 12,
37+
"spinal_cord": 13 # corresponds to "Spinal-Canal"
38+
}
39+
40+
# Get the unique labels (ignoring background 0)
41+
unique_vals = np.unique(data)
42+
unique_vals = unique_vals[unique_vals != 0]
43+
unique_vals = np.sort(unique_vals)
44+
45+
if len(unique_vals) != len(roi_list):
46+
print("Warning: Expected {} ROI labels, but found {} in the segmentation.".format(len(roi_list), len(unique_vals)))
47+
48+
# Build a remapping dictionary based on the assumed order.
49+
remap_dict = {}
50+
for i, orig_label in enumerate(unique_vals):
51+
if i < len(roi_list):
52+
roi = roi_list[i]
53+
remap_dict[orig_label] = desired_mapping[roi]
54+
else:
55+
remap_dict[orig_label] = 0 # fallback to background if extra
56+
57+
# Create a new data array with remapped labels
58+
new_data = np.zeros_like(data, dtype=np.int16)
59+
for orig, new in remap_dict.items():
60+
new_data[data == orig] = new
61+
62+
# Save the new segmentation with the same header and affine
63+
new_img = nib.Nifti1Image(new_data, img.affine, img.header)
64+
nib.save(new_img, output_file)

scripts/run_TotalSegmentator.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/bash
2+
# segment_folder.sh
3+
#
4+
# This script loops over all nii.gz files in an input folder,
5+
# runs TotalSegmentator with a restricted ROI subset, and then
6+
# remaps the resulting multi-label segmentation to a custom label mapping.
7+
#
8+
# The desired final mapping is:
9+
# background: 0
10+
# Spleen: 1
11+
# Kidney-Right: 2
12+
# Kidney-Left: 3
13+
# Gall-Bladder: 4
14+
# Liver: 5
15+
# Stomach: 6
16+
# Pancreas: 7
17+
# Esophagus: 8
18+
# Small-Intestine: 9
19+
# Duodenum: 10
20+
# Bladder: 11
21+
# Prostate: 12
22+
# Spinal-Canal: 13
23+
#
24+
# Note:
25+
# The names used with --roi_subset must match the TotalSegmentator classes.
26+
# Here we use:
27+
# spleen kidney_right kidney_left gallbladder liver stomach pancreas esophagus small_bowel duodenum urinary_bladder prostate spinal_cord
28+
# which correspond to the desired labels (with small naming differences).
29+
#
30+
# Usage:
31+
# ./segment_folder.sh /path/to/input_folder /path/to/output_folder
32+
33+
# Check for two arguments
34+
if [ "$#" -ne 2 ]; then
35+
echo "Usage: $0 <input_folder> <output_folder>"
36+
exit 1
37+
fi
38+
39+
INPUT_FOLDER="$1"
40+
OUTPUT_FOLDER="$2"
41+
42+
# Create output folder if it doesn't exist
43+
mkdir -p "$OUTPUT_FOLDER"
44+
45+
# Create a temporary directory for TotalSegmentator outputs
46+
TMP_DIR=$(mktemp -d)
47+
48+
# Function to process a single file
49+
process_file() {
50+
local file="$1"
51+
local filename=$(basename "$file")
52+
local base="${filename%.nii.gz}"
53+
echo "Processing $filename..."
54+
55+
# Create a temporary folder for this file's segmentation
56+
local FILE_TMP_DIR="$TMP_DIR/$base"
57+
mkdir -p "$FILE_TMP_DIR"
58+
59+
# Run TotalSegmentator using ROI subset and multi-label output
60+
if ! TotalSegmentator -i "$file" -o "$FILE_TMP_DIR" \
61+
--roi_subset spleen kidney_right kidney_left gallbladder liver stomach pancreas esophagus small_bowel duodenum urinary_bladder prostate spinal_cord \
62+
--ml; then
63+
echo "TotalSegmentator failed for $file; skipping."
64+
return
65+
fi
66+
67+
# Log the contents of the temporary directory
68+
echo "Contents of $FILE_TMP_DIR:"
69+
ls -l "$FILE_TMP_DIR"
70+
71+
# Find the segmentation file produced by TotalSegmentator.
72+
local SEG_FILE=$(find "$TMP_DIR" -maxdepth 1 -name "$base.nii" | head -n 1)
73+
if [ -z "$SEG_FILE" ]; then
74+
echo "No segmentation file found for $file; skipping."
75+
return
76+
fi
77+
78+
# Run the Python script to remap labels
79+
if ! python3 scripts/remap_labels.py "$SEG_FILE" "$OUTPUT_FOLDER/${base}.nii.gz"; then
80+
echo "Label remapping failed for $file; skipping."
81+
return
82+
fi
83+
84+
echo "Saved remapped segmentation as ${base}.nii.gz in $OUTPUT_FOLDER"
85+
}
86+
87+
export -f process_file
88+
export TMP_DIR
89+
export OUTPUT_FOLDER
90+
91+
# Loop over all nii.gz files in the input folder and process them in parallel
92+
find "$INPUT_FOLDER" -name "*.nii.gz" -print0 | xargs -0 -n 1 -P 4 bash -c 'process_file "$@"' _
93+
94+
# Clean up temporary files
95+
rm -rf "$TMP_DIR"
96+
echo "Processing complete."

0 commit comments

Comments
 (0)