Skip to content
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

Generalize manual correction scripts #2

Merged
merged 41 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c562652
Update description, imports, and help
valosekj Jan 19, 2023
49b1366
Add new input flags
valosekj Jan 19, 2023
291be5f
Include also GM segmentation ('FILES_GMSEG')
valosekj Jan 19, 2023
44a54f6
Allow to select a viewer (FSLeyes, ITKsnap, 3D Slicer). Add viewer_no…
valosekj Jan 19, 2023
74da357
Implement viewer_not_found to correct_vertebral_labeling. Allow passi…
valosekj Jan 19, 2023
feb8eaa
Implement viewer_not_found to correct_pmj_label.
valosekj Jan 19, 2023
fae12ad
Move the question if to modify labels to a separate function.
valosekj Jan 19, 2023
272c4c9
Introduce fetch_yaml_config function to simplify code among scripts.
valosekj Jan 19, 2023
abe52af
Introduce get_full_path function (to return full path and deal with ~…
valosekj Jan 19, 2023
0e312aa
Introduce fetch_subject_and_session function (to unify get_subject an…
valosekj Jan 19, 2023
daf74dc
Simplify code using fetch_subject_and_session
valosekj Jan 19, 2023
6a1147a
Introduce suffix_dict (which is built from suffixes passed by the user).
valosekj Jan 19, 2023
0f010c1
Update paths for several variables
valosekj Jan 19, 2023
ab791d4
Allow to specify '-suffix-files-seg' ('_seg' or '_label-SC_mask')
valosekj Jan 19, 2023
3d1d81c
Add TODOs
valosekj Jan 19, 2023
f89a9ac
Allow to specify '-suffix-files-seg', '-suffix-files-gmseg', and '-su…
valosekj Jan 19, 2023
9060aaa
Construct paths using fetch_subject_and_session
valosekj Jan 19, 2023
b987153
Extend info message printed to CLI during file copying
valosekj Jan 19, 2023
7a3c18a
Use fetch_subject_and_session inside check_files_exist function
valosekj Jan 19, 2023
43975fa
Description update
valosekj Jan 19, 2023
2cf35b2
Add copy_files_to_derivatives.py script.
valosekj Jan 19, 2023
57b5e11
Add .gitignore
valosekj Jan 19, 2023
a0e1b93
Add requirements.txt
valosekj Jan 19, 2023
a200aaa
Move QC generation to separate function (generate_qc) and do it also …
valosekj Jan 19, 2023
72e7e1b
README.md update
valosekj Jan 19, 2023
8bc0b05
Do not check for missing files for 'add-seg-only' flag
valosekj Jan 23, 2023
8cfc81f
Remove TODO - code is robust enough
valosekj Jan 23, 2023
805f556
Allow to remove the file suffix (e.g., '_RPI_r') for 'add-seg-only' flag
valosekj Jan 23, 2023
6aae8e9
Allow lesion correction using FILES_LESION
valosekj Jan 25, 2023
b29c882
Rename overwrite to copy (for clarity)
valosekj Jan 25, 2023
7a9dae8
Clarify comments
valosekj Jan 25, 2023
8f1ffb8
add args to specify color for fsleyes viewer
naga-karthik Jan 27, 2023
f704fec
minor fix to not generate QC for FILES_LESION
naga-karthik Jan 27, 2023
65acb5f
Add Naga Karthik as a script co-author.
valosekj Jan 27, 2023
c7f1f7e
Improve help formatting. Add docstring.
valosekj Jan 27, 2023
bb3dee4
Fix package_for_correction.py description
valosekj Jan 27, 2023
b08a72f
Add -suffix-files-pmj input flag
valosekj Jan 27, 2023
fe3b203
Add -suffix-files-lesion input flag
valosekj Jan 27, 2023
0a7c527
Clarify -path-derivatives argument description
valosekj Jan 27, 2023
2f52f0c
Describe 'FILES_LESION' in the script description and help
valosekj Jan 28, 2023
f469355
Rename '-label-list' to '-label-disc-list'
valosekj Jan 31, 2023
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
.idea
*/__pycache__/
venv/
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,44 @@
# manual-correction
# Manual correction

This repository contains scripts for the manual correction of spinal cord labels. Currently supported labels are:
- spinal cord segmentation
- gray matter segmentation
- disc labels
- ponto-medullary junction (PMJ) label

## Installation

```console
git clone https://github.com/spinalcordtoolbox/manual-correction.git
cd manual-correction
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```

## Usage

> **Note** All scripts currently assume BIDS-compliant data. For more information about the BIDS standard, please visit http://bids.neuroimaging.io.

### `package_for_correction.py`

The `package_for_correction.py` script is used to create a zip file containing the processed images and labels
(segmentations, disc labels, etc.). The zip file is then sent to the user for manual correction.

This is useful when you need to correct the labels of a large dataset processed on a remote server. In this case, you do
not need to copy the whole dataset. Instead, only the images and labels that need to be corrected are zipped. The yaml
list of images to correct can be obtained from the [SCT QC html report](https://spinalcordtoolbox.com/overview/concepts/inspecting-results-qc-fsleyes.html#how-do-i-use-the-qc-report).

### `manual_correction.py`

The `manual_correction.py` script is used to correct the labels (segmentations, disc labels, etc.) of a dataset.
The script takes as input a processed dataset and outputs the corrected labels to `derivatives/labels` folder.

For the correction of spinal cord and gray matter segmentation, you can choose a viewer ([FSLeyes](https://open.win.ox.ac.uk/pages/fsl/fsleyes/fsleyes/userdoc/#), [ITK-SNAP](http://www.itksnap.org/pmwiki/pmwiki.php), [3D Slicer](https://www.slicer.org)).

For the correction of vertebral labeling and ponto-medullary junction (PMJ), [sct_label_utils](https://github.com/spinalcordtoolbox/spinalcordtoolbox/blob/master/spinalcordtoolbox/scripts/sct_label_utils.py) is used.

### `copy_files_to_derivatives.py`

The `copy_files_to_derivatives.py` script is used to copy manually corrected labels (segmentations, disc labels, etc.)
from your local `derivatives/labels` folder to the already existing git-annex BIDS dataset's `derivatives/labels` folder.
86 changes: 86 additions & 0 deletions copy_files_to_derivatives.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python
#
# Copy manually corrected labels (segmentations, vertebral labeling, etc.) from the preprocessed dataset to the
# git-annex BIDS dataset's derivatives folder
#
# Authors: Jan Valosek
#

import argparse
import glob
import os
import shutil
import utils


def get_parser():
"""
parser function
"""

parser = argparse.ArgumentParser(
description='Copy manually corrected files (segmentations, vertebral labeling, etc.) from the source '
'preprocessed dataset to the git-annex BIDS derivatives folder',
formatter_class=utils.SmartFormatter,
prog=os.path.basename(__file__).strip('.py')
)
parser.add_argument(
'-path-in',
metavar="<folder>",
required=True,
type=str,
help='Path to the folder with manually corrected files (usually derivatives). The script assumes that labels '
'folder is located in the provided folder.'
)
parser.add_argument(
'-path-out',
metavar="<folder>",
required=True,
type=str,
help='Path to the BIDS dataset where manually corrected files will be copied. Include also derivatives folder '
'in the path. Files will be copied to the derivatives/label folder.'
)

return parser


def main():

# Parse the command line arguments
parser = get_parser()
args = parser.parse_args()

# Check if path_in exists
if os.path.isdir(args.path_in):
path_in = os.path.join(os.path.abspath(args.path_in), 'labels')
else:
raise NotADirectoryError(f'{args.path_in} does not exist.')

# Check if path_out exists
if os.path.isdir(args.path_out):
path_out = os.path.join(os.path.abspath(args.path_out), 'labels')
else:
raise NotADirectoryError(f'{args.path_out} does not exist.')

# Loop across files in input dataset
for path_file_in in sorted(glob.glob(path_in + '/**/*.nii.gz', recursive=True)):
sub, ses, filename, contrast = utils.fetch_subject_and_session(path_file_in)
# Construct path for the output file
path_file_out = os.path.join(path_out, sub, ses, contrast, filename)
# Check if subject's folder exists in the output dataset, if not, create it
path_subject_folder_out = os.path.join(path_out, sub, ses, contrast)
if not os.path.isdir(path_subject_folder_out):
os.makedirs(path_subject_folder_out)
print(f'Creating directory: {path_subject_folder_out}')
# Copy nii and json files to the output dataset
# TODO - consider rsync instead of shutil.copy
shutil.copy(path_file_in, path_file_out)
print(f'Copying: {path_file_in} to {path_file_out}')
path_file_json_in = path_file_in.replace('nii.gz', 'json')
path_file_json_out = path_file_out.replace('nii.gz', 'json')
shutil.copy(path_file_json_in, path_file_json_out)
print(f'Copying: {path_file_json_in} to {path_file_json_out}')


if __name__ == '__main__':
main()
Loading