diff --git a/modules/imgcodecs/test/test_common.cpp b/modules/imgcodecs/test/test_common.cpp new file mode 100644 index 0000000000..a0addafa80 --- /dev/null +++ b/modules/imgcodecs/test/test_common.cpp @@ -0,0 +1,51 @@ +// 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" +#include "test_common.hpp" + +namespace opencv_test { + +static +Mat generateTestImageBGR_() +{ + Size sz(640, 480); + Mat result(sz, CV_8UC3, Scalar::all(0)); + + const string fname = cvtest::findDataFile("../cv/shared/baboon.png"); + Mat image = imread(fname, IMREAD_COLOR); + CV_Assert(!image.empty()); + CV_CheckEQ(image.size(), Size(512, 512), ""); + Rect roi((640-512) / 2, 0, 512, 480); + image(Rect(0, 0, 512, 480)).copyTo(result(roi)); + result(Rect(0, 0, 5, 5)).setTo(Scalar(0, 0, 255)); // R + result(Rect(5, 0, 5, 5)).setTo(Scalar(0, 255, 0)); // G + result(Rect(10, 0, 5, 5)).setTo(Scalar(255, 0, 0)); // B + result(Rect(0, 5, 5, 5)).setTo(Scalar(128, 128, 128)); // gray + //imshow("test_image", result); waitKey(); + return result; +} +Mat generateTestImageBGR() +{ + static Mat image = generateTestImageBGR_(); // initialize once + CV_Assert(!image.empty()); + return image; +} + +static +Mat generateTestImageGrayscale_() +{ + Mat imageBGR = generateTestImageBGR(); + CV_Assert(!imageBGR.empty()); + + Mat result; + cvtColor(imageBGR, result, COLOR_BGR2GRAY); + return result; +} +Mat generateTestImageGrayscale() +{ + static Mat image = generateTestImageGrayscale_(); // initialize once + return image; +} + +} // namespace diff --git a/modules/imgcodecs/test/test_common.hpp b/modules/imgcodecs/test/test_common.hpp new file mode 100644 index 0000000000..ec9a5d87fa --- /dev/null +++ b/modules/imgcodecs/test/test_common.hpp @@ -0,0 +1,15 @@ +// 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 + +#ifndef OPENCV_TEST_IMGCODECS_COMMON_HPP +#define OPENCV_TEST_IMGCODECS_COMMON_HPP + +namespace opencv_test { + +Mat generateTestImageBGR(); +Mat generateTestImageGrayscale(); + +} // namespace + +#endif // OPENCV_TEST_IMGCODECS_COMMON_HPP diff --git a/modules/imgcodecs/test/test_read_write.cpp b/modules/imgcodecs/test/test_read_write.cpp index 6396200ec9..c53cc5a46b 100644 --- a/modules/imgcodecs/test/test_read_write.cpp +++ b/modules/imgcodecs/test/test_read_write.cpp @@ -2,6 +2,7 @@ // 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" +#include "test_common.hpp" namespace opencv_test { namespace { @@ -145,45 +146,6 @@ TEST(Imgcodecs_Image, read_write_bmp) typedef string Ext; typedef testing::TestWithParam Imgcodecs_Image; -TEST_P(Imgcodecs_Image, read_write) -{ - const string ext = this->GetParam(); - const string full_name = cv::tempfile(ext.c_str()); - const string _name = TS::ptr()->get_data_path() + "../cv/shared/baboon.png"; - const double thresDbell = 32; - - Mat image = imread(_name); - image.convertTo(image, CV_8UC3); - ASSERT_FALSE(image.empty()); - - imwrite(full_name, image); - Mat loaded = imread(full_name); - ASSERT_FALSE(loaded.empty()); - - double psnr = cvtest::PSNR(loaded, image); - EXPECT_GT(psnr, thresDbell); - - vector from_file; - FILE *f = fopen(full_name.c_str(), "rb"); - fseek(f, 0, SEEK_END); - long len = ftell(f); - from_file.resize((size_t)len); - fseek(f, 0, SEEK_SET); - from_file.resize(fread(&from_file[0], 1, from_file.size(), f)); - fclose(f); - vector buf; - imencode("." + ext, image, buf); - ASSERT_EQ(buf, from_file); - - Mat buf_loaded = imdecode(Mat(buf), 1); - ASSERT_FALSE(buf_loaded.empty()); - - psnr = cvtest::PSNR(buf_loaded, image); - EXPECT_GT(psnr, thresDbell); - - EXPECT_EQ(0, remove(full_name.c_str())); -} - const string exts[] = { #ifdef HAVE_PNG "png", @@ -209,6 +171,90 @@ const string exts[] = { #endif }; +static +void test_image_io(const Mat& image, const std::string& fname, const std::string& ext, int imreadFlag, double psnrThreshold) +{ + vector buf; + ASSERT_NO_THROW(imencode("." + ext, image, buf)); + + ASSERT_NO_THROW(imwrite(fname, image)); + + FILE *f = fopen(fname.c_str(), "rb"); + fseek(f, 0, SEEK_END); + long len = ftell(f); + cout << "File size: " << len << " bytes" << endl; + EXPECT_GT(len, 1024) << "File is small. Test or implementation is broken"; + fseek(f, 0, SEEK_SET); + vector file_buf((size_t)len); + EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f)); + fclose(f); f = NULL; + + EXPECT_EQ(buf, file_buf) << "imwrite() / imencode() calls must provide the same output (bit-exact)"; + + Mat buf_loaded = imdecode(Mat(buf), imreadFlag); + EXPECT_FALSE(buf_loaded.empty()); + + Mat loaded = imread(fname, imreadFlag); + EXPECT_FALSE(loaded.empty()); + + EXPECT_EQ(0, cv::norm(loaded, buf_loaded, NORM_INF)) << "imread() and imdecode() calls must provide the same result (bit-exact)"; + + double psnr = cvtest::PSNR(loaded, image); + EXPECT_GT(psnr, psnrThreshold); + + // not necessary due bitexact check above + //double buf_psnr = cvtest::PSNR(buf_loaded, image); + //EXPECT_GT(buf_psnr, psnrThreshold); + +#if 0 // debug + if (psnr <= psnrThreshold /*|| buf_psnr <= thresDbell*/) + { + cout << "File: " << fname << endl; + imshow("origin", image); + imshow("imread", loaded); + imshow("imdecode", buf_loaded); + waitKey(); + } +#endif +} + +TEST_P(Imgcodecs_Image, read_write_BGR) +{ + const string ext = this->GetParam(); + const string fname = cv::tempfile(ext.c_str()); + + double psnrThreshold = 100; + if (ext == "jpg") + psnrThreshold = 32; + + Mat image = generateTestImageBGR(); + EXPECT_NO_THROW(test_image_io(image, fname, ext, IMREAD_COLOR, psnrThreshold)); + + EXPECT_EQ(0, remove(fname.c_str())); +} + +TEST_P(Imgcodecs_Image, read_write_GRAYSCALE) +{ + const string ext = this->GetParam(); + + if (false + || ext == "ppm" // grayscale is not implemented + || ext == "ras" // broken (black result) + ) + throw SkipTestException("GRAYSCALE mode is not supported"); + + const string fname = cv::tempfile(ext.c_str()); + + double psnrThreshold = 100; + if (ext == "jpg") + psnrThreshold = 40; + + Mat image = generateTestImageGrayscale(); + EXPECT_NO_THROW(test_image_io(image, fname, ext, IMREAD_GRAYSCALE, psnrThreshold)); + + EXPECT_EQ(0, remove(fname.c_str())); +} + INSTANTIATE_TEST_CASE_P(imgcodecs, Imgcodecs_Image, testing::ValuesIn(exts)); TEST(Imgcodecs_Image, regression_9376)