-
-
Notifications
You must be signed in to change notification settings - Fork 715
Make 3D SobelOperator consistent with 2D, add UseLegacyCoefficients, add ND support #5718
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
base: main
Are you sure you want to change the base?
Make 3D SobelOperator consistent with 2D, add UseLegacyCoefficients, add ND support #5718
Conversation
dzenanz
left a comment
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.
Did we have dimensionality constraints somewhere? If so, we should lift them as part of this PR. Also, we probably want to add unit tests for higher dimensions.
Thanks for the reminder 😺 We would also have to generalize its
So you see, it's really WIP! |
| // Standard Sobel definition: derivative = [-1, 0, 1], smoothing = [1, 2, 1]. | ||
| // Kernel for axis a is: K_a(x) = d[x_a] * Π_{j != a} s[x_j], with x_j ∈ {-1,0,1}. | ||
| inline std::vector<std::vector<int>> | ||
| make_nd_sobel_kernels(std::size_t N) |
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.
If this function were templated on N (aka VDimension), it could also be constexpr.
Then the test for N == 0 could also be a static assert instead of a runtime test.
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.
Thanks Hans! I just gave constexpr a try: WIP: Make make_nd_sobel_kernels constexpr (updated link)
Note that in this context, make_nd_sobel_kernels would just be an internal helper function of itk::SobelOperator<TPixel, VDimension, TAllocator>, so it already "knows" VDimension, and there is no need to pass it as parameter. VDimension can never be zero, so the assert isn't really necessary anyway.
Still: this is all WIP. Especially because I don't really understand the AI generated code, do you?
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.
In the mean time, I think I found an AI hallucination! Looking at the original AI generated code at #5702 (comment) This part appears useless:
// Precompute strides for row-major flattening (last dim fastest)
std::vector<std::size_t> stride(N, 1);
for (std::size_t i = 1; i < N; ++i) {
stride[N - 1 - i] = stride[N - i] * 3;
}The rest of the code does not make use of those strides! So it's unnecessary! I think I'll remove this part of code with the next force-push!
54d23a1 to
730a117
Compare
|
FYI My intention is to squash most of these WIP commits before making the PR ready for review. (But before doing so, the PR should also offer a backward compatible legacy option, at least for 3D.) |
|
/azp run ITK.Linux.Python |
|
/azp run ITK.Linux |
6ad490d to
4c20b2c
Compare
| const auto & kernel = sobelKernels[VDimension - 1 - direction]; | ||
| return CoefficientVector(kernel.cbegin(), kernel.cend()); |
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.
Why not simply kernel = sobelKernels[direction]? I still don't understand why the kernels returned by the AI generated function from #5702 (comment) are in the reverse order, compared by ITK 🤷
4c20b2c to
a20d509
Compare
|
When I am building SimpleITK with Elastic against the current ITK main ( or work derived from ). I am getting the following compilation error: Will this PR fix this issue? |
Because this PR is already so elaborate, I would prefer to have the fix in a follow-up. This PR aims to:
The follow-up PR should then:
If SimpleITK does not need 4D Sobel, better just not wrap it at all! It never worked anyway. In the past, it just produced an exception. Update: this PR now includes 4-D support! No need to wait for a follow-up 😃 |
The error comes from elastix usage of the Sobel operator: Should I wait for this PR and subsequent to fix Elastix or update the Elastic version in SimpleITK? Or something else? |
| void | ||
| UseLegacyCoefficients(const bool useLegacyCoefficients) | ||
| { | ||
| m_UseLegacyCoefficients = useLegacyCoefficients; | ||
| } |
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.
Note that itkSetMacro(UseLegacyCoefficients, bool) does not compile, because itk::SobelOperator is not derived from itk::Object, and it does not support this->Modified().
The macro (itkSetMacro) would make it SetUseLegacyCoefficients(bool _arg) instead. But I think UseLegacyCoefficients(const bool useLegacyCoefficients) is more readable. OK?
What about the other member function, IsUsingLegacyCoefficients()? Is that clear enough?
a20d509 to
873d1df
Compare
The old kernel coefficients for 3D appeared inconsistent with the coefficients for 2D. This commit allows generating these coefficients consistently, inspired by the AI generated function `make_nd_sobel_kernels`, presented by Hans Johnson at issue InsightSoftwareConsortium#5702 The new coefficients are more commonly used by other toolkits, including SciPy's `scipy.ndimage.sobel`, and correspond with https://en.wikipedia.org/wiki/Sobel_operator This commit removes the reference to "An Isotropic 3x3x3 Volume Gradient Operator", Sobel, 1995, as it cannot be found anymore. Added a `UseLegacyCoefficients` option to SobelOperator, allows the user to specify whether or not `SobelOperator::GenerateCoefficients()` should still produce the old coefficients for a 3D kernel. Enabled by default.
873d1df to
43801b2
Compare
Removed the `static_assert` from `SobelOperator` that allowed only 2D and 3D. Added checks for 1D and 4D to the `SobelOperator.CheckKernelCoordinates` unit test. Addresses issue InsightSoftwareConsortium#5702 "A ND Sobel implementation is needed."
@dzenanz Addressed by the last commit: ENH: Add ND support to SobelOperator |
@blowekamp My formal answer (however disappointing) is of course: Elastix does not support arbitrary revisions of ITK. It only supports official releases of ITK. (Elastix might occasionally support an alpha or a beta version of ITK, but we try to stick to official releases. Not an arbitrary revision from the |
The old kernel coefficients for 3D appeared inconsistent with the coefficients for 2D. This pull request allows generating these coefficients consistently, inspired by the AI generated function
make_nd_sobel_kernels, presented by Hans Johnson (@hjmjohnson) at #5702 (comment)The new coefficients are more commonly used by other toolkits, including SciPy's
scipy.ndimage.sobel, and correspond with https://en.wikipedia.org/wiki/Sobel_operatorThis PR removes the reference to "An Isotropic 3x3x3 Volume Gradient Operator", Irwin Sobel, 1995, as it cannot be found anymore.
Added a
UseLegacyCoefficientsoption to SobelOperator, which allows the user to specify whether or notSobelOperator::GenerateCoefficients()should still produce the old coefficients for a 3D kernel. Enabled by default.Removed the
static_assertfromSobelOperatorthat allowed only 2D and 3D. Added checks for 1D and 4D to the unit test.