1#!/usr/local/bin/python3.8 2 3# python3 status: ready 4 5# system libraries 6import sys, os 7 8# AFNI libraries 9from afnipy import afni_util as UTIL 10from afnipy import option_list as OL 11 12# ---------------------------------------------------------------------- 13# globals 14 15g_program = 'parse_fs_lt_log.py' 16 17g_help_01 = """ 18============================================================================= 19parse_fs_lt_log.py - parse FreeSurfer labeltable log file 20 21 Get labeltable indices from a rank log file, such as: 22 23 aparc+aseg_rank.niml.lt.log 24 25 usage: parse_fs_lt_log.py -logfile aparc+aseg_rank.niml.lt.log \\ 26 -labels CC_Posterior CC_Mid_Posterior 27 28""" 29g_help_examples = """ 30------------------------------------------ 31examples: 32 33 Example 0: common usage - simply get original indices for aparc+aseg.nii 34 35 parse_fs_lt_log.py -logfile aparc+aseg_rank.niml.lt.log \\ 36 -labels FS_white_matter -verb 0 -show_orig 37 38 Example 1: get known FreeSurfer labels 39 40 parse_fs_lt_log.py -logfile aparc+aseg_rank.niml.lt.log \\ 41 -labels FS_white_matter 42 43 parse_fs_lt_log.py -logfile aparc+aseg_rank.niml.lt.log \\ 44 -labels FS_ventricles 45 46 Example 2: get a specific list of list labels 47 48 parse_fs_lt_log.py -logfile aparc+aseg_rank.niml.lt.log \\ 49 -labels CC_Posterior CC_Mid_Posterior 50 51 Example 3: get known plus extra labels 52 53 parse_fs_lt_log.py -logfile aparc+aseg_rank.niml.lt.log \\ 54 -labels FS_white_matter Left-Cerebellum-Exterior \\ 55 -show_all_orig 56 57""" 58g_help_02 = """ 59------------------------------------------ 60terminal options: 61 62 -help : show this help 63 -hist : show the revision history 64 -ver : show the version number 65 66------------------------------------------ 67process options: 68 69 -labels : specify a list of labels to search for 70 71 e.g. -labels Left-Cerebral-White-Matter Left-Cerebellum-White-Matter \\ 72 Right-Cerebral-White-Matter Right-Cerebellum-White-Matter \\ 73 CC_Posterior CC_Mid_Posterior CC_Central CC_Mid_Anterior \\ 74 CC_Anterior Brain-Stem 75 76 e.g. -labels FS_white_matter 77 78 For convenience, there are 2 label groups: 79 80 FS_white_matter (as in the example): 81 82 Left-Cerebral-White-Matter Left-Cerebellum-White-Matter 83 Right-Cerebral-White-Matter Right-Cerebellum-White-Matter 84 CC_Posterior CC_Mid_Posterior CC_Central CC_Mid_Anterior 85 CC_Anterior Brain-Stem 86 87 FS_ventricles 88 89 Left-Lateral-Ventricle Left-Inf-Lat-Vent 90 3rd-Ventricle 4th-Ventricle CSF 91 Right-Lateral-Ventricle Right-Inf-Lat-Vent 5th-Ventricle 92 93 -logfile : specify rank log file 94 95 e.g. -logfile aparc+aseg_rank.niml.lt.log 96 97------------------------------------------ 98R Reynolds May, 2016 99============================================================================= 100""" 101 102def show_help(): 103 print(g_help_01 + g_help_examples + g_help_02) 104 105g_todo = """ 106 todo list: 107 108 - eat more cheese 109""" 110 111g_history = """ 112 parse_fs_lt_log.py history: 113 114 0.0 May 23, 2016 - initial version 115""" 116 117g_version = "parse_fs_lt_log.py version 0.0, May 23, 2016" 118 119# default label lists 120g_fs_wm_labels = [ \ 121 'Left-Cerebral-White-Matter', 'Left-Cerebellum-White-Matter', 122 'Right-Cerebral-White-Matter', 'Right-Cerebellum-White-Matter', 123 'CC_Posterior', 'CC_Mid_Posterior', 'CC_Central', 'CC_Mid_Anterior', 124 'CC_Anterior', 'Brain-Stem' ] 125 126g_fs_vent_labels = [ \ 127 'Left-Lateral-Ventricle', 'Left-Inf-Lat-Vent', 128 '3rd-Ventricle', '4th-Ventricle', 'CSF', 129 'Right-Lateral-Ventricle', 'Right-Inf-Lat-Vent', '5th-Ventricle' ] 130 131# what to show 132SHOW_ORIG = 1 133SHOW_RANK = 2 134SHOW_ALL_ORIG = 4 135 136class MyInterface: 137 """interface class for MyLibrary (whatever that is) 138 """ 139 def __init__(self, verb=1): 140 # main variables 141 self.valid_opts = None 142 self.user_opts = None 143 144 # control 145 self.verb = 1 146 147 # logfile name parsing 148 self.logfile = '' 149 self.labels = [] 150 self.show_vals = SHOW_ORIG | SHOW_RANK 151 152 # initialize valid_opts 153 self.valid_opts = self.get_valid_opts() 154 155 def get_valid_opts(self): 156 vopts = OL.OptionList('valid opts') 157 158 # short, terminal arguments 159 vopts.add_opt('-help', 0, [], helpstr='display program help') 160 vopts.add_opt('-help_examples', 0, [], helpstr='display program examples') 161 vopts.add_opt('-hist', 0, [], helpstr='display the modification history') 162 vopts.add_opt('-ver', 0, [], helpstr='display the current version number') 163 164 # general options 165 vopts.add_opt('-labels', -1, [], 166 helpstr='specify labels to search for') 167 vopts.add_opt('-logfile', 1, [], 168 helpstr='specify input label table log file') 169 vopts.add_opt('-show_all_orig', 0, [], 170 helpstr='show all unranked indices, even if not found') 171 vopts.add_opt('-show_orig', 0, [], 172 helpstr='only show unranked indices') 173 vopts.add_opt('-show_rank', 0, [], 174 helpstr='only show ranked indices') 175 vopts.add_opt('-verb', 1, [], helpstr='set the verbose level (def=1)') 176 177 vopts.sort() 178 179 return vopts 180 181 def process_options(self): 182 """return 1 on valid and exit 183 return 0 on valid and continue 184 return -1 on invalid 185 """ 186 187 argv = sys.argv 188 189 # process any optlist_ options 190 self.valid_opts.check_special_opts(argv) 191 192 # process terminal options without the option_list interface 193 # (so that errors are not reported) 194 195 # if no arguments are given, do default processing 196 if '-help' in argv or len(argv) < 2: 197 show_help() 198 return 1 199 200 if '-help_examples' in argv: 201 print(g_help_examples) 202 return 1 203 204 if '-hist' in argv: 205 print(g_history) 206 return 1 207 208 if '-show_valid_opts' in argv: 209 self.valid_opts.show('', 1) 210 return 1 211 212 if '-ver' in argv: 213 print(g_version) 214 return 1 215 216 # ============================================================ 217 # read options specified by the user 218 self.user_opts = OL.read_options(argv, self.valid_opts) 219 uopts = self.user_opts # convenience variable 220 if not uopts: return -1 # error condition 221 222 # ------------------------------------------------------------ 223 # process verb first 224 225 val, err = uopts.get_type_opt(int, '-verb') 226 if val != None and not err: self.verb = val 227 228 # ------------------------------------------------------------ 229 # process options sequentially, to make them like a script 230 errs = 0 231 for opt in self.user_opts.olist: 232 # check for anything to skip 233 if opt.name == '-verb': pass 234 235 elif opt.name == '-logfile': 236 self.logfile, err = uopts.get_string_opt('', opt=opt) 237 if self.logfile == None or err: 238 print('** failed to read -logfile name') 239 errs +=1 240 241 elif opt.name == '-labels': 242 self.labels, err = uopts.get_string_list('', opt=opt) 243 if self.labels == None or err: 244 print('** option -labels: failed to process option') 245 errs +=1 246 247 elif opt.name == '-show_all_orig': 248 pass # process later 249 250 elif opt.name == '-show_orig': 251 self.show_vals = SHOW_ORIG 252 253 elif opt.name == '-show_rank': 254 self.show_vals = SHOW_RANK 255 256 if self.user_opts.find_opt('-show_all_orig'): 257 if not (self.show_vals & SHOW_ORIG): 258 print("** -show_all_orig requires showing orig") 259 errs += 1 260 self.show_vals |= SHOW_ALL_ORIG 261 262 # allow early and late error returns 263 if errs: return -1 264 265 # ------------------------------------------------------------ 266 ## apply any trailing logic 267 268 if self.logfile == '': 269 print('** missing -logfile option') 270 errs += 1 271 272 if len(self.labels) < 1: 273 print('** missing -labels to search for') 274 errs += 1 275 276 if errs: return -1 277 278 return 0 279 280 def expand_labels(self): 281 """replace known label groups with actual labels 282 FS_white_matter - white matter labels 283 FS_ventricles - fentricle labels 284 """ 285 newlabs = [] 286 for label in self.labels: 287 if label == 'FS_white_matter': newlabs.extend(g_fs_wm_labels) 288 elif label == 'FS_ventricles': newlabs.extend(g_fs_vent_labels) 289 else: newlabs.append(label) 290 291 self.labels = newlabs 292 293 def make_label_list(self, lines): 294 """create list of [label, orig, rank] values 295 296 only allow lines that look like: XXX INTEGER LABEL 297 """ 298 299 if self.verb: print() 300 301 llist = [] 302 for line in lines: 303 lvals = line.split() 304 if len(lvals) < 3: continue 305 306 rankstr = lvals[0] 307 origstr = lvals[1] 308 label = lvals[2] 309 310 # view in reverse order, to not waste time 311 if label not in self.labels: continue 312 313 try: vv = int(origstr) 314 except: continue 315 316 # rankstring might be an integer, else not_found and finally None 317 try: vv = int(rankstr) 318 except: rankstr = 'not_found' 319 320 if self.verb: print('%-35s %-5s -> %s' % (label, origstr, rankstr)) 321 322 if rankstr == 'not_found': rankstr = None 323 llist.append([label, origstr, rankstr]) 324 325 if self.verb: print() 326 327 return llist 328 329 330 def process_logfile(self): 331 """main function to process logfile 332 """ 333 334 # replace any known label groups 335 self.expand_labels() 336 337 # read logfile 338 inlines = UTIL.read_text_file(self.logfile, lines=1, verb=self.verb) 339 340 llist = self.make_label_list(inlines) 341 342 if self.show_vals & SHOW_RANK: 343 if self.verb: pref = 'rank : ' 344 else: pref = '' 345 lshow = [ll[2] for ll in llist if ll[2] != None] 346 print('%s%s' % (pref, ','.join(lshow))) 347 if self.show_vals & SHOW_ORIG: 348 if self.verb: pref = 'orig : ' 349 else: pref = '' 350 if self.show_vals & SHOW_ALL_ORIG: 351 lshow = [ll[1] for ll in llist] 352 else: 353 lshow = [ll[1] for ll in llist if ll[2] != None] 354 print('%s%s' % (pref, ','.join(lshow))) 355 356 return 0 357 358def main(): 359 me = MyInterface() 360 if not me: return 1 361 362 rv = me.process_options() 363 if rv > 0: return 0 # exit with success 364 if rv < 0: # exit with error status 365 print('** failed to process options...') 366 return 1 367 368 if me.process_logfile(): return 1 369 370 return 0 371 372if __name__ == '__main__': 373 sys.exit(main()) 374 375 376