mirror of
https://github.com/opencv/opencv.git
synced 2026-07-28 23:03:03 +04:00
71c2b944a1
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.
145 lines
5.0 KiB
C++
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);
|
|
}
|
|
|
|
}}
|