示例#1
0
    def register(
        source: MincAtom,
        target: MincAtom,
        conf: ANTSConf,
        initial_source_transform: Optional[XfmAtom] = None,
        transform_name_wo_ext: str = None,
        generation: int = None,
        resample_source: bool = False,
        #resample_name_wo_ext: Optional[str] = None,
        resample_subdir: str = "resampled"
    ) -> Result[XfmHandler]:
        """
    ...
    transform_name_wo_ext -- to use for the output transformation (without the extension)
    generation            -- if provided, the transformation name will be:
                             source.filename_wo_ext + "_ANTS_nlin-" + generation
    resample_source       -- whether or not to resample the source file   
    
    Construct a single call to ANTS.
    Also does blurring according to the specified options
    since the cost function might use these.
    """
        s = Stages()

        if initial_source_transform is not None:
            raise ValueError("ANTs doesn't accept an initial transform")

        # if we resample the source, and place it in the "tmp" directory, we should do
        # the same with the transformation that is created:
        trans_output_dir = "transforms"
        if resample_source and resample_subdir == "tmp":
            trans_output_dir = "tmp"

        if transform_name_wo_ext:
            name = os.path.join(source.pipeline_sub_dir, source.output_sub_dir,
                                trans_output_dir,
                                "%s.xfm" % (transform_name_wo_ext))
        elif generation is not None:
            name = os.path.join(
                source.pipeline_sub_dir, source.output_sub_dir,
                trans_output_dir,
                "%s_ANTS_nlin-%s.xfm" % (source.filename_wo_ext, generation))
        else:
            name = os.path.join(
                source.pipeline_sub_dir, source.output_sub_dir,
                trans_output_dir, "%s_ANTS_to_%s.xfm" %
                (source.filename_wo_ext, target.filename_wo_ext))
        out_xfm = XfmAtom(name=name,
                          pipeline_sub_dir=source.pipeline_sub_dir,
                          output_sub_dir=source.output_sub_dir)

        similarity_cmds = []  # type: List[str]
        similarity_inputs = set()  # type: Set[MincAtom]
        # TODO: similarity_inputs should be a set, but `MincAtom`s aren't hashable
        for sim_metric_conf in conf.sim_metric_confs:
            if sim_metric_conf.use_gradient_image:
                if sim_metric_conf.blur is not None:
                    gradient_blur_resolution = sim_metric_conf.blur
                elif conf.file_resolution is not None:
                    gradient_blur_resolution = conf.file_resolution
                else:
                    gradient_blur_resolution = None
                    raise ValueError(
                        "A similarity metric in the ANTS configuration "
                        "wants to use the gradients, but I know neither the file resolution nor "
                        "an intended nonnegative blur fwhm.")
                if gradient_blur_resolution <= 0:
                    warnings.warn(
                        "Not blurring the gradients as this was explicitly disabled"
                    )
                src = s.defer(mincblur(source,
                                       fwhm=gradient_blur_resolution)).gradient
                dest = s.defer(mincblur(
                    target, fwhm=gradient_blur_resolution)).gradient
            else:
                # these are not gradient image terms; only blur if explicitly specified:
                if sim_metric_conf.blur is not None and sim_metric_conf.blur > 0:
                    src = s.defer(mincblur(source,
                                           fwhm=sim_metric_conf.blur)).img
                    dest = s.defer(mincblur(source,
                                            fwhm=sim_metric_conf.blur)).img
                else:
                    src = source
                    dest = target

            similarity_inputs.add(src)
            similarity_inputs.add(dest)
            inner = ','.join([
                src.path, dest.path,
                str(sim_metric_conf.weight),
                str(sim_metric_conf.radius_or_bins)
            ])
            subcmd = "'" + "".join([sim_metric_conf.metric, '[', inner, ']'
                                    ]) + "'"
            similarity_cmds.extend(["-m", subcmd])
        stage = CmdStage(
            inputs=(source, target) + tuple(similarity_inputs) +
            cast(tuple, ((source.mask, ) if source.mask else ())),
            # need to cast to tuple due to mypy bug; see mypy/issues/622
            outputs=(out_xfm, ),
            cmd=['ANTS', '3', '--number-of-affine-iterations', '0'] +
            similarity_cmds + [
                '-t', conf.transformation_model, '-r', conf.regularization,
                '-i', conf.iterations, '-o', out_xfm.path
            ] + (['-x', source.mask.path]
                 if conf.use_mask and source.mask else []))

        # see comments re: mincblur memory configuration
        stage.when_runnable_hooks.append(lambda st: set_memory(
            st, source=source, conf=conf, mem_cfg=default_ANTS_mem_cfg))

        s.add(stage)
        resampled = (
            s.defer(
                mincresample(
                    img=source,
                    xfm=out_xfm,
                    like=target,
                    interpolation=Interpolation.sinc,
                    #new_name_wo_ext=resample_name_wo_ext,
                    subdir=resample_subdir)) if resample_source else None
        )  # type: Optional[MincAtom]
        return Result(stages=s,
                      output=XfmHandler(source=source,
                                        target=target,
                                        xfm=out_xfm,
                                        resampled=resampled))
