Merge pull request #30646 from sshivensolanki:core-carttopolar-inplace

core: fix in-place cartToPolar when only the magnitude overwrites an input - #30646

Fixes #30645

### Problem

`cv::cartToPolar` gives wrong results on `CV_32F` when only the magnitude output is the same `Mat` as `x` or `y`. The SIMD loop handles the leftover elements by repeating the last full block. In-place calls must skip this, but `cartToPolar32f_` only checked the angle, so the repeated block read magnitudes as inputs.

### Fix

Added `mag == X || mag == Y` to the check, so these calls handle the leftover elements with the scalar code. `polarToCart` and `magnitude` already check all their outputs.

### Tests

New `Core_CartToPolar_inplace_mag.accuracy` for `CV_32F` with lengths 1 to 39: the magnitude overwrites `x`, then `y`, and the results are compared with a call using separate outputs. The existing `Core_CartToPolar_inplace` test overwrites both inputs, so it never reached this case. `CV_64F` is not tested, because it works on separate float buffers and can't hit this bug.

On Apple M1 Pro, 28 of the 39 cases fail without the fix. All pass with the fix, along with the related `cartToPolar`, `polarToCart`, `magnitude` and `phase` tests, with OpenCL on and off.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Shiven Solanki
2026-11-08 14:16:50 +08:00
committed by GitHub
parent ccf4ca3243
commit 0cbde9ceeb
2 changed files with 30 additions and 1 deletions

View File

@@ -119,7 +119,7 @@ static void cartToPolar32f_(const float *X, const float *Y, float *mag, float *a
// if it's inplace operation, we cannot repeatedly process
// the tail for the second time, so we have to use the
// scalar code
if( i == 0 || angle == X || angle == Y )
if( i == 0 || mag == X || mag == Y || angle == X || angle == Y )
break;
i = len - VECSZ*2;
}

View File

@@ -3358,6 +3358,35 @@ INSTANTIATE_TEST_CASE_P(Core_CartPolar, Core_CartToPolar_inplace,
)
);
typedef testing::TestWithParam<int> Core_CartToPolar_inplace_mag;
// only the magnitude overwrites an input, so the last elements must not be processed twice.
// CV_64F is not tested: it works on separate float buffers, so it can't hit this.
TEST_P(Core_CartToPolar_inplace_mag, accuracy)
{
const int len = GetParam();
cv::Mat x(1, len, CV_32F), y(1, len, CV_32F);
cv::randu(x, cv::Scalar::all(-1000), cv::Scalar::all(1000));
cv::randu(y, cv::Scalar::all(-1000), cv::Scalar::all(1000));
cv::Mat expectedMag, expectedAngle;
cv::cartToPolar(x, y, expectedMag, expectedAngle);
// magnitude into x
cv::Mat x1 = x.clone(), angle1;
cv::cartToPolar(x1, y, x1, angle1);
EXPECT_MAT_NEAR(expectedMag, x1, 1e-2);
EXPECT_MAT_NEAR(expectedAngle, angle1, 1e-2);
// magnitude into y
cv::Mat y1 = y.clone(), angle2;
cv::cartToPolar(x, y1, y1, angle2);
EXPECT_MAT_NEAR(expectedMag, y1, 1e-2);
EXPECT_MAT_NEAR(expectedAngle, angle2, 1e-2);
}
INSTANTIATE_TEST_CASE_P(Core_CartPolar, Core_CartToPolar_inplace_mag, testing::Range(1, 40));
PARAM_TEST_CASE(Core_PolarToCart_inplace, int, bool, bool)
{
int depth;