-
-
Notifications
You must be signed in to change notification settings - Fork 663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PERF: N4BiasFieldCorrectionImageFilter implementation using ImageRange #295
PERF: N4BiasFieldCorrectionImageFilter implementation using ImageRange #295
Conversation
Please note: I will remove the "WIP" tag from the commit message when I think the commit is ready to be merged. For now, I still want to have another look at the code, and try it out! |
Hey, this is great. Hope it works out. |
Thanks for your encouragements @ntustison and @gdevenyi ! I just did some experiments (using MeVisLab), and observed a performance improvement of about 10%, with this commit. (Going from 77 sec to 70 sec., on a 453x340x20 MRI volume + mask, provided by my LKEB/LUMC colleague Denis, @dpshamonin ) It may not be a huge performance improvement, but it is significant, and reproducible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great addition! I see some change that need to happen and a couple things that could be improved...
|
||
const InputImageType *const inputImage = this->GetInput(); | ||
|
||
if (inputImage == nullptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check should not be needed. This should be the "PrimaryInput" according to the ProcessObject, and it should by default be marked as required. This should be verified in the base class: https://github.com/InsightSoftwareConsortium/ITK/blob/master/Modules/Core/Common/src/itkProcessObject.cxx#L1419-L1429
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blowekamp Thanks for your feedback. I'll remove itkExceptionMacro("Failed precondition: Input image should not be null")
with the next amend.
if ((maskImage != nullptr) && (maskImage->GetBufferedRegion().GetSize() != inputImageSize)) | ||
{ | ||
itkExceptionMacro( | ||
"Failed precondition: If a mask image is specified, its size should be equal to the input image size"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation for VerifyPreconditions says it is called before UpdateOutputInformation
is called. The is a step in the ITK pipeline before the filters GenerateData step is done. So, the LargestPossibleRegion is not even updated at this point in the pipeline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blowekamp Can you please explain? The maskImage
is an input of the filter. So it should be checked before GenerateData. OK?
if ((confidenceImage != nullptr) && (confidenceImage->GetBufferedRegion().GetSize() != inputImageSize)) | ||
{ | ||
itkExceptionMacro( | ||
"Failed precondition: If a confidence image is specified, its size should be equal to the input image size"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar here. Please review section 8.3.1 of the ITK Software guide explaining the step and how the ITK pipeline works by default. By default this filter's request output region is propagated to all the inputs in the substeps of PropagateRequestedRegion()
. I'm not sure what the issue was to add these checks, but knowing that we can then work on a proper solution with the ITK pipeline.
Modules/Filtering/BiasCorrection/include/itkN4BiasFieldCorrectionImageFilter.hxx
Show resolved
Hide resolved
There seems to be some regions issue with this filter related to the pipeline propagation of the requested region. We could ask the questions can the filter produce a sub-region of the output, or what input regions are needed to produce that output. The answers would indicate output enlarge the output requested regions and the propagate it to the input. However, it looks like this filter assumes to produce the whole output and consumable the whole input. This can be done with the Pipeline by overloading EnlargeOutputRequestedRegion to set the output to the LargestPossibleRegion. Then the default implementation of I hope that makes since and helps... |
@blowekamp Thanks for your extensive analysis! So if I understand correctly, the question is: Was the N4 filter designed to support sub-regions? Or does it always operate on the entire input image and the entire output image? If it always operates on the entire image buffer (its buffered region), 10% performance improvement can be achieved by this commit, using However, if |
@N-Dekker I believe this filter most certainly needs all if its inputs that this filter can only compute the full bias field. Its possible to not apply the bias field to the whole image in the final The After that you should not need these regions checks. My questions what does the |
Yes indeed, I am considering to also propose a range to iterate over the pixels of a region (I think I will call it "ImageRegionRange"). But for this particular filter, it seems preferable to use the existing |
OK... I think I am staring to under stand the requirements of the new ImageRange iterator and how they may need to interact with the ITK pipeline. The pipeline will guarantees that the requested regions is available in the So perhaps these checks are needed. The check would need to occur in the GenerateData method. |
With the current amend/version of the commit, the image sizes are checked within the added Note that I also included your suggestion to override |
The The The N4 test has a bit of "style" to it. After every filter is constructed it is updated, and then the output is disconnected. It does not really exercise the ITK pipeline. You would see this issue if the filters inputs were connected from another filter ( not already updated ).
|
@blowekamp OK: With the latest amend, the image size checks are in |
Adapted N4BiasFieldCorrectionImageFilter to start using ImageRange internally. This commit yields a significant performance improvement, especially because it replaces the (rather expensive) itk::Image::GetPixel(index) calls by more efficient ImageRange[indexValue] calls. Added checks to GenerateData() that the image sizes of the mask image and confidence image (if provided) are equal to the size of the input image. Ensured that the entire output image is computed at once, by overriding EnlargeOutputRequestedRegion(DataObject*), as suggested by Bradley (@blowekamp). Using VS2015 (Release), ~10% reduction of the run-time duration was observed on a 3-D MRI volume + mask (453x340x20 voxels), provided by Denis (@dpshamonin), at LKEB, Leiden University Medical Center.
The discussion of the |
Sorry wrong click... |
@blowekamp It appears that your concerns have been addressed in the latest code. Would you mind reviewing your block on this PR? |
From the discourse discussion, I don't believe we have come to an agreement on how to make the |
@blowekamp @hjmjohnson @thewtex In this specific case (the N4 bias filter), there's an alternative to this PR, that would yield the very same performance gain: just call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great addition! Thank you @N-Dekker !
ImageRange
is much better than raw pointer manipulation. While there may be other refactoring to refine ImageRange
in the future, we could merge this as-is and make those changes in the future. The fundamental changes here are good.
Good first step, could use refactor in a subsequent PR to make the iteration Region explicit in MakeImageRange to ensure consistency between Ranges.
@blowekamp wrote:
It would be helpful to me if this PR could already be merged, indeed, as a starting point for further improvements. |
Adapted N4BiasFieldCorrectionImageFilter to start using ImageRange internally.
This commit yields a significant performance improvement, especially because
it replaces the (rather expensive) itk::Image::GetPixel(index) calls by
more efficient ImageRange[indexValue] calls.
Added checks to GenerateData() that the image sizes of the mask image and
confidence image (if provided) are equal to the size of the input image.
Ensured that the entire output image is computed at once, by overriding
EnlargeOutputRequestedRegion(DataObject*), as suggested by Bradley (@blowekamp).
Using VS2015 (Release), ~10% reduction of the run-time duration was observed on
a 3-D MRI volume + mask (453x340x20 voxels), provided by Denis (@dpshamonin),
at LKEB, Leiden University Medical Center.