示例#2
0
  def register(source: MincAtom,
               target: MincAtom,
               conf: ANTSConf,
               initial_source_transform: Optional[XfmAtom] = None,
               transform_name_wo_ext: str = None,
               generation: int = None,
               resample_source: bool = False,
               #resample_name_wo_ext: Optional[str] = None,
               resample_subdir: str = "resampled") -> Result[XfmHandler]:
    """
    ...
    transform_name_wo_ext -- to use for the output transformation (without the extension)
    generation            -- if provided, the transformation name will be:
                             source.filename_wo_ext + "_ANTS_nlin-" + generation
    resample_source       -- whether or not to resample the source file   
    
    Construct a single call to ANTS.
    Also does blurring according to the specified options
    since the cost function might use these.
    """
    s = Stages()

    if initial_source_transform is not None:
        raise ValueError("ANTs doesn't accept an initial transform")

    # if we resample the source, and place it in the "tmp" directory, we should do
    # the same with the transformation that is created:
    trans_output_dir = "transforms"
    if resample_source and resample_subdir == "tmp":
        trans_output_dir = "tmp"

    if transform_name_wo_ext:
        name = os.path.join(source.pipeline_sub_dir, source.output_sub_dir, trans_output_dir,
                            "%s.xfm" % (transform_name_wo_ext))
    elif generation is not None:
        name = os.path.join(source.pipeline_sub_dir, source.output_sub_dir, trans_output_dir,
                            "%s_ANTS_nlin-%s.xfm" % (source.filename_wo_ext, generation))
    else:
        name = os.path.join(source.pipeline_sub_dir, source.output_sub_dir, trans_output_dir,
                            "%s_ANTS_to_%s.xfm" % (source.filename_wo_ext, target.filename_wo_ext))
    out_xfm = XfmAtom(name=name, pipeline_sub_dir=source.pipeline_sub_dir, output_sub_dir=source.output_sub_dir)

    similarity_cmds = []       # type: List[str]
    similarity_inputs = set()  # type: Set[MincAtom]
    # TODO: similarity_inputs should be a set, but `MincAtom`s aren't hashable
    for sim_metric_conf in conf.sim_metric_confs:
        if sim_metric_conf.use_gradient_image:
            if sim_metric_conf.blur is not None:
                gradient_blur_resolution = sim_metric_conf.blur
            elif conf.file_resolution is not None:
                gradient_blur_resolution = conf.file_resolution
            else:
                gradient_blur_resolution = None
                raise ValueError("A similarity metric in the ANTS configuration "
                                 "wants to use the gradients, but I know neither the file resolution nor "
                                 "an intended nonnegative blur fwhm.")
            if gradient_blur_resolution <= 0:
                warnings.warn("Not blurring the gradients as this was explicitly disabled")
            src = s.defer(mincblur(source, fwhm=gradient_blur_resolution)).gradient
            dest = s.defer(mincblur(target, fwhm=gradient_blur_resolution)).gradient
        else:
            # these are not gradient image terms; only blur if explicitly specified:
            if sim_metric_conf.blur is not None and sim_metric_conf.blur > 0:
                src  = s.defer(mincblur(source, fwhm=sim_metric_conf.blur)).img
                dest = s.defer(mincblur(source, fwhm=sim_metric_conf.blur)).img
            else:
                src  = source
                dest = target

        similarity_inputs.add(src)
        similarity_inputs.add(dest)
        inner = ','.join([src.path, dest.path,
                          str(sim_metric_conf.weight), str(sim_metric_conf.radius_or_bins)])
        subcmd = "'" + "".join([sim_metric_conf.metric, '[', inner, ']']) + "'"
        similarity_cmds.extend(["-m", subcmd])
    stage = CmdStage(
        inputs=(source, target) + tuple(similarity_inputs) + cast(tuple, ((source.mask,) if source.mask else ())),
        # need to cast to tuple due to mypy bug; see mypy/issues/622
        outputs=(out_xfm,),
        cmd=['ANTS', '3',
             '--number-of-affine-iterations', '0']
            + similarity_cmds
            + ['-t', conf.transformation_model,
               '-r', conf.regularization,
               '-i', conf.iterations,
               '-o', out_xfm.path]
            + (['-x', source.mask.path] if conf.use_mask and source.mask else []))

    # see comments re: mincblur memory configuration
    stage.when_runnable_hooks.append(lambda st: set_memory(st, source=source, conf=conf,
                                                           mem_cfg=default_ANTS_mem_cfg))

    s.add(stage)
    resampled = (s.defer(mincresample(img=source, xfm=out_xfm, like=target,
                                      interpolation=Interpolation.sinc,
                                      #new_name_wo_ext=resample_name_wo_ext,
                                      subdir=resample_subdir))
                 if resample_source else None)  # type: Optional[MincAtom]
    return Result(stages=s,
                  output=XfmHandler(source=source,
                                    target=target,
                                    xfm=out_xfm,
                                    resampled=resampled))
