diff options
| -rw-r--r-- | benchmark/rgb_to_xyz.cc | 2 | ||||
| -rw-r--r-- | benchmark/rgb_to_xyz_avx2.cc | 69 | ||||
| -rw-r--r-- | benchmark/wscript | 2 | ||||
| -rw-r--r-- | src/rgb_xyz.cc | 68 | ||||
| -rw-r--r-- | src/rgb_xyz.h | 10 | ||||
| -rw-r--r-- | test/rgb_xyz_test.cc | 75 |
6 files changed, 224 insertions, 2 deletions
diff --git a/benchmark/rgb_to_xyz.cc b/benchmark/rgb_to_xyz.cc index eeec7f52..bdd1e426 100644 --- a/benchmark/rgb_to_xyz.cc +++ b/benchmark/rgb_to_xyz.cc @@ -62,7 +62,7 @@ main () shared_ptr<dcp::OpenJPEGImage> xyz; for (int i = 0; i < trials; ++i) { - xyz = dcp::rgb_to_xyz_avx2 (rgb.get(), size, size.width * 8, dcp::ColourConversion::srgb_to_xyz()); + xyz = dcp::rgb_to_xyz (rgb.get(), size, size.width * 8, dcp::ColourConversion::srgb_to_xyz()); } } diff --git a/benchmark/rgb_to_xyz_avx2.cc b/benchmark/rgb_to_xyz_avx2.cc new file mode 100644 index 00000000..eeec7f52 --- /dev/null +++ b/benchmark/rgb_to_xyz_avx2.cc @@ -0,0 +1,69 @@ +/* + Copyright (C) 2020 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 "openjpeg_image.h" +#include "rgb_xyz.h" +#include "colour_conversion.h" +#include <boost/scoped_array.hpp> +#include <stdint.h> + +using boost::scoped_array; +using boost::shared_ptr; + +int const trials = 256; + +int +main () +{ + srand (1); + + dcp::Size size(1998, 1080); + + scoped_array<uint8_t> rgb (new uint8_t[size.width * size.height * 8]); + for (int y = 0; y < size.height; ++y) { + uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 8); + for (int x = 0; x < size.width; ++x) { + for (int c = 0; c < 3; ++c) { + *p = (rand() & 0xfff) << 4; + ++p; + } + } + } + + shared_ptr<dcp::OpenJPEGImage> xyz; + for (int i = 0; i < trials; ++i) { + xyz = dcp::rgb_to_xyz_avx2 (rgb.get(), size, size.width * 8, dcp::ColourConversion::srgb_to_xyz()); + } +} + + diff --git a/benchmark/wscript b/benchmark/wscript index 3e748972..0b6994b9 100644 --- a/benchmark/wscript +++ b/benchmark/wscript @@ -32,7 +32,7 @@ # def build(bld): - for p in ['rgb_to_xyz']: + for p in ['rgb_to_xyz', 'rgb_to_xyz_avx2']: obj = bld(features='cxx cxxprogram') obj.name = p obj.uselib = 'BOOST_FILESYSTEM' diff --git a/src/rgb_xyz.cc b/src/rgb_xyz.cc index 6db1f1ec..5a6a909d 100644 --- a/src/rgb_xyz.cc +++ b/src/rgb_xyz.cc @@ -273,6 +273,74 @@ dcp::combined_rgb_to_xyz (ColourConversion const & conversion, double* matrix) * DCI_COEFFICIENT * 65535; } + +shared_ptr<dcp::OpenJPEGImage> +dcp::rgb_to_xyz ( + uint8_t const * rgb, + dcp::Size size, + int stride, + ColourConversion const & conversion + ) +{ + shared_ptr<OpenJPEGImage> xyz (new OpenJPEGImage (size)); + + struct { + double r, g, b; + } s; + + struct { + double x, y, z; + } d; + + double const * lut_in = conversion.in()->lut (12, false); + double const * lut_out = conversion.out()->lut (16, true); + + /* This is is the product of the RGB to XYZ matrix, the Bradford transform and the DCI companding */ + double fast_matrix[9]; + combined_rgb_to_xyz (conversion, fast_matrix); + + int clamped = 0; + int* xyz_x = xyz->data (0); + int* xyz_y = xyz->data (1); + int* xyz_z = xyz->data (2); + for (int y = 0; y < size.height; ++y) { + uint16_t const * p = reinterpret_cast<uint16_t const *> (rgb + y * stride); + for (int x = 0; x < size.width; ++x) { + + /* In gamma LUT (converting 16-bit to 12-bit) */ + s.r = lut_in[*p++ >> 4]; + s.g = lut_in[*p++ >> 4]; + s.b = lut_in[*p++ >> 4]; + + /* RGB to XYZ, Bradford transform and DCI companding */ + d.x = s.r * fast_matrix[0] + s.g * fast_matrix[1] + s.b * fast_matrix[2]; + d.y = s.r * fast_matrix[3] + s.g * fast_matrix[4] + s.b * fast_matrix[5]; + d.z = s.r * fast_matrix[6] + s.g * fast_matrix[7] + s.b * fast_matrix[8]; + + /* Clamp */ + + if (d.x < 0 || d.y < 0 || d.z < 0 || d.x > 65535 || d.y > 65535 || d.z > 65535) { + ++clamped; + } + + d.x = max (0.0, d.x); + d.y = max (0.0, d.y); + d.z = max (0.0, d.z); + d.x = min (65535.0, d.x); + d.y = min (65535.0, d.y); + d.z = min (65535.0, d.z); + + /* Out gamma LUT */ + *xyz_x++ = lrint (lut_out[lrint(d.x)] * 4095); + *xyz_y++ = lrint (lut_out[lrint(d.y)] * 4095); + *xyz_z++ = lrint (lut_out[lrint(d.z)] * 4095); + } + } + + return xyz; +} + + /** @param rgb RGBA data; packed RGBA 16:16:16:16, 48bpp, 16R, 16G, 16B, 16A * with the 2-byte value for each R/G/B/A component stored as * little-endian; i.e. AV_PIX_FMT_RGB48LE. A is ignored but necessary for diff --git a/src/rgb_xyz.h b/src/rgb_xyz.h index 56a5fd83..59b2400d 100644 --- a/src/rgb_xyz.h +++ b/src/rgb_xyz.h @@ -57,6 +57,15 @@ extern void xyz_to_rgb ( boost::optional<NoteHandler> note = boost::optional<NoteHandler> () ); + +extern boost::shared_ptr<OpenJPEGImage> rgb_to_xyz ( + uint8_t const * rgba, + dcp::Size size, + int stride, + ColourConversion const & conversion + ); + + extern boost::shared_ptr<OpenJPEGImage> rgb_to_xyz_avx2 ( uint8_t const * rgba, dcp::Size size, @@ -64,6 +73,7 @@ extern boost::shared_ptr<OpenJPEGImage> rgb_to_xyz_avx2 ( ColourConversion const & conversion ); + extern void combined_rgb_to_xyz (ColourConversion const & conversion, double* matrix); } diff --git a/test/rgb_xyz_test.cc b/test/rgb_xyz_test.cc index 92c48e62..d9c27ae2 100644 --- a/test/rgb_xyz_test.cc +++ b/test/rgb_xyz_test.cc @@ -46,6 +46,81 @@ using boost::shared_ptr; using boost::optional; using boost::scoped_array; + +/** Convert a test image from sRGB to XYZ and check that the transforms are right */ +BOOST_AUTO_TEST_CASE (rgb_xyz_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; + } + } + } + + shared_ptr<dcp::OpenJPEGImage> xyz = dcp::rgb_to_xyz (rgb.get(), size, size.width * 6, dcp::ColourConversion::srgb_to_xyz ()); + + 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 using the AVX2 code and check that the transforms are right */ BOOST_AUTO_TEST_CASE (rgb_xyz_test_avx2) { |
