From dc0a276edce12d9d813d9efd5b106fa848059520 Mon Sep 17 00:00:00 2001 From: Murat Raimbekov Date: Wed, 11 Feb 2026 18:06:26 +0600 Subject: [PATCH] Merge pull request #28324 from raimbekovm:fix-kaze-charbonnier features2d: add missing KAZE DIFF_CHARBONNIER support #28324 ### Description This PR fixes the missing implementation for `DIFF_CHARBONNIER` diffusivity type in KAZE feature detector. ### Changes - Added `charbonnier_diffusivity()` call for `DIFF_CHARBONNIER` type in `Create_Nonlinear_Scale_Space()` - Added proper error handling for unsupported diffusivity types ### Problem When using KAZE with `DIFF_CHARBONNIER` diffusivity type, keypoint coordinates were not computed at subpixel level, because the code lacked the specific handling for this diffusivity mode. ### Solution The `charbonnier_diffusivity()` function already exists in `nldiffusion_functions.cpp`, it just wasn't being called. This PR adds the missing `else if` branch to call it, matching the pattern already implemented in `AKAZEFeatures.cpp`. Fixes #27134 ### Pull Request Readiness Checklist - [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 copyleft license. - [x] The PR is proposed to the proper branch. - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/features2d/src/kaze/KAZEFeatures.cpp | 4 ++ modules/features2d/test/test_akaze.cpp | 41 ++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/modules/features2d/src/kaze/KAZEFeatures.cpp b/modules/features2d/src/kaze/KAZEFeatures.cpp index 72327e8683..30e0390359 100644 --- a/modules/features2d/src/kaze/KAZEFeatures.cpp +++ b/modules/features2d/src/kaze/KAZEFeatures.cpp @@ -128,6 +128,10 @@ int KAZEFeatures::Create_Nonlinear_Scale_Space(const Mat &img) pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); else if (options_.diffusivity == KAZE::DIFF_WEICKERT) weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + else if (options_.diffusivity == KAZE::DIFF_CHARBONNIER) + charbonnier_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + else + CV_Error_(Error::StsError, ("Diffusivity is not supported: %d", static_cast(options_.diffusivity))); // Perform FED n inner steps for (int j = 0; j < nsteps_[i - 1]; j++) diff --git a/modules/features2d/test/test_akaze.cpp b/modules/features2d/test/test_akaze.cpp index aafa8a7545..e24894129a 100644 --- a/modules/features2d/test/test_akaze.cpp +++ b/modules/features2d/test/test_akaze.cpp @@ -45,4 +45,45 @@ TEST(Features2d_AKAZE, uninitialized_and_nans) akaze->detectAndCompute(b1, noArray(), keypoints, desc); } +// Test for https://github.com/opencv/opencv/issues/27134 +TEST(Features2d_KAZE, diffusivity_charbonnier) +{ + Mat testImg(200, 200, CV_8U); + RNG rng(42); + rng.fill(testImg, RNG::UNIFORM, Scalar(0), Scalar(255), true); + + // KAZE with DIFF_CHARBONNIER + Ptr kaze_charbonnier = KAZE::create(false, false, 0.001f, 4, 4, KAZE::DIFF_CHARBONNIER); + vector kps_charbonnier; + Mat desc_charbonnier; + kaze_charbonnier->detectAndCompute(testImg, noArray(), kps_charbonnier, desc_charbonnier); + + // KAZE with DIFF_PM_G2 (default) + Ptr kaze_pm_g2 = KAZE::create(false, false, 0.001f, 4, 4, KAZE::DIFF_PM_G2); + vector kps_pm_g2; + Mat desc_pm_g2; + kaze_pm_g2->detectAndCompute(testImg, noArray(), kps_pm_g2, desc_pm_g2); + + // Both should detect keypoints + ASSERT_FALSE(kps_charbonnier.empty()); + ASSERT_FALSE(kps_pm_g2.empty()); + + // Check subpixel accuracy for DIFF_CHARBONNIER (issue #27134) + bool hasSubpixel = false; + for (size_t i = 0; i < kps_charbonnier.size(); i++) + { + float fx = kps_charbonnier[i].pt.x - std::floor(kps_charbonnier[i].pt.x); + float fy = kps_charbonnier[i].pt.y - std::floor(kps_charbonnier[i].pt.y); + if (fx > 1e-5f || fy > 1e-5f) + { + hasSubpixel = true; + break; + } + } + EXPECT_TRUE(hasSubpixel) << "KAZE with DIFF_CHARBONNIER should have subpixel keypoint coordinates"; + + // Descriptor dimensions should match + ASSERT_EQ(desc_charbonnier.cols, desc_pm_g2.cols); +} + }} // namespace