示例#3
0
def img_blur_56um_result(img):
    return mincblur(img=img, fwhm=0.056)
def img_blur_56um_result(img):
    return mincblur(img=img, fwhm=0.056)
示例#5
0
def cortical_thickness(xfms   : pd.Series,  # nlin avg -> subject XfmHandler (iirc)...
                       atlas  : MincAtom,   # nlin avg
                       label_mapping : FileAtom,
                       atlas_fwhm : float,
                       thickness_fwhm : float):

    try:
        import vtk
    except:
        warnings.warn("couldn't `import vtk`, without which `decimate.py` is unable to run ...")
        raise

    s = Stages()

    # generate thickness maps for the average:
    left_grid, right_grid = [s.defer(make_laplace_grid(input_labels=atlas.labels,
                                                       label_mapping=label_mapping,
                                                       binary_closing=True, side=side))
                             for side in (Side.left, Side.right)]

    atlas_left_thickness, atlas_right_thickness = (
        [s.defer(decimate(
            s.defer(minclaplace(input_grid=grid,
                                extra_args=["--create-surface-range", "0", "10"])).surface,  # enclose entire cortex
            reduction=0.8,  # FIXME: magic number ... implement a way to specify number rather than fraction instead?
            smoothing_method=Smoothing.laplace))
         for grid in (left_grid, right_grid)])

    # as per comment in MICe_thickness, blur atlas instead of transformed object files ... ?
    # (maybe this workaround is now obsolete)
    blurred_atlas = s.defer(mincblur(img=atlas, fwhm=atlas_fwhm)).img

    # TODO rename this dataframe
    resampled = (pd.DataFrame(
      {
        'xfm' : xfms,
        # resample the atlas files to each subject:
        'blurred_atlas_grid_resampled'  :
            xfms.apply(lambda xfm: s.defer(mincresample_new(img=blurred_atlas, xfm=xfm.xfm, like=xfm.target))),
        'atlas_left_resampled'    :
            xfms.apply(lambda xfm: s.defer(transform_objects(input_obj=atlas_left_thickness, xfm=xfm.xfm))),
        'atlas_right_resampled'   :
            xfms.apply(lambda xfm: s.defer(transform_objects(input_obj=atlas_right_thickness, xfm=xfm.xfm))),
      })
        .assign(left_grid=lambda df: df.xfm.map(lambda xfm: s.defer(
                    make_laplace_grid(input_labels=xfm.target,
                                      label_mapping=label_mapping,
                                      binary_closing=True,
                                      side=Side.left))),
                right_grid=lambda df: df.xfm.map(lambda xfm: s.defer(
                    make_laplace_grid(input_labels=xfm.target,
                                      label_mapping=label_mapping,
                                      binary_closing=True,
                                      side=Side.right))))
        .assign(left_thickness=lambda df: df.apply(axis=1, func=lambda row:
                  s.defer(minclaplace(input_grid=row.left_grid,
                                      solution_vertices=row.atlas_left_resampled))),
                right_thickness=lambda df: df.apply(axis=1, func=lambda row:
                  s.defer(minclaplace(input_grid=row.right_grid,
                                      solution_vertices=row.atlas_right_resampled))))
        .assign(smooth_left_fwhm=lambda df: df.apply(axis=1, func=lambda row:
                  s.defer(diffuse(obj_file=row.atlas_left_resampled,
                                  input_signal=row.left_thickness.solved,
                                  kernel=thickness_fwhm,
                                  iterations=1000))),
                smooth_right_fwhm=lambda df: df.apply(axis=1, func=lambda row:
                  s.defer(diffuse(obj_file=row.atlas_right_resampled,
                                  input_signal=row.right_thickness.solved,
                                  kernel=thickness_fwhm,
                                  iterations=1000)))))
    return Result(stages=s, output=resampled)
示例#6
0
def cortical_thickness(
        xfms: pd.Series,  # nlin avg -> subject XfmHandler (iirc)...
        atlas: MincAtom,  # nlin avg
        label_mapping: FileAtom,
        atlas_fwhm: float,
        thickness_fwhm: float):

    try:
        import vtk
    except:
        warnings.warn(
            "couldn't `import vtk`, without which `decimate.py` is unable to run ..."
        )
        raise

    s = Stages()

    # generate thickness maps for the average:
    left_grid, right_grid = [
        s.defer(
            make_laplace_grid(input_labels=atlas.labels,
                              label_mapping=label_mapping,
                              binary_closing=True,
                              side=side)) for side in (Side.left, Side.right)
    ]

    atlas_left_thickness, atlas_right_thickness = ([
        s.defer(
            decimate(
                s.defer(
                    minclaplace(
                        input_grid=grid,
                        extra_args=["--create-surface-range", "0",
                                    "10"])).surface,  # enclose entire cortex
                reduction=
                0.8,  # FIXME: magic number ... implement a way to specify number rather than fraction instead?
                smoothing_method=Smoothing.laplace))
        for grid in (left_grid, right_grid)
    ])

    # as per comment in MICe_thickness, blur atlas instead of transformed object files ... ?
    # (maybe this workaround is now obsolete)
    blurred_atlas = s.defer(mincblur(img=atlas, fwhm=atlas_fwhm)).img

    # TODO rename this dataframe
    resampled = (
        pd.DataFrame({
            'xfm':
            xfms,
            # resample the atlas files to each subject:
            'blurred_atlas_grid_resampled':
            xfms.apply(lambda xfm: s.defer(
                mincresample_new(
                    img=blurred_atlas, xfm=xfm.xfm, like=xfm.target))),
            'atlas_left_resampled':
            xfms.apply(lambda xfm: s.defer(
                transform_objects(input_obj=atlas_left_thickness, xfm=xfm.xfm))
                       ),
            'atlas_right_resampled':
            xfms.apply(lambda xfm: s.defer(
                transform_objects(input_obj=atlas_right_thickness, xfm=xfm.xfm)
            )),
        }).assign(left_grid=lambda df: df.xfm.map(lambda xfm: s.defer(
            make_laplace_grid(input_labels=xfm.target,
                              label_mapping=label_mapping,
                              binary_closing=True,
                              side=Side.left))),
                  right_grid=lambda df: df.xfm.map(lambda xfm: s.defer(
                      make_laplace_grid(input_labels=xfm.target,
                                        label_mapping=label_mapping,
                                        binary_closing=True,
                                        side=Side.right)))).
        assign(left_thickness=lambda df: df.apply(
            axis=1,
            func=lambda row: s.defer(
                minclaplace(input_grid=row.left_grid,
                            solution_vertices=row.atlas_left_resampled))),
               right_thickness=lambda df: df.apply(
                   axis=1,
                   func=lambda row: s.defer(
                       minclaplace(input_grid=row.right_grid,
                                   solution_vertices=row.atlas_right_resampled)
                   ))).assign(
                       smooth_left_fwhm=lambda df: df.apply(
                           axis=1,
                           func=lambda row: s.defer(
                               diffuse(obj_file=row.atlas_left_resampled,
                                       input_signal=row.left_thickness.solved,
                                       kernel=thickness_fwhm,
                                       iterations=1000))),
                       smooth_right_fwhm=lambda df: df.apply(
                           axis=1,
                           func=lambda row: s.defer(
                               diffuse(obj_file=row.atlas_right_resampled,
                                       input_signal=row.right_thickness.solved,
                                       kernel=thickness_fwhm,
                                       iterations=1000)))))
    return Result(stages=s, output=resampled)
