Skip to content

Conversation

@N-Dekker
Copy link
Contributor

@N-Dekker N-Dekker commented Jan 6, 2026

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_operator

This PR removes the reference to "An Isotropic 3x3x3 Volume Gradient Operator", Irwin Sobel, 1995, as it cannot be found anymore.

Added a UseLegacyCoefficients option to SobelOperator, which allows the user to specify whether or not SobelOperator::GenerateCoefficients() should still produce the old coefficients for a 3D kernel. Enabled by default.

Removed the static_assert from SobelOperator that allowed only 2D and 3D. Added checks for 1D and 4D to the unit test.

@github-actions github-actions bot added type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct area:Core Issues affecting the Core module area:Documentation Issues affecting the Documentation module labels Jan 6, 2026
Copy link
Member

@dzenanz dzenanz left a 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.

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Jan 6, 2026

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 Fill(const CoefficientVector &) member function, which is now 2D and 3D only:

SobelOperator<TPixel, VDimension, TAllocator>::Fill(const CoefficientVector & coeff)

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)
Copy link
Member

@hjmjohnson hjmjohnson Jan 6, 2026

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.

Copy link
Contributor Author

@N-Dekker N-Dekker Jan 7, 2026

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?

Copy link
Contributor Author

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!

@N-Dekker N-Dekker force-pushed the Make-SobelOperator-3D-consistent branch 2 times, most recently from 54d23a1 to 730a117 Compare January 7, 2026 17:07
@N-Dekker
Copy link
Contributor Author

N-Dekker commented Jan 7, 2026

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.)

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Jan 8, 2026

/azp run ITK.Linux.Python

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Jan 8, 2026

/azp run ITK.Linux

Comment on lines +134 to +138
const auto & kernel = sobelKernels[VDimension - 1 - direction];
return CoefficientVector(kernel.cbegin(), kernel.cend());
Copy link
Contributor Author

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 🤷

@N-Dekker N-Dekker force-pushed the Make-SobelOperator-3D-consistent branch from 4c20b2c to a20d509 Compare January 8, 2026 22:05
@blowekamp
Copy link
Member

When I am building SimpleITK with Elastic against the current ITK main ( or work derived from ). I am getting the following compilation error:

Elastix/Components/Metrics/NormalizedGradientCorrelation/elxNormalizedGradientCorrelationMetric.cxx:21:1:   required from here
ITK-prefix/include/ITK-6.0/itkSobelOperator.h:108:21: error: static assertion failed: The ND version of the Sobel operator has not been implemented. Currently only 2D and 3D versions are available.

Will this PR fix this issue?

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Jan 9, 2026

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:

  • replace hard-coded Sobel kernel coordinates with coordinates computed by an algorithm (based on the AI generated function that Hans provided at A ND Sobel implementation is needed. #5702)
  • add a legacy option to get the old hard-coded coordinates
  • test both the algorithm based and the old hard-coded coordinates

The follow-up PR should then:

  • extend the Sobel kernel operator to 4-D
  • test 4-D

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 😃

@blowekamp
Copy link
Member

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.

The error comes from elastix usage of the Sobel operator:
https://open.cdash.org/viewBuildError.php?buildid=10948517

Should I wait for this PR and subsequent to fix Elastix or update the Elastic version in SimpleITK? Or something else?

Comment on lines +137 to +141
void
UseLegacyCoefficients(const bool useLegacyCoefficients)
{
m_UseLegacyCoefficients = useLegacyCoefficients;
}
Copy link
Contributor Author

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?

@N-Dekker N-Dekker force-pushed the Make-SobelOperator-3D-consistent branch from a20d509 to 873d1df Compare January 9, 2026 22:44
@github-actions github-actions bot removed the type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct label Jan 9, 2026
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.
@N-Dekker N-Dekker force-pushed the Make-SobelOperator-3D-consistent branch from 873d1df to 43801b2 Compare January 10, 2026 10:07
@github-actions github-actions bot added the type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct label Jan 10, 2026
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."
@N-Dekker N-Dekker changed the title WIP: A ND Sobel implementation! Make 3D SobelOperator consistent with 2D, add UseLegacyCoefficients, add ND support Jan 10, 2026
@N-Dekker N-Dekker marked this pull request as ready for review January 10, 2026 11:33
@N-Dekker
Copy link
Contributor Author

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.

@dzenanz Addressed by the last commit: ENH: Add ND support to SobelOperator

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Jan 10, 2026

The error comes from elastix usage of the Sobel operator: https://open.cdash.org/viewBuildError.php?buildid=10948517

Should I wait for this PR and subsequent to fix Elastix or update the Elastic version in SimpleITK? Or something else?

@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 main branch.) But your comment is very helpful as it revealed an elastix issue: SuperElastix/elastix#1391 ! And yes, this PR should fix the issue 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Core Issues affecting the Core module area:Documentation Issues affecting the Documentation module type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants