1+ #!/usr/bin/python3
2+ # Warning: works only on unix-like systems, not windows where "python3 animaAtlasEMTissuesSegmentation.py ..." has to be run
3+
4+ import sys
5+ import argparse
6+
7+ if sys .version_info [0 ] > 2 :
8+ import configparser as ConfParser
9+ else :
10+ import ConfigParser as ConfParser
11+
12+ import glob
13+ import os
14+ from shutil import copyfile , rmtree
15+ from subprocess import call , check_output
16+ import uuid
17+
18+ configFilePath = os .path .join (os .path .expanduser ("~" ), ".anima" , "config.txt" )
19+ if not os .path .exists (configFilePath ):
20+ print ('Please create a configuration file for Anima python scripts. Refer to the README' )
21+ quit ()
22+
23+ configParser = ConfParser .RawConfigParser ()
24+ configParser .read (configFilePath )
25+
26+ animaDir = configParser .get ("anima-scripts" , 'anima' )
27+ animaExtraDataDir = configParser .get ("anima-scripts" , 'extra-data-root' )
28+ animaPyramidalBMRegistration = os .path .join (animaDir , "animaPyramidalBMRegistration" )
29+ animaDenseSVFBMRegistration = os .path .join (animaDir , "animaDenseSVFBMRegistration" )
30+ animaTransformSerieXmlGenerator = os .path .join (animaDir , "animaTransformSerieXmlGenerator" )
31+ animaApplyTransformSerie = os .path .join (animaDir , "animaApplyTransformSerie" )
32+ animaTissuesEMClassification = os .path .join (animaDir , "animaTissuesEMClassification" )
33+ animaConvertImage = os .path .join (animaDir , "animaConvertImage" )
34+ animaMaskImage = os .path .join (animaDir , "animaMaskImage" )
35+ animaThrImage = os .path .join (animaDir , "animaThrImage" )
36+ animaN4BiasCorrection = os .path .join (animaDir , "animaN4BiasCorrection" )
37+ animaMorphologicalOperations = os .path .join (animaDir , "animaMorphologicalOperations" )
38+
39+ # Argument parsing
40+ parser = argparse .ArgumentParser (
41+ description = "Computes tissue segmentation from a prior atlas, multiple modalities inputs, and "
42+ "EM tissue classification" )
43+
44+ parser .add_argument ('-i' , '--input' , type = str , required = True , action = 'append' ,
45+ help = 'Image input: can give multiple modalities' )
46+ parser .add_argument ('-a' , '--atlas' , type = str , help = 'Atlas folder (default: use the one in anima scripts data '
47+ '- em_prior_atlas folder)' )
48+ parser .add_argument ('-m' , '--mask' , type = str , required = True , help = 'Brain mask of the first input' )
49+
50+ parser .add_argument ('-o' , '--output' , type = str , help = 'Output path of the tissues segmentation '
51+ '(default is inputName_classification.nrrd)' )
52+ parser .add_argument ('-O' , '--classes-output' , type = str , help = 'Output path of the tissues probabilities' )
53+ parser .add_argument ('-z' , '--zsc' , type = str , help = 'Output path of the zscores of classification' )
54+ parser .add_argument ('-K' , '--keep-intermediate-folder' , action = 'store_true' ,
55+ help = 'Keep intermediate folder after script end' )
56+ parser .add_argument ('-P' , '--prune-outliers' , action = 'store_true' ,
57+ help = 'Remove outliers from the segmentation (according to z-score)' )
58+ parser .add_argument ('-Z' , '--zsc-thr' , type = int , default = 4 ,
59+ help = 'Outliers threshold if -P is activated (default: 4)' )
60+
61+ args = parser .parse_args ()
62+
63+ numImages = len (sys .argv ) - 1
64+
65+ atlasDir = os .path .join (animaExtraDataDir ,"em_prior_atlas" )
66+ if args .atlas :
67+ atlasDir = args .atlas
68+
69+ atlasImage = os .path .join (atlasDir ,"T1.nrrd" )
70+ tissuesImage = os .path .join (atlasDir ,"Tissue_Probabilities.nrrd" )
71+
72+ brainImages = args .input
73+
74+ # Get floating image prefix
75+ brainImagePrefix = os .path .splitext (brainImages [0 ])[0 ]
76+ if os .path .splitext (brainImages [0 ])[1 ] == '.gz' :
77+ brainImagePrefix = os .path .splitext (brainImagePrefix )[0 ]
78+
79+ tissuesOutputName = args .output if args .output else brainImagePrefix + "_classification.nrrd"
80+ intermediateFolder = os .path .join (os .path .dirname (brainImages [0 ]), 'em_tissues_' + str (uuid .uuid1 ()))
81+
82+ if not os .path .isdir (intermediateFolder ):
83+ os .mkdir (intermediateFolder )
84+
85+ brainImagePrefix = os .path .join (intermediateFolder , os .path .basename (brainImagePrefix ))
86+
87+ # Decide on whether to use large image setting or small image setting
88+ command = [animaConvertImage , "-i" , brainImages [0 ], "-I" ]
89+ convert_output = check_output (command , universal_newlines = True )
90+ size_info = convert_output .split ('\n ' )[1 ].split ('[' )[1 ].split (']' )[0 ]
91+ large_image = False
92+ for i in range (0 , 3 ):
93+ size_tmp = int (size_info .split (', ' )[i ])
94+ if size_tmp >= 350 :
95+ large_image = True
96+ break
97+
98+ pyramidOptions = ["-p" , "4" , "-l" , "1" ]
99+ if large_image :
100+ pyramidOptions = ["-p" , "5" , "-l" , "2" ]
101+
102+ # First preprocess input images
103+ myfileImages = open (os .path .join (intermediateFolder ,"listData.txt" ),"w" )
104+ for i in range (0 ,len (brainImages )):
105+ currentData = brainImages [i ]
106+ if i > 0 :
107+ command = [animaPyramidalBMRegistration , "-m" , currentData ,
108+ "-r" , brainImages [0 ],
109+ "-o" , brainImagePrefix + "_rig_onRef_" + str (i ) + ".nrrd" , "--sp" , "3" ] + pyramidOptions
110+ call (command )
111+
112+ currentData = brainImagePrefix + '_rig_onRef_' + str (i ) + '.nrrd'
113+
114+ command = [animaMaskImage , "-i" , currentData , "-m" , args .mask ,
115+ "-o" , brainImagePrefix + '_masked_' + str (i ) + '.nrrd' ]
116+ call (command )
117+
118+ command = [animaN4BiasCorrection , "-i" , brainImagePrefix + '_masked_' + str (i ) + '.nrrd' ,
119+ "-o" , brainImagePrefix + '_biasCorrected_' + str (i ) + '.nrrd' ]
120+ call (command )
121+
122+ myfileImages .write (brainImagePrefix + '_biasCorrected_' + str (i ) + '.nrrd\n ' )
123+
124+ myfileImages .close ()
125+
126+ # Now register prior atlas onto first reference image
127+ command = [animaPyramidalBMRegistration , "-m" , atlasImage , "-r" , brainImages [0 ], "-o" , brainImagePrefix + "_rig.nrrd" ,
128+ "-O" , brainImagePrefix + "_rig_tr.txt" , "--sp" , "3" ] + pyramidOptions
129+ call (command )
130+
131+ command = [animaPyramidalBMRegistration , "-m" , atlasImage , "-r" , brainImages [0 ], "-o" , brainImagePrefix + "_aff.nrrd" ,
132+ "-O" , brainImagePrefix + "_aff_tr.txt" , "-i" , brainImagePrefix + "_rig_tr.txt" , "--sp" , "3" , "--ot" ,
133+ "2" ] + pyramidOptions
134+ call (command )
135+
136+ command = [animaDenseSVFBMRegistration , "-r" , brainImages [0 ], "-m" , brainImagePrefix + "_aff.nrrd" ,
137+ "-o" , brainImagePrefix + "_nl.nrrd" , "-O" , brainImagePrefix + "_nl_tr.nrrd" , "--tub" , "2" ] + pyramidOptions
138+ call (command )
139+
140+ command = [animaTransformSerieXmlGenerator , "-i" , brainImagePrefix + "_aff_tr.txt" , "-i" ,
141+ brainImagePrefix + "_nl_tr.nrrd" , "-o" , brainImagePrefix + "_nl_tr.xml" ]
142+ call (command )
143+
144+ command = [animaApplyTransformSerie , "-i" , tissuesImage , "-t" , brainImagePrefix + "_nl_tr.xml" , "-g" , brainImages [0 ],
145+ "-o" , brainImagePrefix + "_PriorTissues.nrrd" ]
146+ call (command )
147+
148+ # Finally, run classification step
149+ command = [animaTissuesEMClassification , "-i" , os .path .join (intermediateFolder ,"listData.txt" ),
150+ "-m" , args .mask , "-t" , brainImagePrefix + "_PriorTissues.nrrd" , "-o" , tissuesOutputName ]
151+
152+ if args .classes_output :
153+ command = command + ["-O" , args .classes_output ]
154+
155+ zscOut = args .zsc
156+
157+ if not args .zsc and args .prune_outliers is True :
158+ zscOut = brainImagePrefix + "_zsc.nrrd"
159+
160+ if zscOut :
161+ command = command + ["-z" , zscOut ]
162+
163+ call (command )
164+
165+ if args .prune_outliers is True :
166+ command = [animaThrImage , "-i" , zscOut , "-t" , str (args .zsc_thr ), "-o" , brainImagePrefix + "_WrongMask.nrrd" , "-I" ]
167+ call (command )
168+
169+ command = [animaMaskImage , "-i" , tissuesOutputName , "-m" , brainImagePrefix + "_WrongMask.nrrd" ,
170+ "-o" , tissuesOutputName ]
171+ call (command )
172+
173+ if not args .keep_intermediate_folder :
174+ rmtree (intermediateFolder )
0 commit comments