def antsRegistration(source: MincAtom,
                     target: MincAtom,
                     conf: ANTSRegistrationConf,
                     initial_source_transform: Optional[XfmAtom] = None,
                     initial_target_transform: Optional[XfmAtom] = None,
                     # TODO create source_to_target_transform_name_wo_ext, target_to_source_...
                     transform_name_wo_ext: Optional[str] = None,
                     generation: Optional[int] = None,
                     subdir: Optional[str] = None,
                     resample_source: bool = False,
                     resample_target: bool = False,
                     resample_subdir: str  = 'tmp'):
    """
    :param source: fixedImage
    :param target: movingImage
    :param conf:
    :param initial_source_transform: for --initial-fixed-transform
    :param initial_target_transform: for --initial-moving-transform
    :param transform_name_wo_ext: name of the target_to_source transform will be based on this
    :param generation:
    :param subdir:
    :param resample_source:
    :return:
    """
    s = Stages()

    source_subdir = subdir if subdir is not None else 'transforms'

    # Deal with the transformations first. This function will return two XfmHandlers. One
    # from source to target, and one in the other direction.
    if transform_name_wo_ext:
        xfm_source_to_target = XfmAtom(name=os.path.join(source.pipeline_sub_dir,
                                                         source.output_sub_dir,
                                                         source_subdir,
                                                         "%s.xfm" % transform_name_wo_ext),
                                       pipeline_sub_dir=source.pipeline_sub_dir,
                                       output_sub_dir=source.output_sub_dir)
    elif generation is not None:
        xfm_source_to_target = XfmAtom(name=os.path.join(source.pipeline_sub_dir,
                                                         source.output_sub_dir,
                                                         source_subdir,
                                                         "%s_antsR_to_%s_nlin_%s.xfm" %
                                                         (source.filename_wo_ext,
                                                          target.filename_wo_ext,
                                                          generation)),
                                       pipeline_sub_dir=source.pipeline_sub_dir,
                                       output_sub_dir=source.output_sub_dir)
    else:
        xfm_source_to_target = XfmAtom(name=os.path.join(source.pipeline_sub_dir,
                                                         source.output_sub_dir,
                                                         source_subdir,
                                                         "%s_antsR_to_%s.xfm" %
                                                         (source.filename_wo_ext,
                                                          target.filename_wo_ext)),
                                       pipeline_sub_dir=source.pipeline_sub_dir,
                                       output_sub_dir=source.output_sub_dir)
    # model the target_to_source in a similar manner. Given that
    # antsRegistration will be passed the "output prefix" for the transform,
    # being the whole filename with .xfm, this transform will live in a
    # directory belonging to the source image.
    # TODO: is this what we want? perhaps we actually want to move this transformation
    # over to a subdirectory of the target file...
    # N.B.: the name of the following file is forced by antsRegistration; we can't specify a subdir or anything...
    # DON'T CHANGE THIS!
    xfm_target_to_source = xfm_source_to_target.newname_with_suffix("_inverse", subdir=source_subdir)

    # outputs from antRegistration are:
    #   {output_prefix}_grid_0.mnc
    #   {output_prefix}.xfm
    #   {output_prefix}_inverse_grid_0.mnc
    #   {output_prefix}_inverse.xfm

    # Outputs:
    # 1) transform from source_to_target
    # 2) transform from target_to_source --> need to create an XfmHandler for this one

    def optional(x, f, default=[]):
        return f(x) if x is not None else []
    # TODO: use a proper configuration to set the parameters
    # TODO: add a second metric for the gradients (and get gradient files)

    if any(m.use_gradient_image for m in conf.metrics):
        if conf.file_resolution is None:
            raise ValueError("A similarity metric in the ANTS configuration "
                             "wants to use the gradients, but the file resolution for the "
                             "configuration has not been set.")
        blurred_source, blurred_target = [s.defer(mincblur(img, fwhm=conf.file_resolution)).gradient
                                          for img in (source, target)]
    else:
        blurred_source = blurred_target = None

    def render_metric(m : Metric):
        if m.use_gradient_image:
            if conf.file_resolution is None:
                raise ValueError("A similarity metric in the ANTS configuration "
                                 "wants to use the gradients, but the file resolution for the "
                                 "configuration has not been set.")
            fixed = blurred_source
            moving = blurred_target
        else:
            fixed = source
            moving = target
        return "'%s[%s,%s,%s,%s]'" % (m.metric, fixed.path, moving.path, m.weight, m.radius_or_bins)

    if conf.use_masks:
        if source.mask is not None and target.mask is not None:
            mask_arr = ['--masks', '[%s,%s]' % (source.mask.path, target.mask.path)]
        elif source.mask is not None:
            mask_arr = ['--masks', '[%s]' % source.mask.path]
        elif target.mask is not None:
            warnings.warn("only target mask is specified; antsRegistration needs at least a source mask")
            mask_arr = []
        else:
            warnings.warn("no masks supplied")
            mask_arr = []
    else:
        mask_arr = []

    cmd = CmdStage(
        inputs=tuple(img for img in
                     (source, target,
                      source.mask, target.mask,
                      blurred_source, blurred_target,
                      initial_source_transform, initial_target_transform)
                     if img is not None),
        outputs=(xfm_source_to_target, xfm_target_to_source),
        cmd=['antsRegistration']
            + optional(conf.dimensionality, lambda d: ['--dimensionality', "%d" % d])
            + ['--convergence', render_convergence_conf(conf.convergence)]
            + ['--verbose']
            + ['--minc']
            + ['--collapse-output-transforms', '1']
            + ['--write-composite-transform']
            + ['--winsorize-image-intensities', '[0.01,0.99]']
            + optional(conf.use_histogram_matching, lambda _: ['--use-histogram-matching', '1'])
            + ['--float', '0']
            + ['--output', '[' + os.path.join(xfm_source_to_target.dir, xfm_source_to_target.filename_wo_ext) + ']']
            + ['--transform', conf.transformation_model]
            + optional(initial_source_transform, lambda xfm: ['--initial-fixed-transform', xfm.path])
            + optional(initial_target_transform, lambda xfm: ['--initial-moving-transform', xfm.path])
            + flatten(*[['--metric', render_metric(m)] for m in conf.metrics])
            + mask_arr
            + ['--shrink-factors', 'x'.join(str(s) for s in conf.shrink_factors)]
            + ['--smoothing-sigmas', 'x'.join(str(s) for s in conf.smoothing_sigmas)]
    )

    # shamelessly stolen from ANTS, probably inaccurate
    # see comments re: mincblur memory configuration
    def set_memory(st, mem_cfg):
        # see comments re: mincblur memory configuration
        voxels = reduce(mul, volumeFromFile(source.path).getSizes())
        mem_per_voxel = (mem_cfg.mem_per_voxel_coarse
                         if 0 in conf.convergence.iterations[-1:]  #-2?
                         # yikes ... this parsing should be done earlier
                         else mem_cfg.mem_per_voxel_fine)
        st.setMem(mem_cfg.base_mem + voxels * mem_per_voxel)

    cmd.when_runnable_hooks.append(lambda st: set_memory(st, mem_cfg=default_ANTSRegistration_mem_cfg))

    s.add(cmd)

    # create file names for the two output files. It's better to use our standard
    # mincresample command for this, because that also deals with any associated
    # masks, whereas antsRegistration would only resample the input files.
    resampled_source = (s.defer(mincresample(img=source,
                                             xfm=xfm_source_to_target,
                                             like=target,
                                             interpolation=Interpolation.sinc,
                                             subdir=resample_subdir))
                        if resample_source else None)
    resampled_target = (s.defer(mincresample(img=target,
                                             xfm=xfm_target_to_source,
                                             like=source,
                                             interpolation=Interpolation.sinc,
                                             subdir=resample_subdir))
                        if resample_target else None)

    # return an XfmHandler for both the forward and the inverse transformations
    return Result(stages=s,
                  output=XfmHandler(source=source,
                                    target=target,
                                    xfm=xfm_source_to_target,
                                    resampled=resampled_source,
                                    inverse=XfmHandler(source=target,
                                                       target=source,
                                                       xfm=xfm_target_to_source,
                                                       resampled=resampled_target)))
