-
Notifications
You must be signed in to change notification settings - Fork 0
Dev/use tissue2MR - generating Permittivity and Conductivity for whole-spine dataset #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,36 +6,13 @@ | |
| import json | ||
| import datetime | ||
| import git | ||
|
|
||
| from utils.tissue2MR import run_tissue2MR | ||
|
|
||
| def label_to_chi(bids_subject_dir): | ||
|
|
||
| bids_subject_dir = Path(bids_subject_dir) | ||
| subject = str(bids_subject_dir.stem) | ||
| merged_labels_path = bids_subject_dir / ".." / 'derivatives' / 'labels' / subject / 'anat' / (subject + '_T1w_label-all.nii.gz') | ||
|
|
||
|
|
||
| vol = nib.load(merged_labels_path.resolve()) | ||
|
|
||
| vol_data = vol.get_fdata() | ||
|
|
||
| vol_data.astype(np.float64) | ||
|
|
||
| vol_data[vol_data==0] = 0.35 | ||
| vol_data[vol_data==1] = -9.05 | ||
| vol_data[vol_data==2] = -2 | ||
| vol_data[vol_data==3] = -2 | ||
| vol_data[vol_data==4] = -4.2 | ||
| vol_data[vol_data==5] = -4.2 | ||
| vol_data[vol_data==6] = -4.2 | ||
| vol_data[vol_data==56] = -9.04 | ||
| vol_data[vol_data==60] = -9.05 | ||
| vol_data[vol_data==91] = -11 | ||
| vol_data[vol_data==92] = -11 | ||
| vol_data[vol_data==93] = -9.055 | ||
| vol_data[vol_data==100] = -9.055 | ||
|
|
||
| new_volume = nib.Nifti1Image(vol_data, vol.affine, vol.header, dtype=np.float64) | ||
| merged_labels_path = bids_subject_dir / ".." / 'derivatives' /'labels' / subject / 'anat' / (subject + '_T1w_label-all.nii.gz') | ||
|
|
||
| # Check if the directory bids_subject_dir / ".." / 'derivatives' / subject / exists and if no, create it | ||
|
|
||
|
|
@@ -47,9 +24,16 @@ def label_to_chi(bids_subject_dir): | |
| if not os.path.exists(bids_subject_dir / ".." / 'derivatives' / subject / 'anat' ): | ||
| os.makedirs(bids_subject_dir / ".." / 'derivatives' / subject / 'anat' ) | ||
|
|
||
| chi_file = bids_subject_dir / ".." / 'derivatives' / subject / 'anat' / (subject + '_T1w-chi.nii.gz') | ||
| chi_file = bids_subject_dir / ".." / 'derivatives' / 'b0sim' /subject / 'anat' / (subject + '_T1w-chi.nii.gz') | ||
|
|
||
| nib.save(new_volume, chi_file) | ||
| vol = nib.load(merged_labels_path.resolve()) | ||
| run_tissue2MR( | ||
| in_file = str(merged_labels_path.resolve()), | ||
| seg_tool = "compare_fm", | ||
| version = "ds005616", | ||
| tissue_prop = "sus", | ||
| out_file = str(chi_file) | ||
| ) | ||
|
|
||
| # Save json | ||
|
|
||
|
|
@@ -65,48 +49,8 @@ def label_to_chi(bids_subject_dir): | |
| bids_sidecar['input file'] = str(merged_labels_path.resolve()) | ||
| bids_sidecar['command'] = 'python label_to_chi.py -s ' + str(bids_subject_dir) | ||
|
|
||
| bids_sidecar['anatomy'] = {} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why remove the bids sidecar that report the values that were used to generate the map from the label? I think this is valuable - especially since the values can change with new information, as you have discovered re: chi. An alternative would maybe be to report the tissue2MR version / commit, but this makes it more difficult to actually go find the values / fetch them in the json sidecar files in a script. |
||
| bids_sidecar['anatomy']['background'] = {} | ||
| bids_sidecar['anatomy']['background']['label'] = 0 | ||
| bids_sidecar['anatomy']['background']['chi'] = 0.35 | ||
| bids_sidecar['anatomy']['body'] = {} | ||
| bids_sidecar['anatomy']['body']['label'] = '1' | ||
| bids_sidecar['anatomy']['body']['chi'] = -9.05 | ||
| bids_sidecar['anatomy']['sinus'] = {} | ||
| bids_sidecar['anatomy']['sinus'] ['label'] = 2 | ||
| bids_sidecar['anatomy']['sinus'] ['chi'] = -2 | ||
| bids_sidecar['anatomy']['earcanal'] = {} | ||
| bids_sidecar['anatomy']['earcanal'] ['label'] = 3 | ||
| bids_sidecar['anatomy']['earcanal']['chi'] = -2 | ||
| bids_sidecar['anatomy']['trachea'] = {} | ||
| bids_sidecar['anatomy']['trachea']['label'] = 4 | ||
| bids_sidecar['anatomy']['trachea']['chi'] = -4.2 | ||
| bids_sidecar['anatomy']['rightlung'] = {} | ||
| bids_sidecar['anatomy']['rightlung']['label'] = 5 | ||
| bids_sidecar['anatomy']['rightlung']['chi'] = -4.2 | ||
| bids_sidecar['anatomy']['leftlung'] = {} | ||
| bids_sidecar['anatomy']['leftlung']['label'] = 6 | ||
| bids_sidecar['anatomy']['leftlung']['chi'] = -4.2 | ||
| bids_sidecar['anatomy']['brain'] = {} | ||
| bids_sidecar['anatomy']['brain']['label'] = 56 | ||
| bids_sidecar['anatomy']['brain']['chi'] = -9.04 | ||
| bids_sidecar['anatomy']['eyes'] = {} | ||
| bids_sidecar['anatomy']['eyes']['label'] = 60 | ||
| bids_sidecar['anatomy']['eyes']['chi'] = -9.05 | ||
| bids_sidecar['anatomy']['skull'] = {} | ||
| bids_sidecar['anatomy']['skull']['label'] = 91 | ||
| bids_sidecar['anatomy']['skull']['chi'] = -11 | ||
| bids_sidecar['anatomy']['verterbae'] = {} | ||
| bids_sidecar['anatomy']['verterbae']['label'] = 92 | ||
| bids_sidecar['anatomy']['verterbae']['chi'] = -11 | ||
| bids_sidecar['anatomy']['disks'] = {} | ||
| bids_sidecar['anatomy']['disks']['label'] = 93 | ||
| bids_sidecar['anatomy']['disks']['chi'] = -9.055 | ||
| bids_sidecar['anatomy']['canal'] = {} | ||
| bids_sidecar['anatomy']['canal']['label'] = 100 | ||
| bids_sidecar['anatomy']['canal']['chi'] = -9.055 | ||
|
|
||
| json_file = bids_subject_dir / ".." / 'derivatives' / subject / 'anat' / (subject + '_T1w-chi.json') | ||
|
|
||
| json_file = bids_subject_dir / ".." / 'derivatives' / 'b0sim' /subject / 'anat' / (subject + '_T1w-chi.json') | ||
| if os.path.exists(json_file): | ||
| os.remove(json_file) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import nibabel as nib | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whole file duplicates most of the code in the label2chi.py. It would be better to reuse that code; there could be one file / function that would be called label_to_property, and add a flag depending on the property you want to use (eg chi, perm_cond, etc). That way you won't need to add new files for every other new map you'll want to create in the future (eg T2, T1, etc), you may just need to change one line or two in that one function. The other reason this is a better approach is from a review side - since there are no tests and many similar lines, I would need to go line by line to check that you properly copied / didn't accidentally change a line. Overall though, it looks like it should work the way you intend it to, but you would know best if it works or not. |
||
| import numpy as np | ||
| import argparse | ||
| from pathlib import Path | ||
| import os | ||
| import json | ||
| import datetime | ||
| import git | ||
| from utils.tissue2MR import run_tissue2MR | ||
|
|
||
|
|
||
| def label_to_perm_cond(bids_subject_dir): | ||
|
|
||
| bids_subject_dir = Path(bids_subject_dir) | ||
| subject = str(bids_subject_dir.stem) | ||
| merged_labels_path = bids_subject_dir / ".." / 'derivatives' / 'labels' / subject / 'anat' / (subject + '_T1w_label-all.nii.gz') | ||
|
|
||
| # Check if the directory bids_subject_dir / ".." / 'derivatives' / subject / exists and if no, create it | ||
|
|
||
| if not os.path.exists(bids_subject_dir / ".." / 'derivatives' / subject ): | ||
| os.makedirs(bids_subject_dir / ".." / 'derivatives' / subject ) | ||
|
|
||
| # Check if the directory bids_subject_dir / ".." / 'derivatives' / subject / 'anat' exists and if no, create it | ||
|
|
||
| if not os.path.exists(bids_subject_dir / ".." / 'derivatives' / subject / 'anat' ): | ||
| os.makedirs(bids_subject_dir / ".." / 'derivatives' / subject / 'anat' ) | ||
|
|
||
| elecsim_anat_dir = bids_subject_dir / ".." / 'derivatives' / 'elecsim' / subject / 'anat' | ||
| os.makedirs(elecsim_anat_dir, exist_ok=True) | ||
|
|
||
| perm7t_file = bids_subject_dir / ".." / 'derivatives' / 'elecsim' / subject / 'anat' / (subject + '_T1w-perm7T.nii.gz') | ||
| chi7t_file = bids_subject_dir / ".." / 'derivatives' / 'elecsim' / subject / 'anat' / (subject + '_T1w-cond7T.nii.gz') | ||
|
|
||
| run_tissue2MR( | ||
| in_file = str(merged_labels_path.resolve()), | ||
| seg_tool = "compare_fm", | ||
| version = "ds005616", | ||
| tissue_prop = "perm7T", | ||
| out_file = str(perm7t_file) | ||
| ) | ||
|
|
||
| run_tissue2MR( | ||
| in_file = str(merged_labels_path.resolve()), | ||
| seg_tool = "compare_fm", | ||
| version = "ds005616", | ||
| tissue_prop = "cond7T", | ||
| out_file = str(chi7t_file) | ||
| ) | ||
| # Save json | ||
|
|
||
| repo = git.Repo(search_parent_directories=True) | ||
|
|
||
|
|
||
| bids_sidecar = {} | ||
| bids_sidecar['author'] = os.getenv('USERNAME') | ||
| bids_sidecar['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") | ||
| bids_sidecar['script'] = str(Path(os.path.abspath(__file__)).resolve()) | ||
| bids_sidecar['script source'] = repo.remotes.origin.url | ||
| bids_sidecar['script commit hash'] = repo.head.object.hexsha | ||
| bids_sidecar['input file'] = str(merged_labels_path.resolve()) | ||
| bids_sidecar['command'] = 'python label_to_perm_cond.py -s ' + str(bids_subject_dir) | ||
|
|
||
| json_perm7t_file = bids_subject_dir / ".." / 'derivatives' / subject / 'anat' / (subject + '_T1w-perm7T.json') | ||
| json_cond7t_file = bids_subject_dir / ".." / 'derivatives' / subject / 'anat' / (subject + '_T1w-cond7T.json') | ||
| if os.path.exists(json_perm7t_file): | ||
| os.remove(json_perm7t_file) | ||
|
|
||
| with open(json_perm7t_file, 'w', encoding='utf-8') as f: | ||
| json.dump(bids_sidecar, f, ensure_ascii=False, indent=4) | ||
|
|
||
| if os.path.exists(json_cond7t_file): | ||
| os.remove(json_cond7t_file) | ||
|
|
||
| with open(json_cond7t_file, 'w', encoding='utf-8') as f: | ||
| json.dump(bids_sidecar, f, ensure_ascii=False, indent=4) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
|
|
||
| current_directory = os.path.dirname(os.path.abspath(__file__)) | ||
|
|
||
| # Create an argument parser | ||
| parser = argparse.ArgumentParser(description="Process subject directory path and other arguments.") | ||
|
|
||
| # Add the -s argument to the parser | ||
| parser.add_argument("-s", "--bids_subject_dir", required=True, help="Path to the subject directory in BIDS format") | ||
|
|
||
| # Parse the arguments | ||
| args = parser.parse_args() | ||
| label_to_perm_cond(args.bids_subject_dir) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| #!/bin/bash | ||
|
|
||
| # Find all the subjects in the bids directory and create a list of them | ||
| # This is useful for the batch processing of subjects | ||
|
|
||
| # Parse the command line arguments -b --bids_dir | ||
| while getopts ":b:" opt; do | ||
| case ${opt} in | ||
| b ) | ||
| BIDS_DIR=$OPTARG | ||
| ;; | ||
| \? ) | ||
| echo "Usage: cmd [-b] bids_dir" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| done | ||
|
|
||
| # Handle Windows-style paths like E:/... or C:/... | ||
| if [[ "$BIDS_DIR" =~ ^[A-Z]:/ ]]; then | ||
| drive=$(echo $BIDS_DIR | cut -c1 | tr 'A-Z' 'a-z') | ||
| path=$(echo $BIDS_DIR | cut -c3-) | ||
| BIDS_DIR="/cygdrive/$drive/$path" | ||
| fi | ||
|
|
||
| # Print the bids directory to the screen | ||
| echo "BIDS directory: $BIDS_DIR" | ||
|
|
||
| # Check if BIDS_DIR exists | ||
| if [ ! -d "$BIDS_DIR" ]; then | ||
| echo "[ERROR] BIDS directory not found: $BIDS_DIR" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # If subjects.txt exists, remove it and create a new one | ||
| if [ -f subjects.txt ]; then | ||
| rm subjects.txt | ||
| touch subjects.txt | ||
| fi | ||
|
|
||
| # Find all the subjects in the bids directory | ||
| SUBJECTS=$(ls $BIDS_DIR | grep sub-) | ||
|
|
||
| # Remove sub-unfErssm001 and sub-unfErssm021 from the list of subjects | ||
| SUBJECTS=$(echo $SUBJECTS | sed 's/sub-unfErssm001//g' | sed 's/sub-unfErssm021//g') | ||
|
|
||
| echo "Subjects found:" | ||
| echo $SUBJECTS | ||
|
|
||
| # Create a list of the subjects | ||
| echo $SUBJECTS > subjects.txt | ||
|
|
||
| # Get script directory | ||
| DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" | ||
|
|
||
| # Convert DIR to Windows path for Windows Python | ||
| WIN_DIR=$(echo "$DIR" | sed 's|/cygdrive/\([a-z]\)/|\U\1:/|') | ||
|
|
||
| for subject in $SUBJECTS | ||
| do | ||
| # Skip empty entries from excluded subjects | ||
| if [ -z "$subject" ]; then | ||
| continue | ||
| fi | ||
|
|
||
| echo "[INFO] Processing $subject" | ||
|
|
||
| # Convert BIDS_DIR to Windows path | ||
| WIN_BIDS_DIR=$(echo "$BIDS_DIR" | sed 's|/cygdrive/\([a-z]\)/|\U\1:/|') | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this line needed for every OS? |
||
|
|
||
| # Check if subject folder exists | ||
| if [ ! -d "$WIN_BIDS_DIR/$subject" ]; then | ||
| echo "[WARNING] Subject folder not found: $WIN_BIDS_DIR/$subject, skipping..." | ||
| continue | ||
| fi | ||
|
|
||
| # Check if Python script exists | ||
| if [ ! -f "$WIN_DIR/label_to_perm_cond.py" ]; then | ||
| echo "[ERROR] Python script not found: $WIN_DIR/label_to_perm_cond.py" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Run Python to generate perm/cond maps | ||
| "C:/Users/Admin/Miniconda3/python.exe" "$WIN_DIR/label_to_perm_cond.py" -s "$WIN_BIDS_DIR/$subject" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is too hardcoded; a user without a miniconda installation or with it installed in a different username won't be able to run this script when it is hardcoded like this. |
||
| done | ||
|
|
||
| echo "[INFO] All subjects processed." | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import subprocess | ||
| from pathlib import Path | ||
|
|
||
| def run_tissue2MR( | ||
| in_file: str, | ||
| seg_tool: str, | ||
| version: str, | ||
| tissue_prop: str, | ||
| out_file: str, | ||
| ): | ||
| """ | ||
| Run tissue_to_MR from command line inside Python. | ||
| Parameters | ||
| ---------- | ||
| in_file : str | ||
| Path to input NIfTI file (.nii.gz). | ||
| seg_tool : str | ||
| The tool used to segmented the input file (argument to -s). | ||
| version : str | ||
| Version of the tool used to select approriate dictionary (argument to -v). | ||
| tissue_prop : str | ||
| Tissue type desired (argument to -t). | ||
| out_file : str | ||
| Path to output NIfTI file (.nii.gz). | ||
| """ | ||
|
|
||
| # Ensure paths are safe | ||
| in_file = Path(in_file).resolve() | ||
| out_file = Path(out_file).resolve() | ||
|
|
||
| cmd = [ | ||
| "tissue_to_MR", | ||
| "-i", str(in_file), | ||
| "-s", seg_tool, | ||
| "-v", version, | ||
| "-t", tissue_prop, | ||
| "-o", str(out_file) | ||
| ] | ||
|
|
||
| try: | ||
| subprocess.run(cmd, check=True) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't good practice unfortunately - it will make it very difficult to debug / test, and also make it challenging for cross-platform functionality. A better approach would be to import your tissue2MR python module and call the python functions directly. |
||
| print(f"[INFO] tissue_to_MR finished successfully. Output: {out_file}") | ||
| except subprocess.CalledProcessError as e: | ||
| print("[ERROR] tissue_to_MR failed!") | ||
| print(e) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating this! Maybe a comment could be left explaining why the change (i.e. previously was made for wholespine version X, but in version Y this was updated.