diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-05-05 00:42:36 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-10-14 20:50:53 +0200 |
| commit | 2e9e778462a86e9c35e1758eb95cced4ddd5468d (patch) | |
| tree | 8cd6b2bbfbfa564fc5c81cafd76c4abdd7923e9c /test | |
| parent | 6ee5579ba47f120f731c11f47417d690d6ce1325 (diff) | |
Move rgb_xyz into colour_conversion.
Diffstat (limited to 'test')
| -rw-r--r-- | test/colour_conversion_test.cc | 222 | ||||
| -rw-r--r-- | test/decryption_test.cc | 1 | ||||
| -rw-r--r-- | test/rgb_xyz_test.cc | 256 | ||||
| -rw-r--r-- | test/round_trip_test.cc | 23 | ||||
| -rw-r--r-- | test/wscript | 1 |
5 files changed, 232 insertions, 271 deletions
diff --git a/test/colour_conversion_test.cc b/test/colour_conversion_test.cc index 54708753..f445d2e6 100644 --- a/test/colour_conversion_test.cc +++ b/test/colour_conversion_test.cc @@ -31,16 +31,36 @@ files in the program, then also delete it here. */ -#include "gamma_transfer_function.h" + #include "colour_conversion.h" +#include "gamma_transfer_function.h" #include "modified_gamma_transfer_function.h" +#include "openjpeg_image.h" +#include "piecewise_lut.h" +#include "stream_operators.h" +#include <boost/bind/bind.hpp> +#include <boost/random.hpp> +#include <boost/scoped_array.hpp> #include <boost/test/unit_test.hpp> #include <cmath> + +using std::cout; +using std::list; +using std::make_shared; +using std::max; using std::pow; using std::shared_ptr; +using std::shared_ptr; +using std::string; +using boost::optional; +using boost::scoped_array; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif using namespace dcp; + static void check_gamma (shared_ptr<const TransferFunction> tf, int bit_depth, bool inverse, float gamma) { @@ -154,3 +174,203 @@ BOOST_AUTO_TEST_CASE (colour_conversion_bradford_test) BOOST_CHECK_CLOSE (b(2, 1), 0.0119945, 0.1); BOOST_CHECK_CLOSE (b(2, 2), 0.7785377, 0.1); } + + +static +void +rgb_xyz_test_case(std::function<void (uint16_t*)> write_pixel) +{ + srand(0); + dcp::Size const size(640, 480); + + scoped_array<uint8_t> rgb(new uint8_t[size.width * size.height * 6]); + for (int y = 0; y < size.height; ++y) { + uint16_t* p = reinterpret_cast<uint16_t*>(rgb.get() + y * size.width * 6); + for (int x = 0; x < size.width; ++x) { + write_pixel(p); + p += 3; + } + } + + auto xyz = dcp::rgb_to_xyz(rgb.get(), size, size.width * 6, dcp::ColourConversion::srgb_to_dcp()); + + for (int y = 0; y < size.height; ++y) { + uint16_t* p = reinterpret_cast<uint16_t*>(rgb.get() + y * size.width * 6); + for (int x = 0; x < size.width; ++x) { + + double cr = *p++ / 65535.0; + double cg = *p++ / 65535.0; + double cb = *p++ / 65535.0; + + /* Input gamma */ + + if (cr < 0.04045) { + cr /= 12.92; + } else { + cr = pow ((cr + 0.055) / 1.055, 2.4); + } + + if (cg < 0.04045) { + cg /= 12.92; + } else { + cg = pow ((cg + 0.055) / 1.055, 2.4); + } + + if (cb < 0.04045) { + cb /= 12.92; + } else { + cb = pow ((cb + 0.055) / 1.055, 2.4); + } + + /* Matrix */ + + double cx = cr * 0.4124564 + cg * 0.3575761 + cb * 0.1804375; + double cy = cr * 0.2126729 + cg * 0.7151522 + cb * 0.0721750; + double cz = cr * 0.0193339 + cg * 0.1191920 + cb * 0.9503041; + + /* Compand */ + + cx *= 48 / 52.37; + cy *= 48 / 52.37; + cz *= 48 / 52.37; + + /* Output gamma */ + + cx = pow (cx, 1 / 2.6); + cy = pow (cy, 1 / 2.6); + cz = pow (cz, 1 / 2.6); + + BOOST_REQUIRE_CLOSE (cx * 4095, xyz->data(0)[y * size.width + x], 1); + BOOST_REQUIRE_CLOSE (cy * 4095, xyz->data(1)[y * size.width + x], 1); + BOOST_REQUIRE_CLOSE (cz * 4095, xyz->data(2)[y * size.width + x], 1); + } + } +} + + +/** Convert a test image from sRGB to XYZ and check that the transforms are right */ +BOOST_AUTO_TEST_CASE(rgb_xyz_test) +{ + { + int counter = 0; + rgb_xyz_test_case([&counter](uint16_t* p) { + p[0] = p[1] = p[2] = (counter << 4); + ++counter; + if (counter >= 4096) { + counter = 0; + } + }); + } + + boost::random::mt19937 rng(1); + boost::random::uniform_int_distribution<> dist(0, 4095); + + rgb_xyz_test_case([&rng, &dist](uint16_t* p) { + p[0] = dist(rng) << 4; + p[1] = dist(rng) << 4; + p[2] = dist(rng) << 4; + }); +} + + +/** Check the piecewise LUT that is used for inverse gamma calculation */ +BOOST_AUTO_TEST_CASE (rgb_xyz_lut_test) +{ + auto conversion = dcp::ColourConversion::rec709_to_dcp(); + auto lut = dcp::make_inverse_gamma_lut(conversion.out_j2k()); + + for (double x = 0; x < 1; x += 0.000001) { + BOOST_CHECK(std::abs(lut.lookup(x) - lrint(pow(x, 1 / 2.6) * 4095)) < 2); + } +} + + +static list<string> notes; + +static void +note_handler (dcp::NoteType n, string s) +{ + BOOST_REQUIRE_EQUAL (n, dcp::NoteType::NOTE); + notes.push_back (s); +} + +/** Check that xyz_to_rgb clamps XYZ values correctly */ +BOOST_AUTO_TEST_CASE (xyz_rgb_range_test) +{ + auto xyz = make_shared<dcp::OpenJPEGImage>(dcp::Size(2, 2)); + + xyz->data(0)[0] = -4; + xyz->data(0)[1] = 6901; + xyz->data(0)[2] = 0; + xyz->data(0)[3] = 4095; + xyz->data(1)[0] = -4; + xyz->data(1)[1] = 6901; + xyz->data(1)[2] = 0; + xyz->data(1)[3] = 4095; + xyz->data(2)[0] = -4; + xyz->data(2)[1] = 6901; + xyz->data(2)[2] = 0; + xyz->data(2)[3] = 4095; + + scoped_array<uint8_t> rgb (new uint8_t[2 * 2 * 6]); + + notes.clear (); + dcp::xyz_to_rgb ( + xyz, dcp::ColourConversion::srgb_to_dcp(), rgb.get(), 2 * 6, boost::optional<dcp::NoteHandler>(boost::bind(¬e_handler, _1, _2)) + ); + + /* The 6 out-of-range samples should have been noted */ + BOOST_REQUIRE_EQUAL (notes.size(), 6U); + auto i = notes.begin (); + BOOST_REQUIRE_EQUAL (*i++, "XYZ value -4 out of range"); + BOOST_REQUIRE_EQUAL (*i++, "XYZ value -4 out of range"); + BOOST_REQUIRE_EQUAL (*i++, "XYZ value -4 out of range"); + BOOST_REQUIRE_EQUAL (*i++, "XYZ value 6901 out of range"); + BOOST_REQUIRE_EQUAL (*i++, "XYZ value 6901 out of range"); + BOOST_REQUIRE_EQUAL (*i++, "XYZ value 6901 out of range"); + + /* And those samples should have been clamped, so check that they give the same result + as inputs at the extremes (0 and 4095). + */ + + auto buffer = reinterpret_cast<uint16_t*> (rgb.get ()); + BOOST_REQUIRE_EQUAL (buffer[0 * 3 + 0], buffer[2 * 3 + 1]); + BOOST_REQUIRE_EQUAL (buffer[0 * 3 + 1], buffer[2 * 3 + 1]); + BOOST_REQUIRE_EQUAL (buffer[0 * 3 + 2], buffer[2 * 3 + 2]); + + BOOST_REQUIRE_EQUAL (buffer[1 * 3 + 0], buffer[3 * 3 + 0]); + BOOST_REQUIRE_EQUAL (buffer[1 * 3 + 1], buffer[3 * 3 + 1]); + BOOST_REQUIRE_EQUAL (buffer[1 * 3 + 2], buffer[3 * 3 + 2]); +} + +/** Convert an image from RGB to XYZ and back again */ +BOOST_AUTO_TEST_CASE (rgb_xyz_round_trip_test) +{ + srand (0); + dcp::Size const size (640, 480); + + scoped_array<uint8_t> rgb (new uint8_t[size.width * size.height * 6]); + for (int y = 0; y < size.height; ++y) { + uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 6); + for (int x = 0; x < size.width; ++x) { + /* Write a 12-bit random number for each component */ + for (int c = 0; c < 3; ++c) { + *p = (rand () & 0xfff) << 4; + ++p; + } + } + } + + auto xyz = dcp::rgb_to_xyz(rgb.get(), size, size.width * 6, dcp::ColourConversion::srgb_to_dcp()); + scoped_array<uint8_t> back (new uint8_t[size.width * size.height * 6]); + dcp::xyz_to_rgb(xyz, dcp::ColourConversion::srgb_to_dcp(), back.get(), size.width * 6); + +#if 0 + uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get ()); + uint16_t* q = reinterpret_cast<uint16_t*> (back.get ()); + for (int i = 0; i < (size.width * size.height); ++i) { + /* XXX: doesn't quite work */ + // BOOST_REQUIRE_EQUAL (*p++, *q++); + } +#endif +} diff --git a/test/decryption_test.cc b/test/decryption_test.cc index 66402e24..061b50c8 100644 --- a/test/decryption_test.cc +++ b/test/decryption_test.cc @@ -47,7 +47,6 @@ #include "reel_picture_asset.h" #include "reel_sound_asset.h" #include "reel_smpte_text_asset.h" -#include "rgb_xyz.h" #include "smpte_text_asset.h" #include "sound_asset.h" #include "sound_asset_writer.h" diff --git a/test/rgb_xyz_test.cc b/test/rgb_xyz_test.cc deleted file mode 100644 index 59ace3d2..00000000 --- a/test/rgb_xyz_test.cc +++ /dev/null @@ -1,256 +0,0 @@ -/* - Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net> - - This file is part of libdcp. - - libdcp is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - libdcp is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libdcp. If not, see <http://www.gnu.org/licenses/>. - - In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - - You must obey the GNU General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. -*/ - - -#include "colour_conversion.h" -#include "openjpeg_image.h" -#include "piecewise_lut.h" -#include "rgb_xyz.h" -#include "stream_operators.h" -#include <boost/bind/bind.hpp> -#include <boost/random.hpp> -#include <boost/scoped_array.hpp> -#include <boost/test/unit_test.hpp> - - -using std::cout; -using std::list; -using std::make_shared; -using std::max; -using std::shared_ptr; -using std::string; -using boost::optional; -using boost::scoped_array; -#if BOOST_VERSION >= 106100 -using namespace boost::placeholders; -#endif - - -static -void -rgb_xyz_test_case (std::function<void (uint16_t*)> write_pixel) -{ - srand (0); - dcp::Size const size (640, 480); - - scoped_array<uint8_t> rgb (new uint8_t[size.width * size.height * 6]); - for (int y = 0; y < size.height; ++y) { - uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 6); - for (int x = 0; x < size.width; ++x) { - write_pixel (p); - p += 3; - } - } - - auto xyz = dcp::rgb_to_xyz(rgb.get(), size, size.width * 6, dcp::ColourConversion::srgb_to_dcp()); - - for (int y = 0; y < size.height; ++y) { - uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 6); - for (int x = 0; x < size.width; ++x) { - - double cr = *p++ / 65535.0; - double cg = *p++ / 65535.0; - double cb = *p++ / 65535.0; - - /* Input gamma */ - - if (cr < 0.04045) { - cr /= 12.92; - } else { - cr = pow ((cr + 0.055) / 1.055, 2.4); - } - - if (cg < 0.04045) { - cg /= 12.92; - } else { - cg = pow ((cg + 0.055) / 1.055, 2.4); - } - - if (cb < 0.04045) { - cb /= 12.92; - } else { - cb = pow ((cb + 0.055) / 1.055, 2.4); - } - - /* Matrix */ - - double cx = cr * 0.4124564 + cg * 0.3575761 + cb * 0.1804375; - double cy = cr * 0.2126729 + cg * 0.7151522 + cb * 0.0721750; - double cz = cr * 0.0193339 + cg * 0.1191920 + cb * 0.9503041; - - /* Compand */ - - cx *= 48 / 52.37; - cy *= 48 / 52.37; - cz *= 48 / 52.37; - - /* Output gamma */ - - cx = pow (cx, 1 / 2.6); - cy = pow (cy, 1 / 2.6); - cz = pow (cz, 1 / 2.6); - - BOOST_REQUIRE_CLOSE (cx * 4095, xyz->data(0)[y * size.width + x], 1); - BOOST_REQUIRE_CLOSE (cy * 4095, xyz->data(1)[y * size.width + x], 1); - BOOST_REQUIRE_CLOSE (cz * 4095, xyz->data(2)[y * size.width + x], 1); - } - } -} - - -/** Convert a test image from sRGB to XYZ and check that the transforms are right */ -BOOST_AUTO_TEST_CASE (rgb_xyz_test) -{ - { - int counter = 0; - rgb_xyz_test_case ([&counter](uint16_t* p) { - p[0] = p[1] = p[2] = (counter << 4); - ++counter; - if (counter >= 4096) { - counter = 0; - } - }); - } - - boost::random::mt19937 rng(1); - boost::random::uniform_int_distribution<> dist(0, 4095); - - rgb_xyz_test_case ([&rng, &dist](uint16_t* p) { - p[0] = dist(rng) << 4; - p[1] = dist(rng) << 4; - p[2] = dist(rng) << 4; - }); -} - - -/** Check the piecewise LUT that is used for inverse gamma calculation */ -BOOST_AUTO_TEST_CASE (rgb_xyz_lut_test) -{ - auto conversion = dcp::ColourConversion::rec709_to_dcp(); - auto lut = dcp::make_inverse_gamma_lut(conversion.out_j2k()); - - for (double x = 0; x < 1; x += 0.000001) { - BOOST_CHECK(std::abs(lut.lookup(x) - lrint(pow(x, 1 / 2.6) * 4095)) < 2); - } -} - - -static list<string> notes; - -static void -note_handler (dcp::NoteType n, string s) -{ - BOOST_REQUIRE_EQUAL (n, dcp::NoteType::NOTE); - notes.push_back (s); -} - -/** Check that xyz_to_rgb clamps XYZ values correctly */ -BOOST_AUTO_TEST_CASE (xyz_rgb_range_test) -{ - auto xyz = make_shared<dcp::OpenJPEGImage>(dcp::Size(2, 2)); - - xyz->data(0)[0] = -4; - xyz->data(0)[1] = 6901; - xyz->data(0)[2] = 0; - xyz->data(0)[3] = 4095; - xyz->data(1)[0] = -4; - xyz->data(1)[1] = 6901; - xyz->data(1)[2] = 0; - xyz->data(1)[3] = 4095; - xyz->data(2)[0] = -4; - xyz->data(2)[1] = 6901; - xyz->data(2)[2] = 0; - xyz->data(2)[3] = 4095; - - scoped_array<uint8_t> rgb (new uint8_t[2 * 2 * 6]); - - notes.clear (); - dcp::xyz_to_rgb ( - xyz, dcp::ColourConversion::srgb_to_dcp(), rgb.get(), 2 * 6, boost::optional<dcp::NoteHandler>(boost::bind(¬e_handler, _1, _2)) - ); - - /* The 6 out-of-range samples should have been noted */ - BOOST_REQUIRE_EQUAL (notes.size(), 6U); - auto i = notes.begin (); - BOOST_REQUIRE_EQUAL (*i++, "XYZ value -4 out of range"); - BOOST_REQUIRE_EQUAL (*i++, "XYZ value -4 out of range"); - BOOST_REQUIRE_EQUAL (*i++, "XYZ value -4 out of range"); - BOOST_REQUIRE_EQUAL (*i++, "XYZ value 6901 out of range"); - BOOST_REQUIRE_EQUAL (*i++, "XYZ value 6901 out of range"); - BOOST_REQUIRE_EQUAL (*i++, "XYZ value 6901 out of range"); - - /* And those samples should have been clamped, so check that they give the same result - as inputs at the extremes (0 and 4095). - */ - - auto buffer = reinterpret_cast<uint16_t*> (rgb.get ()); - BOOST_REQUIRE_EQUAL (buffer[0 * 3 + 0], buffer[2 * 3 + 1]); - BOOST_REQUIRE_EQUAL (buffer[0 * 3 + 1], buffer[2 * 3 + 1]); - BOOST_REQUIRE_EQUAL (buffer[0 * 3 + 2], buffer[2 * 3 + 2]); - - BOOST_REQUIRE_EQUAL (buffer[1 * 3 + 0], buffer[3 * 3 + 0]); - BOOST_REQUIRE_EQUAL (buffer[1 * 3 + 1], buffer[3 * 3 + 1]); - BOOST_REQUIRE_EQUAL (buffer[1 * 3 + 2], buffer[3 * 3 + 2]); -} - -/** Convert an image from RGB to XYZ and back again */ -BOOST_AUTO_TEST_CASE (rgb_xyz_round_trip_test) -{ - srand (0); - dcp::Size const size (640, 480); - - scoped_array<uint8_t> rgb (new uint8_t[size.width * size.height * 6]); - for (int y = 0; y < size.height; ++y) { - uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 6); - for (int x = 0; x < size.width; ++x) { - /* Write a 12-bit random number for each component */ - for (int c = 0; c < 3; ++c) { - *p = (rand () & 0xfff) << 4; - ++p; - } - } - } - - auto xyz = dcp::rgb_to_xyz(rgb.get(), size, size.width * 6, dcp::ColourConversion::srgb_to_dcp()); - scoped_array<uint8_t> back (new uint8_t[size.width * size.height * 6]); - dcp::xyz_to_rgb(xyz, dcp::ColourConversion::srgb_to_dcp(), back.get(), size.width * 6); - -#if 0 - uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get ()); - uint16_t* q = reinterpret_cast<uint16_t*> (back.get ()); - for (int i = 0; i < (size.width * size.height); ++i) { - /* XXX: doesn't quite work */ - // BOOST_REQUIRE_EQUAL (*p++, *q++); - } -#endif -} diff --git a/test/round_trip_test.cc b/test/round_trip_test.cc index 1e87912a..c512c6a8 100644 --- a/test/round_trip_test.cc +++ b/test/round_trip_test.cc @@ -31,24 +31,23 @@ files in the program, then also delete it here. */ + #include "certificate.h" +#include "certificate_chain.h" +#include "colour_conversion.h" +#include "cpl.h" #include "decrypted_kdm.h" #include "encrypted_kdm.h" -#include "certificate_chain.h" #include "mono_j2k_picture_asset.h" -#include "sound_asset.h" -#include "reel.h" -#include "test.h" -#include "cpl.h" -#include "mono_j2k_picture_frame.h" -#include "certificate_chain.h" -#include "mono_j2k_picture_asset_writer.h" #include "mono_j2k_picture_asset_reader.h" -#include "reel_picture_asset.h" -#include "reel_mono_picture_asset.h" +#include "mono_j2k_picture_asset_writer.h" +#include "mono_j2k_picture_frame.h" #include "openjpeg_image.h" -#include "rgb_xyz.h" -#include "colour_conversion.h" +#include "reel.h" +#include "reel_mono_picture_asset.h" +#include "reel_picture_asset.h" +#include "sound_asset.h" +#include "test.h" #include <boost/test/unit_test.hpp> #include <boost/scoped_array.hpp> #include <iostream> diff --git a/test/wscript b/test/wscript index 5e94b1f7..ebb3a1e3 100644 --- a/test/wscript +++ b/test/wscript @@ -107,7 +107,6 @@ def build(bld): read_change_write_test.cc reel_asset_test.cc recovery_test.cc - rgb_xyz_test.cc round_trip_test.cc scope_guard_test.cc search_test.cc |
