1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-28 23:03:03 +04:00
Files
opencv/modules/photo/test/test_chromatic_aberration.cpp
Mykhailo Trushch 71c2b944a1 Merge pull request #27491 from MykhailoTrushch:ca_cpp
Chromatic aberration correction #27491

Merge with https://github.com/opencv/opencv_extra/pull/1266

### 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.
- [x] The feature is well documented and sample code can be built with the project CMake

Parent issue: https://github.com/opencv/opencv/issues/27206
Related PR: [#27490](https://github.com/opencv/opencv/pull/27490)

This PR adds chromatic aberration correction in C++ based on calibration data from the python app.

This code adds a function for chromatic aberration correction based on the calibration file (Mat correctChromaticAberration(InputArray image, const String& calibration_file)), and a class ChromaticAberrationCorrector which can be used to correct images of the same camera under the same settings (so for the same calibration data that is initialized in the beginning).

Also, basic functionality and performance tests are added.
2025-12-19 15:02:57 +03:00

145 lines
5.0 KiB
C++

// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "test_precomp.hpp"
namespace opencv_test { namespace {
const unsigned long EXPECTED_COEFFS_SIZE = 78;
class ChromaticAberrationTest : public testing::Test
{
protected:
std::string test_yaml_file;
cv::Mat test_image;
cv::Mat coeffMat;
cv::Mat corrected;
int degree = -1;
Size calib_size = {-1, -1};
void SetUp() override
{
string data_path = cvtest::TS::ptr()->get_data_path();
ASSERT_TRUE(!data_path.empty()) << "OPENCV_TEST_DATA_PATH not set";
test_yaml_file = std::string(data_path) + "cameracalibration/chromatic_aberration/ca_photo_calib.yaml";
test_image = cv::imread(std::string(data_path) + "cameracalibration/chromatic_aberration/ca_photo.png");
ASSERT_FALSE(test_image.empty()) << "Failed to load test image";
}
};
TEST_F(ChromaticAberrationTest, LoadCalibAndCorrectImage)
{
FileStorage fs(test_yaml_file, FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
ASSERT_FALSE(coeffMat.empty());
ASSERT_EQ(coeffMat.type(), CV_32F);
ASSERT_EQ(coeffMat.rows, 4);
ASSERT_GT(coeffMat.cols, 0);
ASSERT_EQ((degree + 1) * (degree + 2) / 2, coeffMat.cols);
ASSERT_GT(calib_size.width, 0);
ASSERT_GT(calib_size.height, 0);
ASSERT_EQ(test_image.cols, calib_size.width);
ASSERT_EQ(test_image.rows, calib_size.height);
ASSERT_NO_THROW(cv::correctChromaticAberration(test_image, coeffMat, corrected, calib_size, degree));
EXPECT_EQ(corrected.size(), test_image.size());
EXPECT_EQ(corrected.channels(), test_image.channels());
EXPECT_EQ(corrected.type(), test_image.type());
cv::Mat diff; cv::absdiff(test_image, corrected, diff);
cv::Scalar s = cv::sum(diff);
EXPECT_GT(s[0] + s[1] + s[2], 0.0);
}
TEST_F(ChromaticAberrationTest, YAMLContentsAsExpected)
{
cv::FileStorage fs(test_yaml_file, cv::FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
cv::FileNode red_node = fs["red_channel"];
cv::FileNode blue_node = fs["blue_channel"];
EXPECT_TRUE(red_node.isMap());
EXPECT_TRUE(blue_node.isMap());
std::vector<double> coeffs_x;
red_node["coeffs_x"] >> coeffs_x;
EXPECT_EQ(coeffs_x.size(), EXPECTED_COEFFS_SIZE);
blue_node["coeffs_x"] >> coeffs_x;
EXPECT_EQ(coeffs_x.size(), EXPECTED_COEFFS_SIZE);
std::vector<double> coeffs_y;
red_node["coeffs_y"] >> coeffs_y;
EXPECT_EQ(coeffs_y.size(), EXPECTED_COEFFS_SIZE);
blue_node["coeffs_y"] >> coeffs_y;
EXPECT_EQ(coeffs_y.size(), EXPECTED_COEFFS_SIZE);
fs.release();
}
TEST_F(ChromaticAberrationTest, InvalidSingleChannel)
{
FileStorage fs(test_yaml_file, FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
cv::Mat gray;
cv::cvtColor(test_image, gray, cv::COLOR_BGR2GRAY);
EXPECT_THROW(cv::correctChromaticAberration(gray, coeffMat, corrected, calib_size, degree),
cv::Exception);
}
TEST_F(ChromaticAberrationTest, EmptyCoeffMat)
{
FileStorage fs(test_yaml_file, FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
cv::Mat emptyCoeff;
EXPECT_THROW(cv::correctChromaticAberration(test_image, emptyCoeff, corrected, calib_size, degree),
cv::Exception);
}
TEST_F(ChromaticAberrationTest, MismatchedImageSize)
{
FileStorage fs(test_yaml_file, FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
cv::Mat resized;
cv::resize(test_image, resized, cv::Size(test_image.cols/2, test_image.rows/2));
EXPECT_THROW(cv::correctChromaticAberration(resized, coeffMat, corrected, calib_size, degree),
cv::Exception);
}
TEST_F(ChromaticAberrationTest, WrongCoeffType)
{
FileStorage fs(test_yaml_file, FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
cv::Mat wrongType;
coeffMat.convertTo(wrongType, CV_64F);
EXPECT_THROW(cv::correctChromaticAberration(test_image, wrongType, corrected, calib_size, degree),
cv::Exception);
}
TEST_F(ChromaticAberrationTest, DegreeDoesNotMatchCoeffCols)
{
FileStorage fs(test_yaml_file, FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
int wrongDegree = std::max(1, degree - 1);
ASSERT_NE((wrongDegree + 1) * (wrongDegree + 2) / 2, coeffMat.cols);
EXPECT_THROW(cv::correctChromaticAberration(test_image, coeffMat, corrected, calib_size, wrongDegree),
cv::Exception);
}
}}