-
Notifications
You must be signed in to change notification settings - Fork 0
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
Adding the python script for manual labeling #2
Open
lrouhier
wants to merge
24
commits into
master
Choose a base branch
from
lr/python_manual_script
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
67235b4
first version of the script (WIP)
599d771
Modified with an option to skip existing label
e9495d4
scrpit that retrieve BIDS file
c799481
correction of output file name
6fab4c6
error on list name
lrouhier 265eae4
added output folder (if not same as input)
lrouhier de47052
added something for best view
lrouhier 273a798
some files had more than 15 discs
lrouhier 59ff681
Modification for task sharing.
lrouhier 805f350
adding sop
lrouhier 4617a4f
added image
lrouhier 59c915d
added auto update of the list with made subject
lrouhier 70c45d5
modified sop for stoping
lrouhier 57f7186
modification for -file argument
lrouhier b33396a
modification for output change
lrouhier 8c0a765
sop: minor fixes
charleygros d8343ee
modification for no condition
lrouhier 6cb3ba5
better looking sop (Charley comment)
lrouhier 78442f4
get_BIDS_sub: robust to not provided arg
charleygros 755bece
get_BIDS_sub: get only sub-*.nii.gz
charleygros f7f67da
sop.md: typo
charleygros fb9d37e
Update get_BIDS_sub.py (help issue)
lrouhier cb64694
Update manual_labeling.py (typo missing letter)
lrouhier 5a0d685
added minimal resolution
lrouhier File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import sys | ||
import os | ||
import argparse | ||
import json | ||
import subprocess | ||
import nibabel as nib | ||
import glob | ||
import numpy | ||
|
||
def get_parser(): | ||
# Mandatory arguments | ||
parser = argparse.ArgumentParser( | ||
description=" ", | ||
epilog="EXAMPLES:\n", | ||
prog=os.path.basename(__file__).strip('.py')) | ||
|
||
mandatoryArguments = parser.add_argument_group("\nMANDATORY ARGUMENTS") | ||
mandatoryArguments.add_argument( | ||
'-path', | ||
required=True, | ||
help="path to BIDS Data", | ||
) | ||
optional = parser.add_argument_group("\nOPTIONAL ARGUMENTS") | ||
optional.add_argument( | ||
'-constraint', | ||
help="constraint field. You can use a specific fields from the header or 'orientation'", | ||
) | ||
|
||
optional.add_argument( | ||
'-value', | ||
help="value of constraint. Number or other. For orientation use capital letters (e.g., RPI) default operation is '==' if you wish to use something else please check the -ope option.", | ||
) | ||
optional.add_argument( | ||
'-ope', | ||
default='==', | ||
help=" operation type. You can use '<' or '>'. Don't forget to use quote (Unix will throw EOL error otherwise)") | ||
|
||
optional.add_argument( | ||
'-ofile', | ||
help="name of output file (txt file). If the file already exist, the found subject will be added at the end of the file") | ||
|
||
return parser | ||
|
||
|
||
def get_view(im): | ||
nifti = nib.load(im) | ||
axis = nib.aff2axcodes(nifti.affine) | ||
best_res_axis = numpy.where(nifti.header['pixdim'][1:4] == nifti.header['pixdim'][1:4].min())[0] | ||
if len(best_res_axis)==3: | ||
return 'valid' | ||
elif len(best_res_axis)==2: | ||
plane_dic={'SA':'sagittal','SP':'sagittal','IA':'sagittal','IP':'sagittal','AS':'sagittal','PS':'sagittal','AI':'sagittal','PI':'sagittal','SR':'coronal','SL':'coronal','IR':'coronal','IL':'coronal','RS':'coronal','LS':'coronal','RI':'coronal','LI':'coronal','AR':'axial','AL':'axial','PR':'axial','PL':'axial','RP':'axial','LP':'axial','RA':'axial','LA':'axial'} | ||
best_plane = axis[best_res_axis[0]] + axis[best_res_axis[1]] | ||
if nifti.header['pixdim'][1:4].min()>2: | ||
return 'not_valid' | ||
else: | ||
return plane_dic[best_plane] | ||
|
||
|
||
|
||
def main(args=None): | ||
if args is None: | ||
args = None if sys.argv[1:] else ['--help'] | ||
parser = get_parser() | ||
arguments = parser.parse_args(args=args) | ||
if arguments.constraint is not None and arguments.value is None: | ||
parser.error("-constraint requires the -value option") | ||
field = arguments.constraint | ||
path_data = arguments.path | ||
value = arguments.value | ||
operation = arguments.ope | ||
if arguments.ofile is not None: | ||
out = arguments.ofile | ||
else: | ||
out = 'list-generated' | ||
for param in [field, operation, value]: | ||
if param: | ||
out += param | ||
path_images = (glob.glob(path_data+'/sub-*/anat/sub-*.nii.gz')) #grab all subject images path | ||
|
||
to_keep = [] | ||
for im in path_images: | ||
|
||
if field == 'orientation': | ||
#Orientation gets a special case because it is not in the header per se | ||
if nib.aff2axcodes(nifti.affine) == (str(value[0]), str(value[1]), str(value[2])): | ||
spli = im.rsplit('/',3) #we get the last 3 element | ||
subj = spli[-3]+'/'+spli[-2]+'/'+spli[-1] | ||
to_keep.append(subj) | ||
|
||
elif field == 'view': | ||
#sagittal or axial or coronal. | ||
if get_view(im) == value or get_view(im) == 'valid': | ||
spli = im.rsplit('/',3) #we get the last 3 element | ||
subj = spli[-3]+'/'+spli[-2]+'/'+spli[-1] | ||
to_keep.append(subj) | ||
|
||
|
||
elif field is not None: | ||
nifti = nib.load(im) | ||
if eval(str(nifti.header[field]) + operation + str(value)): #eval() allows to ask the user for '<' or '>' | ||
spli = im.rsplit('/',3) #we get the last 3 element | ||
subj = spli[-3]+'/'+spli[-2]+'/'+spli[-1] | ||
to_keep.append(subj) | ||
else: | ||
spli = im.rsplit('/',3) #we get the last 3 element | ||
subj = spli[-3]+'/'+spli[-2]+'/'+spli[-1] | ||
to_keep.append(subj) | ||
|
||
if len(to_keep)>0: | ||
f = open(out + '.txt', 'a') # 'a' option allows you to append file to a list. | ||
l1 = map(lambda x: x + '\n', to_keep) | ||
f.writelines(l1) | ||
f.close() | ||
else: | ||
print('No file matching your criteria found') | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
|
||
|
||
|
||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import sys | ||
import os | ||
import argparse | ||
import json | ||
import subprocess | ||
|
||
|
||
def get_parser(): | ||
# Mandatory arguments | ||
parser = argparse.ArgumentParser( | ||
description=" ", | ||
epilog="EXAMPLES:\n", | ||
add_help=None, | ||
prog=os.path.basename(__file__).strip('.py')) | ||
|
||
mandatoryArguments = parser.add_argument_group("\nMANDATORY ARGUMENTS") | ||
mandatoryArguments.add_argument( | ||
'-file', | ||
required=True, | ||
help="Json file containing list of path to image file", | ||
) | ||
mandatoryArguments.add_argument( | ||
'-path', | ||
required=True, | ||
help="path to bids folder",) | ||
mandatoryArguments.add_argument( | ||
'-author', | ||
required=True, | ||
help="Author name for json file", | ||
) | ||
optional = parser.add_argument_group("\nOPTIONAL ARGUMENTS") | ||
optional.add_argument( | ||
'-correct', | ||
default=0, | ||
help=" if this is activated the -ilabel option will be used and therefore existing file will be open") | ||
optional.add_argument( | ||
'-o', | ||
help="output path. if empty it will save in BIDS_path/label/derivatives/sub/anat ") | ||
|
||
return parser | ||
|
||
|
||
def main(args=None): | ||
if args is None: | ||
args = None if sys.argv[1:] else ['--help'] | ||
parser = get_parser() | ||
arguments = parser.parse_args(args=args) | ||
file_path = arguments.file | ||
author_name = arguments.author | ||
correct = arguments.correct | ||
json_content = {"author": author_name, "label": "labels-disc-manual"} | ||
list_of_subj = [line.rstrip('\n') for line in open(file_path)] | ||
derivatives_base = arguments.path # file path is BIDS: last 3 elements are /sub-xx/anat/FILENAM | ||
derivatives_path = derivatives_base + 'derivatives/labels' | ||
if arguments.o is not None: | ||
out_path = arguments.o | ||
i=0 | ||
else: | ||
out_path = derivatives_path | ||
i=0 | ||
|
||
try: | ||
for rel_path in list_of_subj: | ||
im_path = arguments.path+rel_path | ||
label_base = im_path.rsplit('/', 1)[-1][:-7] # we remove the last 7 caracters that are .nii.gz | ||
subj = im_path.rsplit('/', 3)[-3] | ||
label_filename = label_base + '_labels-disc-manual.nii.gz' | ||
json_filename = label_base + '_labels-disc-manual.json' | ||
|
||
if os.path.exists( out_path+'/'+ subj + '/anat'): | ||
pass | ||
else: | ||
os.makedirs(out_path +'/'+ subj + '/anat') | ||
path_json = out_path +'/'+ subj +'/anat/' + json_filename | ||
path_label = derivatives_path + subj + '/anat/' + label_filename # retrieving label filename | ||
path_out = out_path +'/'+ subj + '/anat/' + label_filename | ||
|
||
if correct: | ||
if os.path.exists(path_label): | ||
command = """sct_label_utils -i """ + im_path + """ -create-viewer 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 -ilabel """ + path_label + """ -o """ + path_out | ||
else: | ||
command = """sct_label_utils -i """ + im_path + """ -create-viewer 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 -o """ + path_out | ||
|
||
subprocess.run(command, shell=True) | ||
i=i+1 | ||
|
||
with open(path_json, 'w') as f: | ||
json.dump(json_content, f) | ||
else: | ||
if os.path.exists(path_label): | ||
pass | ||
else: | ||
command = """sct_label_utils -i """ + im_path + """ -create-viewer 3,4,5,6,7,8,9,10,11,12,13,14,15 -o """ + path_out | ||
subprocess.run(command, shell=True) | ||
i=i+1 | ||
with open(path_json, 'w') as f: | ||
json.dump(json_content, f) | ||
|
||
except KeyboardInterrupt: | ||
print('saving list') | ||
following = list_of_subj[i:] | ||
f = open(file_path,'w') # 'a' option allows you to append file to a list. | ||
l1 = map(lambda x: x + '\n', following) | ||
f.writelines(l1) | ||
f.close() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
This script is made for manual labeling of BIDS folder | ||
|
||
Run the following with python: | ||
|
||
python manual-labeling/manual_labeling.py -file list_todo_update.txt -author lucas -path Path_to_duke/ \[-correct 1 -o label_tmp\] | ||
|
||
- -file: txt file that contains the list of all images inside bids root folder that you want to process separated by '\n'.The format in sub-xx/anat/sub-xxx_xxx.nii.gz. Can be obtained with get_bids_sub.py. | ||
- -path : Path the Bids root folder (duke) with a '/' at the end | ||
- -author: Author name that will appear on the .json file | ||
- -correct: Boolean. Default is 0. If correct is 1, the script will look for existing label and open them with the -ilabel option from sct_label_utils so you can verify/correct existing label. | ||
- -o: desired output folder. It will be created if missing. After the task, you will find the file there in BIDS convention sub-xxx/anat/sub-xxx_labels-disc-manual.nii.gz and sub-xxx/anat/sub-xxx_labels-disc-manual.json" if the argument is not use or empty files will be saved in BIDS_PATH/derivatives/labels/sub-xx/anat/xxx | ||
|
||
To end the script: | ||
Perform a keyboard interrupt from the terminal (ctrl+c). This will update the list by deleting the viewed subjects. don't forget to commit and push it on github. | ||
|
||
Specific: | ||
suffix to label is labels-disc-manual | ||
The json file contain the name of the label and the name of the author given by the -author args. | ||
|
||
Example image for manual labeling: | ||
![example](label_disc_capture.png) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Would it be relevant to add a parameter that controls this suffix?
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.
it's actually a remain of a first version where I asked for file suffix. I'll remove it from the SOP. Now there are no reason that I can think of. we can add a case for the constraint on it if you think this could be useful.
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.
I was simply thinking: if one day we want to label pmj instead of the disc -->
pmj-manual
instead oflabels-disc-manual
.But we can leave it as it is for now :-)