# N4BiasFieldCorrectionImageFilter: GetLogBiasFieldAsImage, wrap BSplineControlPointImageFilter, alternatives

**URL:** <https://discourse.itk.org/t/n4biasfieldcorrectionimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142>\
**Category:** Uncategorized\
**Created:** [June 29, 2022, 8:50pm UTC](https://discourse.itk.org/t/n4biasfieldcorrectionimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142 "2022-06-29T20:50:34Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![dyoll](https://discourse.itk.org/user_avatar/discourse.itk.org/dyoll/32/2311_2.png) [@dyoll](https://discourse.itk.org/u/dyoll)\
**Post date:** [June 29, 2022, 8:50pm UTC](https://discourse.itk.org/t/n4biasfieldcorrectionimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142/1 "2022-06-29T20:50:34Z")

</div>

With SimpleITK I can do:

```auto
def bias_correct(
    input: sitk.Image,
    mask: sitk.Image = None,
    shrink_factor: int = 4,
    num_fitting_levels: int = 4,
    num_iterations: int = 50,
) -> sitk.Image:
    """Perform N4 bias correction on MRI

    Note:
    - if no mask is provided it will be generated using Otsu-thresholding
    """
    if not isinstance(mask, sitk.Image):
        mask = sitk.OtsuThreshold(input, 0, 1, 200)

    input = sitk.Cast(input, sitk.sitkFloat32)
    image = sitk.Shrink(input, [shrink_factor] * input.GetDimension())
    mask = sitk.Shrink(mask, [shrink_factor] * input.GetDimension())

    corrector = sitk.N4BiasFieldCorrectionImageFilter()
    corrector.SetMaximumNumberOfIterations([num_iterations] * num_fitting_levels)

    corrector.Execute(image, mask)
    log_bias_field = corrector.GetLogBiasFieldAsImage(input)
    corrected_image_full_resolution = input / sitk.Exp(log_bias_field)
    return corrected_image_full_resolution

```

But since ITK Python does not have the convenience function `GetLogBiasFieldAsImage` and does not wrap itk::BSplineControlPointImageFilter, it seems I cannot implement bias correction as in ANTs using ITK Python.

If (there are no workarounds) I might do a pull request, but I wonder if I should implement the `GetLogBiasFieldAsImage` like helper or wrap `BSplineControlPointImageFilter` to implement [something like this](https://github.com/dyollb/osparc-iseg/blob/master/Plugins/BiasCorrection/BiasCorrection.cpp).

Then again, I might just install ITK (for MONAI `ITKReader`) and SimpleITK (for image pre-processing) side-by-side, since dealing with image/filter types is much easier and auto-completion works (for simpleitk)

---

<div class="post-metadata">

**Author:** ![dyoll](https://discourse.itk.org/user_avatar/discourse.itk.org/dyoll/32/2311_2.png) [@dyoll](https://discourse.itk.org/u/dyoll)\
**Post date:** [June 30, 2022, 7:18am UTC](https://discourse.itk.org/t/n4biasfieldcorrectionimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142/2 "2022-06-30T07:18:19Z")

</div>

Looking at itkN4BiasFieldCorrectionImageFilter.h there is a function `ReconstructBiasField`. It looks like I should basically be able to do:

```auto
    image_small = itk.shrink_image_filter(image, shrink_factors=[shrink_factor] * image.GetImageDimension())
    mask_small = itk.shrink_image_filter(mask, shrink_factors=[shrink_factor] * image.GetImageDimension())

    corrector = itk.N4BiasFieldCorrectionImageFilter.New(image_small, mask_small)
    corrector.SetNumberOfFittingLevels(num_fitting_levels)
    corrector.SetMaximumNumberOfIterations([num_iterations] * num_fitting_levels)
    corrector.Update()

    full_res_corrector = itk.N4BiasFieldCorrectionImageFilter.New(image, mask)
    log_bias_field = full_res_corrector.ReconstructBiasField(corrector.GetLogBiasFieldControlPointLattice())

    print("image", itk.size(image), itk.origin(image), itk.spacing(image))
    print("log_bias_field", itk.size(log_bias_field), itk.origin(log_bias_field), itk.spacing(log_bias_field))

    corrected_image_np = itk.array_view_from_image(image) / np.exp(itk.array_view_from_image(log_bias_field))
    corrected_image_full_resolution = itk.image_from_array(corrected_image_np)

```

For some reason the reconstructed bias field has the wrong shape though:

> image itkSize3 ([256, 256, 150])  
> log\_bias\_field itkSize3 ([253, 253, 145])

But `ReconstructBiasField` is setting the size [from the input image RequestedRegion after the data is generated (?)](https://github.com/InsightSoftwareConsortium/ITK/blob/b53f1aae2027dbf8e58184f4bf54d9bdca541f4d/Modules/Filtering/BiasCorrection/include/itkN4BiasFieldCorrectionImageFilter.hxx#L636):

```auto
    biasField->SetRegions(inputImage->GetRequestedRegion());

```

So, my workaround for this bug (?) is to to first ensure the requested region is the same as the largest possible (and buffered) region:

```auto
    image.SetRequestedRegion(image.GetBufferedRegion())
    
    full_res_corrector = itk.N4BiasFieldCorrectionImageFilter.New(image, mask)
    log_bias_field = full_res_corrector.ReconstructBiasField(corrector.GetLogBiasFieldControlPointLattice())

```

---

<div class="post-metadata">

**Author:** ![dyoll](https://discourse.itk.org/user_avatar/discourse.itk.org/dyoll/32/2311_2.png) [@dyoll](https://discourse.itk.org/u/dyoll)\
**Post date:** [June 30, 2022, 7:58am UTC](https://discourse.itk.org/t/n4biasfieldcorrectionimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142/3 "2022-06-30T07:58:50Z")

</div>

> <https://github.com/InsightSoftwareConsortium/ITK/pull/3477>
>
> \## Description of problem
> 
> See https://discourse.itk.org/t/n4biasfieldcorrecti…onimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142/2
> 
> If (for some reason) the input requested region is different than the largest possible region, the code was
> \- first reconstructing the bias field using the largest possible region
> \- then setting all regions to \`inputImage-\>GetRequestedRegion()\`, which doesn't make sense
> 
> To reduce the change to a minimum I still set the requested region based on \`inputImage-\>GetRequestedRegion()\`, but probably we could just remove the line completely.
> 
> \<!-- The text within this markup is a comment, and is intended to provide
> guidelines to open a Pull Request for the ITK repository. This text will not
> be part of the Pull Request. --\>
> 
> 
> \<!-- See the CONTRIBUTING (CONTRIBUTING.md) guide. Specifically:
> 
> Start ITK commit messages with a standard prefix (and a space):
> 
> \* BUG: fix for runtime crash or incorrect result
> \* COMP: compiler error or warning fix
> \* DOC: documentation change
> \* ENH: new functionality
> \* PERF: performance improvement
> \* STYLE: no logic impact (indentation, comments)
> \* WIP: Work In Progress not ready for merge
> 
> Provide a short, meaningful message that describes the change you made.
> 
> When the PR is based on a single commit, the commit message is usually left as
> the PR message.
> 
> A reference to a related issue or pull request (https://help.github.com/articles/basic-writing-and-formatting-syntax/#referencing-issues-and-pull-requests)
> in your repository. You can automatically
> close a related issues using keywords (https://help.github.com/articles/closing-issues-using-keywords/)
> 
> @mentions (https://help.github.com/articles/basic-writing-and-formatting-syntax/#mentioning-people-and-teams)
> of the person or team responsible for reviewing proposed changes. --\>
> 
> \## PR Checklist
> \- \[x\] No \[API changes\](https://github.com/InsightSoftwareConsortium/ITK/blob/master/CONTRIBUTING.md#breaking-changes) were made (or the changes have been approved)
> \- \[x\] No \[major design changes\](https://github.com/InsightSoftwareConsortium/ITK/blob/master/CONTRIBUTING.md#design-changes) were made (or the changes have been approved)
> \- \[\] Added test (or behavior not changed)
> \- \[\] Updated API documentation (or API not changed)
> \- \[\] Added \[license\](https://github.com/InsightSoftwareConsortium/ITK/blob/master/Utilities/KWStyle/ITKHeader.h) to new files (if any)
> \- \[\] Added Python wrapping to new files (if any) as described in \[ITK Software Guide\](https://itk.org/ItkSoftwareGuide.pdf) Section 9.5
> \- \[\] Added \[ITK examples\](https://github.com/InsightSoftwareConsortium/ITKSphinxExamples) for all new major features (if any)
> 
> Refer to the \[ITK Software Guide\](https://itk.org/ItkSoftwareGuide.pdf) for
> further development details if necessary.

---

<div class="post-metadata">

**Author:** ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)\
**Post date:** [June 30, 2022, 8:07am UTC](https://discourse.itk.org/t/n4biasfieldcorrectionimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142/4 "2022-06-30T08:07:37Z")

</div>

> [@dyoll](#):
>
> wrap `BSplineControlPointImageFilter`

This sounds better to me. But if engineering time is not an issue for you, doing both sounds good to me.

---

<div class="post-metadata">

**Author:** ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)\
**Post date:** [November 20, 2023, 9:18pm UTC](https://discourse.itk.org/t/n4biasfieldcorrectionimagefilter-getlogbiasfieldasimage-wrap-bsplinecontrolpointimagefilter-alternatives/5142/5 "2023-11-20T21:18:15Z")

</div>

I just needed bias field correction in Python, so this was quite useful.
