From 0cbde9ceeb779ae3448755f5b964f7e3de6d65c1 Mon Sep 17 00:00:00 2001 From: Shiven Solanki Date: Sun, 8 Nov 2026 14:16:50 +0800 Subject: [PATCH] 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 --- modules/core/src/mathfuncs_core.simd.hpp | 2 +- modules/core/test/test_arithm.cpp | 29 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp index 8c4dbffe1d..ec0e948f3f 100644 --- a/modules/core/src/mathfuncs_core.simd.hpp +++ b/modules/core/src/mathfuncs_core.simd.hpp @@ -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; } diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index 9da3f03586..ec120816ab 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -3358,6 +3358,35 @@ INSTANTIATE_TEST_CASE_P(Core_CartPolar, Core_CartToPolar_inplace, ) ); +typedef testing::TestWithParam 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;