示例#8
0
def antsRegistration(
        source: MincAtom,
        target: MincAtom,
        conf: ANTSRegistrationConf,
        initial_source_transform: Optional[XfmAtom] = None,
        initial_target_transform: Optional[XfmAtom] = None,
        # TODO create source_to_target_transform_name_wo_ext, target_to_source_...
        transform_name_wo_ext: Optional[str] = None,
        generation: Optional[int] = None,
        subdir: Optional[str] = None,
        resample_source: bool = False,
        resample_target: bool = False,
        resample_subdir: str = 'tmp'):
    """
    :param source: fixedImage
    :param target: movingImage
    :param conf:
    :param initial_source_transform: for --initial-fixed-transform
    :param initial_target_transform: for --initial-moving-transform
    :param transform_name_wo_ext: name of the target_to_source transform will be based on this
    :param generation:
    :param subdir:
    :param resample_source:
    :return:
    """
    s = Stages()

    source_subdir = subdir if subdir is not None else 'transforms'

    # Deal with the transformations first. This function will return two XfmHandlers. One
    # from source to target, and one in the other direction.
    if transform_name_wo_ext:
        xfm_source_to_target = XfmAtom(
            name=os.path.join(source.pipeline_sub_dir, source.output_sub_dir,
                              source_subdir, "%s.xfm" % transform_name_wo_ext),
            pipeline_sub_dir=source.pipeline_sub_dir,
            output_sub_dir=source.output_sub_dir)
    elif generation is not None:
        xfm_source_to_target = XfmAtom(
            name=os.path.join(
                source.pipeline_sub_dir, source.output_sub_dir, source_subdir,
                "%s_antsR_to_%s_nlin_%s.xfm" %
                (source.filename_wo_ext, target.filename_wo_ext, generation)),
            pipeline_sub_dir=source.pipeline_sub_dir,
            output_sub_dir=source.output_sub_dir)
    else:
        xfm_source_to_target = XfmAtom(
            name=os.path.join(
                source.pipeline_sub_dir, source.output_sub_dir, source_subdir,
                "%s_antsR_to_%s.xfm" %
                (source.filename_wo_ext, target.filename_wo_ext)),
            pipeline_sub_dir=source.pipeline_sub_dir,
            output_sub_dir=source.output_sub_dir)
    # model the target_to_source in a similar manner. Given that
    # antsRegistration will be passed the "output prefix" for the transform,
    # being the whole filename with .xfm, this transform will live in a
    # directory belonging to the source image.
    # TODO: is this what we want? perhaps we actually want to move this transformation
    # over to a subdirectory of the target file...
    # N.B.: the name of the following file is forced by antsRegistration; we can't specify a subdir or anything...
    # DON'T CHANGE THIS!
    xfm_target_to_source = xfm_source_to_target.newname_with_suffix(
        "_inverse", subdir=source_subdir)

    # outputs from antRegistration are:
    #   {output_prefix}_grid_0.mnc
    #   {output_prefix}.xfm
    #   {output_prefix}_inverse_grid_0.mnc
    #   {output_prefix}_inverse.xfm

    # Outputs:
    # 1) transform from source_to_target
    # 2) transform from target_to_source --> need to create an XfmHandler for this one

    def optional(x, f, default=[]):
        return f(x) if x is not None else []

    # TODO: use a proper configuration to set the parameters
    # TODO: add a second metric for the gradients (and get gradient files)

    if any(m.use_gradient_image for m in conf.metrics):
        if conf.file_resolution is None:
            raise ValueError(
                "A similarity metric in the ANTS configuration "
                "wants to use the gradients, but the file resolution for the "
                "configuration has not been set.")
        blurred_source, blurred_target = [
            s.defer(mincblur(img, fwhm=conf.file_resolution)).gradient
            for img in (source, target)
        ]
    else:
        blurred_source = blurred_target = None

    def render_metric(m: Metric):
        if m.use_gradient_image:
            if conf.file_resolution is None:
                raise ValueError(
                    "A similarity metric in the ANTS configuration "
                    "wants to use the gradients, but the file resolution for the "
                    "configuration has not been set.")
            fixed = blurred_source
            moving = blurred_target
        else:
            fixed = source
            moving = target
        return "'%s[%s,%s,%s,%s]'" % (m.metric, fixed.path, moving.path,
                                      m.weight, m.radius_or_bins)

    if conf.use_masks:
        if source.mask is not None and target.mask is not None:
            mask_arr = [
                '--masks',
                '[%s,%s]' % (source.mask.path, target.mask.path)
            ]
        elif source.mask is not None:
            mask_arr = ['--masks', '[%s]' % source.mask.path]
        elif target.mask is not None:
            warnings.warn(
                "only target mask is specified; antsRegistration needs at least a source mask"
            )
            mask_arr = []
        else:
            warnings.warn("no masks supplied")
            mask_arr = []
    else:
        mask_arr = []

    cmd = CmdStage(
        inputs=tuple(img for img in (source, target, source.mask, target.mask,
                                     blurred_source, blurred_target,
                                     initial_source_transform,
                                     initial_target_transform)
                     if img is not None),
        outputs=(xfm_source_to_target, xfm_target_to_source),
        cmd=['antsRegistration'] +
        optional(conf.dimensionality,
                 lambda d: ['--dimensionality', "%d" % d]) +
        ['--convergence',
         render_convergence_conf(conf.convergence)] + ['--verbose'] +
        ['--minc'] + ['--collapse-output-transforms', '1'] +
        ['--write-composite-transform'] +
        ['--winsorize-image-intensities', '[0.01,0.99]'] +
        optional(conf.use_histogram_matching,
                 lambda _: ['--use-histogram-matching', '1']) +
        ['--float', '0'] + [
            '--output',
            '[' + os.path.join(xfm_source_to_target.dir,
                               xfm_source_to_target.filename_wo_ext) + ']'
        ] + ['--transform', conf.transformation_model] +
        optional(initial_source_transform,
                 lambda xfm: ['--initial-fixed-transform', xfm.path]) +
        optional(initial_target_transform,
                 lambda xfm: ['--initial-moving-transform', xfm.path]) +
        flatten(*[['--metric', render_metric(m)]
                  for m in conf.metrics]) + mask_arr +
        ['--shrink-factors', 'x'.join(str(s) for s in conf.shrink_factors)] + [
            '--smoothing-sigmas', 'x'.join(
                str(s) for s in conf.smoothing_sigmas)
        ])

    # shamelessly stolen from ANTS, probably inaccurate
    # see comments re: mincblur memory configuration
    def set_memory(st, mem_cfg):
        # see comments re: mincblur memory configuration
        voxels = reduce(mul, volumeFromFile(source.path).getSizes())
        mem_per_voxel = (
            mem_cfg.mem_per_voxel_coarse
            if 0 in conf.convergence.iterations[-1:]  #-2?
            # yikes ... this parsing should be done earlier
            else mem_cfg.mem_per_voxel_fine)
        st.setMem(mem_cfg.base_mem + voxels * mem_per_voxel)

    cmd.when_runnable_hooks.append(
        lambda st: set_memory(st, mem_cfg=default_ANTSRegistration_mem_cfg))

    s.add(cmd)

    # create file names for the two output files. It's better to use our standard
    # mincresample command for this, because that also deals with any associated
    # masks, whereas antsRegistration would only resample the input files.
    resampled_source = (s.defer(
        mincresample(img=source,
                     xfm=xfm_source_to_target,
                     like=target,
                     interpolation=Interpolation.sinc,
                     subdir=resample_subdir)) if resample_source else None)
    resampled_target = (s.defer(
        mincresample(img=target,
                     xfm=xfm_target_to_source,
                     like=source,
                     interpolation=Interpolation.sinc,
                     subdir=resample_subdir)) if resample_target else None)

    # return an XfmHandler for both the forward and the inverse transformations
    return Result(stages=s,
                  output=XfmHandler(source=source,
                                    target=target,
                                    xfm=xfm_source_to_target,
                                    resampled=resampled_source,
                                    inverse=XfmHandler(
                                        source=target,
                                        target=source,
                                        xfm=xfm_target_to_source,
                                        resampled=resampled_target)))