def group_options(options, timepoint): options = copy.deepcopy(options) if options.mbm.lsq6.target_type == TargetType.pride_of_models: options = copy.deepcopy(options) targets = get_closest_model_from_pride_of_models(pride_of_models_dict=get_pride_of_models_mapping( pride_csv=options.mbm.lsq6.target_file, output_dir=options.application.output_directory, pipeline_name=options.application.pipeline_name), time_point=timepoint) options.mbm.lsq6 = options.mbm.lsq6.replace(target_type=TargetType.initial_model, target_file=targets.registration_standard.path) # resolution = (options.registration.resolution # or get_resolution_from_file(targets.registration_standard.path)) # options.registration = options.registration.replace(resolution=resolution) # FIXME use of registration_standard here is quite wrong ... # part of the trouble is that mbm calls registration_targets itself, # so we can't send this RegistrationTargets to `mbm` directly ... # one option: add yet another optional arg to `mbm` ... else: targets = s.defer(registration_targets(lsq6_conf=options.mbm.lsq6, app_conf=options.application, reg_conf=options.registration, first_input_file=imgs.filename.iloc[0])) resolution = (options.registration.resolution or get_resolution_from_file(targets.registration_standard.path)) # This must happen after calling registration_targets otherwise it will resample to options.registration.resolution options.registration = options.registration.replace(resolution=resolution) return options
def group_options(options, group): options = copy.deepcopy(options) if options.mbm.lsq6.target_type == TargetType.pride_of_models: targets = get_closest_model_from_pride_of_models( pride_of_models_dict=pride_of_models_mapping, time_point=group) options.mbm.lsq6 = options.mbm.lsq6.replace( target_type=TargetType.initial_model, target_file=targets.registration_standard.path) else: # this will ensure that all groups have the same resolution -- is it necessary? targets = s.defer( registration_targets( lsq6_conf=options.mbm.lsq6, app_conf=options.application, reg_conf=options.registration, first_input_file=grouped_files_df.file.iloc[0])) resolution = (options.registration.resolution or get_resolution_from_file( targets.registration_standard.path)) # This must happen after calling registration_targets otherwise it will resample to options.registration.resolution options.registration = options.registration.replace( resolution=resolution) # no need to check common space settings here since they're turned off at the parser level # (a bit strange) return options
def lsq6_pipeline(options): # TODO could also allow pluggable pipeline parts e.g. LSQ6 could be substituted out for the modified LSQ6 # for the kidney tips, etc... output_dir = options.application.output_directory pipeline_name = options.application.pipeline_name # TODO this is tedious and annoyingly similar to the registration chain and MBM ... lsq6_dir = os.path.join(output_dir, pipeline_name + "_lsq6") imgs = get_imgs(options.application) s = Stages() # FIXME: why do we have to call registration_targets *outside* of lsq6_nuc_inorm? is it just because of the extra # options required? targets = s.defer( registration_targets(lsq6_conf=options.lsq6, app_conf=options.application, reg_conf=options.registration, first_input_file=imgs[0].path)) # TODO this is quite tedious and duplicates stuff in the registration chain ... resolution = (options.registration.resolution or get_resolution_from_file( targets.registration_standard.path)) # This must happen after calling registration_targets otherwise it will resample to options.registration.resolution options.registration = options.registration.replace(resolution=resolution) lsq6_result = s.defer( lsq6_nuc_inorm(imgs=imgs, resolution=resolution, registration_targets=targets, lsq6_dir=lsq6_dir, lsq6_options=options.lsq6)) return Result(stages=s, output=lsq6_result)
def LSQ12_pipeline(options): output_dir = options.application.output_directory pipeline_name = options.application.pipeline_name # TODO this is tedious and annoyingly similar to the registration chain and MBM and LSQ6 ... processed_dir = os.path.join(output_dir, pipeline_name + "_processed") lsq12_dir = os.path.join(output_dir, pipeline_name + "_lsq12") resolution = ( options.registration. resolution # TODO does using the finest resolution here make sense? or min( [get_resolution_from_file(f) for f in options.application.files])) imgs = [ MincAtom(f, pipeline_sub_dir=processed_dir) for f in options.application.files ] # determine LSQ12 settings by overriding defaults with # any settings present in protocol file, if it exists # could add a hook to print a message announcing completion, output files, # add more stages here to make a CSV return lsq12_pairwise(imgs, lsq12_conf=options.lsq12, lsq12_dir=lsq12_dir, resolution=resolution)
def lsq6_pipeline(options): # TODO could also allow pluggable pipeline parts e.g. LSQ6 could be substituted out for the modified LSQ6 # for the kidney tips, etc... output_dir = options.application.output_directory pipeline_name = options.application.pipeline_name # TODO this is tedious and annoyingly similar to the registration chain and MBM ... lsq6_dir = os.path.join(output_dir, pipeline_name + "_lsq6") processed_dir = os.path.join(output_dir, pipeline_name + "_processed") imgs = get_imgs(options.application) s = Stages() # TODO this is quite tedious and duplicates stuff in the registration chain ... resolution = (options.registration.resolution or get_resolution_from_file( s.defer(registration_targets(lsq6_conf=options.lsq6, app_conf=options.application, reg_conf=options.registration)).registration_standard.path)) # FIXME: why do we have to call registration_targets *outside* of lsq6_nuc_inorm? is it just because of the extra # options required? targets = s.defer(registration_targets(lsq6_conf=options.lsq6, app_conf=options.application, reg_conf=options.registration, first_input_file=imgs[0])) # This must happen after calling registration_targets otherwise it will resample to options.registration.resolution options.registration = options.registration.replace(resolution=resolution) lsq6_result = s.defer(lsq6_nuc_inorm(imgs=imgs, resolution=resolution, registration_targets=targets, lsq6_dir=lsq6_dir, lsq6_options=options.lsq6)) return Result(stages=s, output=lsq6_result)
def NLIN_pipeline(options): # if options.application.files is None: # raise ValueError("Please, some files! (or try '--help')") # TODO make a util procedure for this output_dir = options.application.output_directory pipeline_name = options.application.pipeline_name # TODO this is tedious and annoyingly similar to the registration chain and MBM and LSQ6 ... processed_dir = os.path.join(output_dir, pipeline_name + "_processed") nlin_dir = os.path.join(output_dir, pipeline_name + "_nlin") resolution = (options.registration.resolution # TODO does using the finest resolution here make sense? or min([get_resolution_from_file(f) for f in options.application.files])) imgs = get_imgs(options.application) # imgs = [MincAtom(f, pipeline_sub_dir=processed_dir) for f in options.application.files] # determine NLIN settings by overriding defaults with # any settings present in protocol file, if it exists # could add a hook to print a message announcing completion, output files, # add more stages here to make a CSV initial_target_mask = MincAtom(options.nlin.target_mask) if options.nlin.target_mask else None initial_target = MincAtom(options.nlin.target, mask=initial_target_mask) full_hierarchy = get_nonlinear_configuration_from_options(nlin_protocol=options.nlin.nlin_protocol, reg_method=options.nlin.reg_method, file_resolution=resolution) s = Stages() nlin_result = s.defer(nlin_build_model(imgs, initial_target=initial_target, conf=full_hierarchy, nlin_dir=nlin_dir)) # TODO return these? inverted_xfms = [s.defer(invert_xfmhandler(xfm)) for xfm in nlin_result.output] if options.stats.calc_stats: # TODO: put the stats part behind a flag ... determinants = [s.defer(determinants_at_fwhms( xfm=inv_xfm, inv_xfm=xfm, blur_fwhms=options.stats.stats_kernels)) for xfm, inv_xfm in zip(nlin_result.output, inverted_xfms)] return Result(stages=s, output=Namespace(nlin_xfms=nlin_result, avg_img=nlin_result.avg_img, determinants=determinants)) else: # there's no consistency in what gets returned, yikes ... return Result(stages=s, output=Namespace(nlin_xfms=nlin_result, avg_img=nlin_result.avg_img))
def NLIN_pipeline(options): if options.application.files is None: raise ValueError("Please, some files! (or try '--help')") # TODO make a util procedure for this output_dir = options.application.output_directory pipeline_name = options.application.pipeline_name # TODO this is tedious and annoyingly similar to the registration chain and MBM and LSQ6 ... processed_dir = os.path.join(output_dir, pipeline_name + "_processed") nlin_dir = os.path.join(output_dir, pipeline_name + "_nlin") resolution = (options.registration.resolution # TODO does using the finest resolution here make sense? or min([get_resolution_from_file(f) for f in options.application.files])) imgs = [MincAtom(f, pipeline_sub_dir=processed_dir) for f in options.application.files] # determine NLIN settings by overriding defaults with # any settings present in protocol file, if it exists # could add a hook to print a message announcing completion, output files, # add more stages here to make a CSV initial_target_mask = MincAtom(options.nlin.target_mask) if options.nlin.target_mask else None initial_target = MincAtom(options.nlin.target, mask=initial_target_mask) full_hierarchy = get_nonlinear_configuration_from_options(nlin_protocol=options.nlin.nlin_protocol, flag_nlin_protocol=next(iter(options.nlin.flags_.nlin_protocol)), reg_method=options.nlin.reg_method, file_resolution=resolution) s = Stages() nlin_result = s.defer(nlin_build_model(imgs, initial_target=initial_target, conf=full_hierarchy, nlin_dir=nlin_dir)) # TODO return these? inverted_xfms = [s.defer(invert_xfmhandler(xfm)) for xfm in nlin_result.output] if options.stats.calc_stats: # TODO: put the stats part behind a flag ... determinants = [s.defer(determinants_at_fwhms( xfm=inv_xfm, inv_xfm=xfm, blur_fwhms=options.stats.stats_kernels)) for xfm, inv_xfm in zip(nlin_result.output, inverted_xfms)] return Result(stages=s, output=Namespace(nlin_xfms=nlin_result, avg_img=nlin_result.avg_img, determinants=determinants)) else: # there's no consistency in what gets returned, yikes ... return Result(stages=s, output=Namespace(nlin_xfms=nlin_result, avg_img=nlin_result.avg_img))
def LSQ12_pipeline(options): output_dir = options.application.output_directory pipeline_name = options.application.pipeline_name # TODO this is tedious and annoyingly similar to the registration chain and MBM and LSQ6 ... processed_dir = os.path.join(output_dir, pipeline_name + "_processed") lsq12_dir = os.path.join(output_dir, pipeline_name + "_lsq12") resolution = (options.registration.resolution # TODO does using the finest resolution here make sense? or min([get_resolution_from_file(f) for f in options.application.files])) imgs = [MincAtom(f, pipeline_sub_dir=processed_dir) for f in options.application.files] # determine LSQ12 settings by overriding defaults with # any settings present in protocol file, if it exists # could add a hook to print a message announcing completion, output files, # add more stages here to make a CSV return lsq12_pairwise(imgs, lsq12_conf=options.lsq12, lsq12_dir=lsq12_dir, resolution=resolution)
def group_options(options, group): options = copy.deepcopy(options) if options.mbm.lsq6.target_type == TargetType.pride_of_models: targets = get_closest_model_from_pride_of_models(pride_of_models_dict=pride_of_models_mapping, time_point=group) options.mbm.lsq6 = options.mbm.lsq6.replace(target_type=TargetType.initial_model, target_file=targets.registration_standard.path) else: # this will ensure that all groups have the same resolution -- is it necessary? targets = registration_targets(lsq6_conf=options.mbm.lsq6, app_conf=options.application, first_input_file=grouped_files_df.file.iloc[0]) resolution = (options.registration.resolution or get_resolution_from_file(targets.registration_standard.path)) options.registration = options.registration.replace(resolution=resolution) return options
def stage_embryos_pipeline(options): s = Stages() imgs = get_imgs(options.application) rough_volume_imgs = get_volume_estimate(imgs) imgs_and_rough_volume = pd.DataFrame({"mincatom" : imgs, "rough_volume" : pd.Series(rough_volume_imgs, dtype=float)}) check_MINC_input_files([img.path for img in imgs]) output_directory = options.application.output_directory output_sub_dir = os.path.join(output_directory, options.application.pipeline_name + "_4D_atlas") time_points_in_4D_atlas = instances_in_4D_atlas_from_csv(options.staging.staging.csv_4D, output_sub_dir) # we can use the resolution of one of the time points in the 4D atlas # for all the registrations that will be run. resolution = get_resolution_from_file(time_points_in_4D_atlas.loc[0]["mincatom"].orig_path) print(options.staging.lsq12) lsq12_conf = get_linear_configuration_from_options(options.staging.lsq12, transform_type=LinearTransType.lsq12, file_resolution=resolution) nlin_component = get_nonlinear_component(options.staging.nlin.reg_method) # match each of the embryos individually for i in range(imgs_and_rough_volume.shape[0]): s.defer(match_embryo_to_4D_atlas(imgs_and_rough_volume.loc[i], time_points_in_4D_atlas, lsq6_conf=options.staging.lsq6, lsq12_conf=lsq12_conf, nlin_module=nlin_component, resolution=resolution, nlin_options=options.staging.nlin)) return Result(stages=s, output=None)
def mbm(imgs : List[MincAtom], options : MBMConf, prefix : str, output_dir : str = ""): # TODO could also allow pluggable pipeline parts e.g. LSQ6 could be substituted out for the modified LSQ6 # for the kidney tips, etc... # TODO this is tedious and annoyingly similar to the registration chain ... lsq6_dir = os.path.join(output_dir, prefix + "_lsq6") lsq12_dir = os.path.join(output_dir, prefix + "_lsq12") nlin_dir = os.path.join(output_dir, prefix + "_nlin") s = Stages() if len(imgs) == 0: raise ValueError("Please, some files!") # FIXME: why do we have to call registration_targets *outside* of lsq6_nuc_inorm? is it just because of the extra # options required? Also, shouldn't options.registration be a required input (as it contains `input_space`) ...? targets = registration_targets(lsq6_conf=options.mbm.lsq6, app_conf=options.application, first_input_file=imgs[0].path) # TODO this is quite tedious and duplicates stuff in the registration chain ... resolution = (options.registration.resolution or get_resolution_from_file(targets.registration_standard.path)) options.registration = options.registration.replace(resolution=resolution) # FIXME it probably makes most sense if the lsq6 module itself (even within lsq6_nuc_inorm) handles the run_lsq6 # setting (via use of the identity transform) since then this doesn't have to be implemented for every pipeline if options.mbm.lsq6.run_lsq6: lsq6_result = s.defer(lsq6_nuc_inorm(imgs=imgs, resolution=resolution, registration_targets=targets, lsq6_dir=lsq6_dir, lsq6_options=options.mbm.lsq6)) else: # TODO don't actually do this resampling if not required (i.e., if the imgs already have the same grids) identity_xfm = s.defer(param2xfm(out_xfm=FileAtom(name="identity.xfm"))) lsq6_result = [XfmHandler(source=img, target=img, xfm=identity_xfm, resampled=s.defer(mincresample_new(img=img, like=targets.registration_standard, xfm=identity_xfm))) for img in imgs] # what about running nuc/inorm without a linear registration step?? full_hierarchy = get_nonlinear_configuration_from_options(nlin_protocol=options.mbm.nlin.nlin_protocol, reg_method=options.mbm.nlin.reg_method, file_resolution=resolution) lsq12_nlin_result = s.defer(lsq12_nlin_build_model(imgs=[xfm.resampled for xfm in lsq6_result], resolution=resolution, lsq12_dir=lsq12_dir, nlin_dir=nlin_dir, nlin_prefix=prefix, lsq12_conf=options.mbm.lsq12, nlin_conf=full_hierarchy)) inverted_xfms = [s.defer(invert_xfmhandler(xfm)) for xfm in lsq12_nlin_result.output] determinants = s.defer(determinants_at_fwhms( xfms=inverted_xfms, inv_xfms=lsq12_nlin_result.output, blur_fwhms=options.mbm.stats.stats_kernels)) overall_xfms = [s.defer(concat_xfmhandlers([rigid_xfm, lsq12_nlin_xfm])) for rigid_xfm, lsq12_nlin_xfm in zip(lsq6_result, lsq12_nlin_result.output)] output_xfms = (pd.DataFrame({ "rigid_xfm" : lsq6_result, # maybe don't return this if LSQ6 not run?? "lsq12_nlin_xfm" : lsq12_nlin_result.output, "overall_xfm" : overall_xfms })) # we could `merge` the determinants with this table, but preserving information would cause lots of duplication # of the transforms (or storing determinants in more columns, but iterating over dynamically known columns # seems a bit odd ...) # TODO transpose these fields?}) #avg_img=lsq12_nlin_result.avg_img, # inconsistent w/ WithAvgImgs[...]-style outputs # "determinants" : determinants }) #output.avg_img = lsq12_nlin_result.avg_img #output.determinants = determinants # TODO temporary - remove once incorporated properly into `output` proper # TODO add more of lsq12_nlin_result? # FIXME: this needs to go outside of the `mbm` function to avoid being run from within other pipelines (or # those other pipelines need to turn off this option) # TODO return some MAGeT stuff from MBM function ?? # if options.mbm.mbm.run_maget: # import copy # maget_options = copy.deepcopy(options) #Namespace(maget=options) # #maget_options # #maget_options.maget = maget_options.mbm # #maget_options.execution = options.execution # #maget_options.application = options.application # maget_options.maget = options.mbm.maget # del maget_options.mbm # # s.defer(maget([xfm.resampled for xfm in lsq6_result], # options=maget_options, # prefix="%s_MAGeT" % prefix, # output_dir=os.path.join(output_dir, prefix + "_processed"))) # should also move outside `mbm` function ... #if options.mbm.thickness.run_thickness: # if not options.mbm.segmentation.run_maget: # warnings.warn("MAGeT files (atlases, protocols) are needed to run thickness calculation.") # # run MAGeT to segment the nlin average: # import copy # maget_options = copy.deepcopy(options) #Namespace(maget=options) # maget_options.maget = options.mbm.maget # del maget_options.mbm # segmented_avg = s.defer(maget(imgs=[lsq12_nlin_result.avg_img], # options=maget_options, # output_dir=os.path.join(options.application.output_directory, # prefix + "_processed"), # prefix="%s_thickness_MAGeT" % prefix)).ix[0].img # thickness = s.defer(cortical_thickness(xfms=pd.Series(inverted_xfms), atlas=segmented_avg, # label_mapping=FileAtom(options.mbm.thickness.label_mapping), # atlas_fwhm=0.56, thickness_fwhm=0.56)) # TODO magic fwhms # # TODO write CSV -- should `cortical_thickness` do this/return a table? # FIXME: this needs to go outside of the `mbm` function to avoid being run from within other pipelines (or # those other pipelines need to turn off this option) if options.mbm.common_space.do_common_space_registration: warnings.warn("This feature is experimental ...") if not options.mbm.common_space.common_space_model: raise ValueError("No common space template provided!") # TODO allow lsq6 registration as well ... common_space_model = MincAtom(options.mbm.common_space.common_space_model, pipeline_sub_dir=os.path.join(options.application.output_directory, options.application.pipeline_name + "_processed")) # TODO allow different lsq12/nlin config params than the ones used in MBM ... # WEIRD ... see comment in lsq12_nlin code ... nlin_conf = full_hierarchy.confs[-1] if isinstance(full_hierarchy, MultilevelMincANTSConf) else full_hierarchy # also weird that we need to call get_linear_configuration_from_options here ... ? lsq12_conf = get_linear_configuration_from_options(conf=options.mbm.lsq12, transform_type=LinearTransType.lsq12, file_resolution=resolution) xfm_to_common = s.defer(lsq12_nlin(source=lsq12_nlin_result.avg_img, target=common_space_model, lsq12_conf=lsq12_conf, nlin_conf=nlin_conf, resample_source=True)) model_common = s.defer(mincresample_new(img=lsq12_nlin_result.avg_img, xfm=xfm_to_common.xfm, like=common_space_model, postfix="_common")) overall_xfms_common = [s.defer(concat_xfmhandlers([rigid_xfm, nlin_xfm, xfm_to_common])) for rigid_xfm, nlin_xfm in zip(lsq6_result, lsq12_nlin_result.output)] xfms_common = [s.defer(concat_xfmhandlers([nlin_xfm, xfm_to_common])) for nlin_xfm in lsq12_nlin_result.output] output_xfms = output_xfms.assign(xfm_common=xfms_common, overall_xfm_common=overall_xfms_common) log_nlin_det_common, log_full_det_common = [dets.map(lambda d: s.defer(mincresample_new( img=d, xfm=xfm_to_common.xfm, like=common_space_model, postfix="_common", extra_flags=("-keep_real_range",), interpolation=Interpolation.nearest_neighbour))) for dets in (determinants.log_nlin_det, determinants.log_full_det)] determinants = determinants.assign(log_nlin_det_common=log_nlin_det_common, log_full_det_common=log_full_det_common) output = Namespace(avg_img=lsq12_nlin_result.avg_img, xfms=output_xfms, determinants=determinants) if options.mbm.common_space.do_common_space_registration: output.model_common = model_common return Result(stages=s, output=output)
def mbm(imgs: List[MincAtom], options: MBMConf, prefix: str, output_dir: str = "", with_maget: bool = True): # TODO could also allow pluggable pipeline parts e.g. LSQ6 could be substituted out for the modified LSQ6 # for the kidney tips, etc... # TODO this is tedious and annoyingly similar to the registration chain ... lsq6_dir = os.path.join(output_dir, prefix + "_lsq6") lsq12_dir = os.path.join(output_dir, prefix + "_lsq12") nlin_dir = os.path.join(output_dir, prefix + "_nlin") s = Stages() if len(imgs) == 0: raise ValueError("Please, some files!") # FIXME: why do we have to call registration_targets *outside* of lsq6_nuc_inorm? is it just because of the extra # options required? Also, shouldn't options.registration be a required input (as it contains `input_space`) ...? targets = s.defer( registration_targets(lsq6_conf=options.mbm.lsq6, app_conf=options.application, reg_conf=options.registration, first_input_file=imgs[0].path)) # TODO this is quite tedious and duplicates stuff in the registration chain ... resolution = (options.registration.resolution or get_resolution_from_file( targets.registration_standard.path)) options.registration = options.registration.replace(resolution=resolution) # FIXME: this needs to go outside of the `mbm` function to avoid being run from within other pipelines (or # those other pipelines need to turn off this option) if with_maget: if options.mbm.segmentation.run_maget or options.mbm.maget.maget.mask: # temporary fix...? if options.mbm.maget.maget.mask and not options.mbm.segmentation.run_maget: # which means that --no-run-maget was specified if options.mbm.maget.maget.atlas_lib == None: # clearly you do not want to run MAGeT at any point in this pipeline err_msg_maget = "\nYou specified not to run MAGeT using the " \ "--no-run-maget flag. However, the code also " \ "wants to use MAGeT to generate masks for your " \ "input files after the 6 parameter alignment (lsq6). " \ "Because you did not specify a MAGeT atlas library " \ "this can not be done. \nTo run the pipeline without " \ "using MAGeT to mask your input files, please also " \ "specify: \n--maget-no-mask\n" raise ValueError(err_msg_maget) import copy maget_options = copy.deepcopy(options) #Namespace(maget=options) #maget_options #maget_options.maget = maget_options.mbm #maget_options.execution = options.execution #maget_options.application = options.application #maget_options.application.output_directory = os.path.join(options.application.output_directory, "segmentation") maget_options.maget = options.mbm.maget fixup_maget_options(maget_options=maget_options.maget, nlin_options=maget_options.mbm.nlin, lsq12_options=maget_options.mbm.lsq12) del maget_options.mbm #def with_new_output_dir(img : MincAtom): #img = copy.copy(img) #img.pipeline_sub_dir = img.pipeline_sub_dir + img.output_dir #img. #return img.newname_with_suffix(suffix="", subdir="segmentation") # FIXME it probably makes most sense if the lsq6 module itself (even within lsq6_nuc_inorm) handles the run_lsq6 # setting (via use of the identity transform) since then this doesn't have to be implemented for every pipeline if options.mbm.lsq6.run_lsq6: lsq6_result = s.defer( lsq6_nuc_inorm(imgs=imgs, resolution=resolution, registration_targets=targets, lsq6_dir=lsq6_dir, lsq6_options=options.mbm.lsq6)) else: # FIXME the code shouldn't branch here based on run_lsq6 (which should probably # be part of the lsq6 options rather than the MBM ones; see comments on #287. # TODO don't actually do this resampling if not required (i.e., if the imgs already have the same grids)?? # however, for now need to add the masks: identity_xfm = s.defer( param2xfm( out_xfm=FileAtom(name=os.path.join(lsq6_dir, 'tmp', "id.xfm"), pipeline_sub_dir=lsq6_dir, output_sub_dir='tmp'))) lsq6_result = [ XfmHandler(source=img, target=img, xfm=identity_xfm, resampled=s.defer( mincresample_new(img=img, like=targets.registration_standard, xfm=identity_xfm))) for img in imgs ] # what about running nuc/inorm without a linear registration step?? if with_maget and options.mbm.maget.maget.mask: masking_imgs = copy.deepcopy([xfm.resampled for xfm in lsq6_result]) masked_img = (s.defer( maget_mask(imgs=masking_imgs, resolution=resolution, maget_options=maget_options.maget, pipeline_sub_dir=os.path.join( options.application.output_directory, "%s_atlases" % prefix)))) masked_img.index = masked_img.apply(lambda x: x.path) # replace any masks of the resampled images with the newly created masks: for xfm in lsq6_result: xfm.resampled = masked_img.loc[xfm.resampled.path] elif with_maget: warnings.warn( "Not masking your images from atlas masks after LSQ6 alignment ... probably not what you want " "(this can have negative effects on your registration and statistics)" ) #full_hierarchy = get_nonlinear_configuration_from_options(nlin_protocol=options.mbm.nlin.nlin_protocol, # flag_nlin_protocol=next(iter(options.mbm.nlin.flags_.nlin_protocol)), # reg_method=options.mbm.nlin.reg_method, # file_resolution=resolution) #I = TypeVar("I") #X = TypeVar("X") #def wrap_minc(nlin_module: NLIN[I, X]) -> type[NLIN[MincAtom, XfmAtom]]: # class N(NLIN[MincAtom, XfmAtom]): pass # TODO now the user has to call get_nonlinear_component followed by parse_<...>; previously various things # like lsq12_nlin_pairwise all branched on the reg_method so one didn't have to call get_nonlinear_component; # they could still do this if it can be done safety (i.e., not breaking assumptions of various nonlinear units) nlin_module = get_nonlinear_component( reg_method=options.mbm.nlin.reg_method) nlin_build_model_component = get_model_building_procedure( options.mbm.nlin.reg_strategy, # was: model_building.reg_strategy reg_module=nlin_module) # does this belong here? # def model_building_with_initial_target_generation(prelim_model_building_component, # final_model_building_component): # class C(final_model_building_component): # @staticmethod # def build_model(imgs, # conf : BuildModelConf, # nlin_dir, # nlin_prefix, # initial_target, # output_name_wo_ext = None): pass # # return C #if options.mbm.model_building.prelim_reg_strategy is not None: # prelim_nlin_build_model_component = get_model_building_procedure(options.mbm.model_building.prelim_reg_strategy, # reg_module=nlin_module) # nlin_build_model_component = model_building_with_initial_target_generation( # final_model_building_component=nlin_build_model_component, # prelim_model_building_component=prelim_nlin_build_model_component) # TODO don't use name 'x_module' for something that's technically not a module ... perhaps unit/component? # TODO tedious: why can't parse_build_model_protocol handle the null protocol case? is this something we want? nlin_conf = (nlin_build_model_component.parse_build_model_protocol( options.mbm.nlin.nlin_protocol, resolution=resolution) if options.mbm.nlin.nlin_protocol is not None else nlin_build_model_component.get_default_build_model_conf( resolution=resolution)) lsq12_nlin_result = s.defer( lsq12_nlin_build_model( nlin_module=nlin_build_model_component, imgs=[xfm.resampled for xfm in lsq6_result], lsq12_dir=lsq12_dir, nlin_dir=nlin_dir, nlin_prefix=prefix, use_robust_averaging=options.mbm.nlin.use_robust_averaging, resolution=resolution, lsq12_conf=options.mbm.lsq12, nlin_conf=nlin_conf)) #options.mbm.nlin inverted_xfms = [ s.defer(invert_xfmhandler(xfm)) for xfm in lsq12_nlin_result.output ] if options.mbm.stats.stats_kernels: determinants = s.defer( determinants_at_fwhms(xfms=inverted_xfms, inv_xfms=lsq12_nlin_result.output, blur_fwhms=options.mbm.stats.stats_kernels)) else: determinants = None overall_xfms = [ s.defer(concat_xfmhandlers([rigid_xfm, lsq12_nlin_xfm])) for rigid_xfm, lsq12_nlin_xfm in zip(lsq6_result, lsq12_nlin_result.output) ] output_xfms = ( pd.DataFrame({ "rigid_xfm": lsq6_result, # maybe don't return this if LSQ6 not run?? "lsq12_nlin_xfm": lsq12_nlin_result.output, "overall_xfm": overall_xfms })) # we could `merge` the determinants with this table, but preserving information would cause lots of duplication # of the transforms (or storing determinants in more columns, but iterating over dynamically known columns # seems a bit odd ...) # TODO transpose these fields?}) #avg_img=lsq12_nlin_result.avg_img, # inconsistent w/ WithAvgImgs[...]-style outputs # "determinants" : determinants }) #output.avg_img = lsq12_nlin_result.avg_img #output.determinants = determinants # TODO temporary - remove once incorporated properly into `output` proper # TODO add more of lsq12_nlin_result? # FIXME moved above rest of registration for debugging ... shouldn't use and destructively modify lsq6_result!!! if with_maget and options.mbm.segmentation.run_maget: maget_options = copy.deepcopy(maget_options) maget_options.maget.maget.mask = maget_options.maget.maget.mask_only = False # already done above # use the original masks here otherwise the masking step will be re-run due to the previous masking run's # masks having been applied to the input images: maget_result = s.defer( maget( [xfm.resampled for xfm in lsq6_result], #[xfm.resampled for _ix, xfm in mbm_result.xfms.rigid_xfm.iteritems()], options=maget_options, prefix="%s_MAGeT" % prefix, output_dir=os.path.join(output_dir, prefix + "_processed"))) # FIXME add pipeline dir to path and uncomment! #maget.to_csv(path_or_buf="segmentations.csv", columns=['img', 'voted_labels']) # TODO return some MAGeT stuff from MBM function ?? # if options.mbm.mbm.run_maget: # import copy # maget_options = copy.deepcopy(options) #Namespace(maget=options) # #maget_options # #maget_options.maget = maget_options.mbm # #maget_options.execution = options.execution # #maget_options.application = options.application # maget_options.maget = options.mbm.maget # del maget_options.mbm # # s.defer(maget([xfm.resampled for xfm in lsq6_result], # options=maget_options, # prefix="%s_MAGeT" % prefix, # output_dir=os.path.join(output_dir, prefix + "_processed"))) # should also move outside `mbm` function ... #if options.mbm.thickness.run_thickness: # if not options.mbm.segmentation.run_maget: # warnings.warn("MAGeT files (atlases, protocols) are needed to run thickness calculation.") # # run MAGeT to segment the nlin average: # import copy # maget_options = copy.deepcopy(options) #Namespace(maget=options) # maget_options.maget = options.mbm.maget # del maget_options.mbm # segmented_avg = s.defer(maget(imgs=[lsq12_nlin_result.avg_img], # options=maget_options, # output_dir=os.path.join(options.application.output_directory, # prefix + "_processed"), # prefix="%s_thickness_MAGeT" % prefix)).ix[0].img # thickness = s.defer(cortical_thickness(xfms=pd.Series(inverted_xfms), atlas=segmented_avg, # label_mapping=FileAtom(options.mbm.thickness.label_mapping), # atlas_fwhm=0.56, thickness_fwhm=0.56)) # TODO magic fwhms # # TODO write CSV -- should `cortical_thickness` do this/return a table? output = Namespace(avg_img=lsq12_nlin_result.avg_img, xfms=output_xfms, determinants=determinants) if with_maget and options.mbm.segmentation.run_maget: output.maget_result = maget_result nlin_maget = ( s.defer( maget( [lsq12_nlin_result.avg_img], #[xfm.resampled for _ix, xfm in mbm_result.xfms.rigid_xfm.iteritems()], options=maget_options, prefix="%s_nlin_MAGeT" % prefix, output_dir=os.path.join( output_dir, prefix + "_processed")))).iloc[0] #.voted_labels #output.avg_img.mask = nlin_maget.mask # makes more sense, but might have weird effects elsewhere output.avg_img.labels = nlin_maget.labels return Result(stages=s, output=output)
def NLIN_pipeline(options): output_dir = options.application.output_directory pipeline_name = options.application.pipeline_name # TODO this is tedious and annoyingly similar to the registration chain and MBM and LSQ6 ... processed_dir = os.path.join(output_dir, pipeline_name + "_processed") nlin_dir = os.path.join(output_dir, pipeline_name + "_nlin") resolution = ( options.registration. resolution # TODO does using the finest resolution here make sense? or min( [get_resolution_from_file(f) for f in options.application.files])) imgs = get_imgs(options.application) initial_target_mask = MincAtom( options.nlin.target_mask) if options.nlin.target_mask else None initial_target = MincAtom(options.nlin.target, mask=initial_target_mask) nlin_module = get_nonlinear_component(reg_method=options.nlin.reg_method) nlin_build_model_component = get_model_building_procedure( options.nlin.reg_strategy, reg_module=nlin_module) nlin_conf = (nlin_build_model_component.parse_build_model_protocol( options.nlin.nlin_protocol, resolution=resolution) if options.nlin.nlin_protocol is not None else nlin_build_model_component.get_default_build_model_conf( resolution=resolution)) s = Stages() nlin_result = s.defer( nlin_build_model_component.build_model( imgs=imgs, initial_target=initial_target, conf=nlin_conf, nlin_dir=nlin_dir, use_robust_averaging=options.nlin.use_robust_averaging, nlin_prefix="")) inverted_xfms = [ s.defer(invert_xfmhandler(xfm)) for xfm in nlin_result.output ] if options.stats.calc_stats: determinants = s.defer( determinants_at_fwhms(xfms=inverted_xfms, inv_xfms=nlin_result.output, blur_fwhms=options.stats.stats_kernels)) return Result(stages=s, output=Namespace(nlin_xfms=nlin_result, avg_img=nlin_result.avg_img, determinants=determinants)) else: # there's no consistency in what gets returned, yikes ... return Result(stages=s, output=Namespace(nlin_xfms=nlin_result, avg_img=nlin_result.avg_img))
# we can not use the registration_targets() function. The pride_of_models # works in a fairly different way, so we will separate out that option. if options.lsq6.target_type == TargetType.pride_of_models: pride_of_models_mapping = get_pride_of_models_mapping(options.lsq6.target_file, options.application.output_directory, options.application.pipeline_name) # all initial models that are part of the pride of models must have # the same resolution (it's currently a requirement). So we can get the # resolution from any of the RegistrationTargets: random_key = list(pride_of_models_mapping)[0] file_for_resolution = pride_of_models_mapping[random_key].registration_standard.path else: file_for_resolution = registration_targets(lsq6_conf=options.lsq6, app_conf=options.application).registration_standard.path options.registration = options.registration.replace( resolution=get_resolution_from_file(file_for_resolution)) # *** *** *** *** *** *** *** *** *** chain_result = chain(options) chain_output = chain_result.output # write some useful CSVs: analysis_csv = open("".join([options.application.pipeline_name, "_analysis_files.csv"]), "w") print("subject_id, timepoint, fwhm, log_det_absolute_second_level, " "log_det_relative_second_level, log_det_absolute_first_level, " "log_det_relative_first_level", file=analysis_csv) for subj_id, subject in chain_output.determinants_from_common_avg_to_subject.items(): for timept, img in subject.time_pt_dict.items(): # these rows contain full_det, fwhm, inv_xfm, log_full_det, log_nlin_det,... (some more) for i, row in img.iterrows():