Allow LUTs to be created with a particular range.
authorCarl Hetherington <cth@carlh.net>
Thu, 5 May 2022 18:56:14 +0000 (20:56 +0200)
committerCarl Hetherington <cth@carlh.net>
Thu, 5 May 2022 19:09:40 +0000 (21:09 +0200)
12 files changed:
src/gamma_transfer_function.cc
src/gamma_transfer_function.h
src/identity_transfer_function.cc
src/identity_transfer_function.h
src/modified_gamma_transfer_function.cc
src/modified_gamma_transfer_function.h
src/rgb_xyz.cc
src/s_gamut3_transfer_function.cc
src/s_gamut3_transfer_function.h
src/transfer_function.cc
src/transfer_function.h
test/colour_conversion_test.cc

index 4ec09afb0997403f5eb8fa508c2fe391f795ef1e..ff9073a1954441691865bddc098efc82902b1d18 100644 (file)
@@ -56,13 +56,14 @@ GammaTransferFunction::GammaTransferFunction (double gamma)
 
 
 vector<double>
-GammaTransferFunction::make_lut (int bit_depth, bool inverse) const
+GammaTransferFunction::make_lut (double from, double to, int bit_depth, bool inverse) const
 {
        int const bit_length = int(std::pow(2.0f, bit_depth));
        auto lut = vector<double>(bit_length);
        double const gamma = inverse ? (1 / _gamma) : _gamma;
        for (int i = 0; i < bit_length; ++i) {
-               lut[i] = pow(double(i) / (bit_length - 1), gamma);
+               double const x = double(i) / (bit_length - 1);
+               lut[i] = pow((x * (to - from)) + from, gamma);
        }
 
        return lut;
index bfd336df1a3ba5f1ab5beb4ca4af3b17eb5acdbf..e7294000f2214060202ab3a17febd8d21df0cc4f 100644 (file)
@@ -58,7 +58,7 @@ public:
        bool about_equal (std::shared_ptr<const TransferFunction> other, double epsilon) const override;
 
 protected:
-       std::vector<double> make_lut (int bit_depth, bool inverse) const override;
+       std::vector<double> make_lut (double from, double to, int bit_depth, bool inverse) const override;
 
 private:
        double _gamma;
index c888bd3ec9dac142d786d46ede687d956675b24e..b6f03d07f69a09df33864b7c01b13d98d89e1485 100644 (file)
@@ -49,12 +49,13 @@ using namespace dcp;
 
 
 vector<double>
-IdentityTransferFunction::make_lut (int bit_depth, bool) const
+IdentityTransferFunction::make_lut (double from, double to, int bit_depth, bool) const
 {
        int const bit_length = int(std::pow(2.0f, bit_depth));
        auto lut = vector<double>(bit_length);
        for (int i = 0; i < bit_length; ++i) {
-               lut[i] = double(i) / (bit_length - 1);
+               double x = double(i) / (bit_length - 1);
+               lut[i] = (x * (to - from)) + from;
        }
 
        return lut;
index 3db71b301b61bcac9e35475f80d3c32f14d17824..f6e80abbd69af319a83740481c68da3de32e4916 100644 (file)
@@ -49,7 +49,7 @@ public:
        bool about_equal (std::shared_ptr<const TransferFunction> other, double epsilon) const override;
 
 protected:
-       std::vector<double> make_lut (int bit_depth, bool inverse) const override;
+       std::vector<double> make_lut (double from, double to, int bit_depth, bool inverse) const override;
 };
 
 
index e2cc1e8677baecae36587f8c57e6f863d2173ef1..991e642e9dab8069c61901cd01d5b045d9549d1d 100644 (file)
@@ -59,7 +59,7 @@ ModifiedGammaTransferFunction::ModifiedGammaTransferFunction (double power, doub
 
 
 vector<double>
-ModifiedGammaTransferFunction::make_lut (int bit_depth, bool inverse) const
+ModifiedGammaTransferFunction::make_lut (double from, double to, int bit_depth, bool inverse) const
 {
        int const bit_length = int(std::pow(2.0f, bit_depth));
        auto lut = vector<double>(bit_length);
@@ -67,19 +67,21 @@ ModifiedGammaTransferFunction::make_lut (int bit_depth, bool inverse) const
                double const threshold = _threshold / _B;
                for (int i = 0; i < bit_length; ++i) {
                        double const p = static_cast<double>(i) / (bit_length - 1);
-                       if (p > threshold) {
-                               lut[i] = (1 + _A) * pow (p, 1 / _power) - _A;
+                       double const q = (p * (to - from)) + from;
+                       if (q > threshold) {
+                               lut[i] = (1 + _A) * pow (q, 1 / _power) - _A;
                        } else {
-                               lut[i] = p * _B;
+                               lut[i] = q * _B;
                        }
                }
        } else {
                for (int i = 0; i < bit_length; ++i) {
                        double const p = static_cast<double>(i) / (bit_length - 1);
-                       if (p > _threshold) {
-                               lut[i] = pow ((p + _A) / (1 + _A), _power);
+                       double const q = (p * (to - from)) + from;
+                       if (q > _threshold) {
+                               lut[i] = pow ((q + _A) / (1 + _A), _power);
                        } else {
-                               lut[i] = p / _B;
+                               lut[i] = q / _B;
                        }
                }
        }
index d4e9cf97fa642de6160751cb9633394865a1c83b..0427dd6170e4de18410c4c265762c21e463cc3c3 100644 (file)
@@ -77,7 +77,7 @@ public:
        bool about_equal (std::shared_ptr<const TransferFunction>, double epsilon) const override;
 
 protected:
-       std::vector<double> make_lut (int bit_depth, bool inverse) const override;
+       std::vector<double> make_lut (double from, double to, int bit_depth, bool inverse) const override;
 
 private:
        double _power;
index b8acc9b0db0563514e55c83eddc970751b1abd0d..de6a40db2cd5fab8a23e1c02efcbe49bb04bdfb8 100644 (file)
@@ -80,8 +80,8 @@ dcp::xyz_to_rgba (
        int* xyz_y = xyz_image->data (1);
        int* xyz_z = xyz_image->data (2);
 
-       auto lut_in = conversion.out()->lut (12, false);
-       auto lut_out = conversion.in()->lut (16, true);
+       auto lut_in = conversion.out()->lut(0, 1, 12, false);
+       auto lut_out = conversion.in()->lut(0, 1, 16, true);
        boost::numeric::ublas::matrix<double> const matrix = conversion.xyz_to_rgb ();
 
        double fast_matrix[9] = {
@@ -157,8 +157,8 @@ dcp::xyz_to_rgb (
        int* xyz_y = xyz_image->data (1);
        int* xyz_z = xyz_image->data (2);
 
-       auto lut_in = conversion.out()->lut (12, false);
-       auto lut_out = conversion.in()->lut (16, true);
+       auto lut_in = conversion.out()->lut(0, 1, 12, false);
+       auto lut_out = conversion.in()->lut(0, 1, 16, true);
        auto const matrix = conversion.xyz_to_rgb ();
 
        double fast_matrix[9] = {
@@ -276,8 +276,8 @@ dcp::rgb_to_xyz (
                double x, y, z;
        } d;
 
-       auto lut_in = conversion.in()->lut (12, false);
-       auto lut_out = conversion.out()->lut (16, true);
+       auto lut_in = conversion.in()->lut(0, 1, 12, false);
+       auto lut_out = conversion.out()->lut(0, 1, 16, true);
 
        /* This is is the product of the RGB to XYZ matrix, the Bradford transform and the DCI companding */
        double fast_matrix[9];
index b2e82b5144226bba59b553922ff34aaf8455fa3b..512a29510b7b0b48f5fbce0844fe3152dfe3a397 100644 (file)
@@ -49,26 +49,28 @@ using namespace dcp;
 
 
 vector<double>
-SGamut3TransferFunction::make_lut (int bit_depth, bool inverse) const
+SGamut3TransferFunction::make_lut (double from, double to, int bit_depth, bool inverse) const
 {
        int const bit_length = int(std::pow(2.0f, bit_depth));
        auto lut = vector<double>(bit_length);
        if (inverse) {
                for (int i = 0; i < bit_length; ++i) {
                        auto const p = static_cast<double>(i) / (bit_length - 1);
-                       if (p >= (0.01125 / 1023)) {
-                               lut[i] = (420 + log10((p + 0.01) / (0.18 + 0.01)) * 261.5) / 1023;
+                       auto const q = (p * (to - from)) + from;
+                       if (q >= (0.01125 / 1023)) {
+                               lut[i] = (420 + log10((q + 0.01) / (0.18 + 0.01)) * 261.5) / 1023;
                        } else {
-                               lut[i] = (p * (171.2102946929 - 95) / 0.01125000 + 95) / 1023;
+                               lut[i] = (q * (171.2102946929 - 95) / 0.01125000 + 95) / 1023;
                        }
                }
        } else {
                for (int i = 0; i < bit_length; ++i) {
                        auto const p = static_cast<double>(i) / (bit_length - 1);
-                       if (p >= (171.2102946929 / 1023)) {
+                       auto const q = (p * (to - from)) + from;
+                       if (q >= (171.2102946929 / 1023)) {
                                lut[i] = pow(10, ((p * 1023 - 420) / 261.5)) * (0.18 + 0.01) - 0.01;
                        } else {
-                               lut[i] = (p * 1023 - 95) * 0.01125000 / (171.2102946929 - 95);
+                               lut[i] = (q * 1023 - 95) * 0.01125000 / (171.2102946929 - 95);
                        }
                }
        }
index 790cd6b15d5fa0e59668ffa7a7f79e2917bc6b29..50b4875ab93bb9ca0553877809b8215bc83df3d3 100644 (file)
@@ -49,7 +49,7 @@ public:
        bool about_equal (std::shared_ptr<const TransferFunction> other, double epsilon) const override;
 
 protected:
-       std::vector<double> make_lut (int bit_depth, bool inverse) const override;
+       std::vector<double> make_lut (double from, double to, int bit_depth, bool inverse) const override;
 };
 
 
index dd3512ec3d498fdeb58a312ef8fc883f15100d44..c1343ff2c3eb6b003c58e781f534ed44e6855034 100644 (file)
@@ -51,15 +51,35 @@ using namespace dcp;
 
 
 vector<double> const&
-TransferFunction::lut (int bit_depth, bool inverse) const
+TransferFunction::lut (double from, double to, int bit_depth, bool inverse) const
 {
        boost::mutex::scoped_lock lm (_mutex);
 
-       auto i = _luts.find (make_pair (bit_depth, inverse));
-       if (i != _luts.end ()) {
+       auto const descriptor = LUTDescriptor{from, to, bit_depth, inverse};
+
+       auto i = _luts.find(descriptor);
+       if (i != _luts.end()) {
                return i->second;
        }
 
-       _luts[make_pair(bit_depth, inverse)] = make_lut (bit_depth, inverse);
-       return _luts[make_pair(bit_depth, inverse)];
+       _luts[descriptor] = make_lut(from, to, bit_depth, inverse);
+       return _luts[descriptor];
+}
+
+
+bool
+TransferFunction::LUTDescriptor::operator==(TransferFunction::LUTDescriptor const& other) const
+{
+       return from == other.from && to == other.to && bit_depth == other.bit_depth && inverse == other.inverse;
+}
+
+
+std::size_t
+TransferFunction::LUTDescriptorHasher::operator()(TransferFunction::LUTDescriptor const& desc) const
+{
+       return std::hash<double>()(desc.from) ^
+               std::hash<double>()(desc.to) ^
+               std::hash<int>()(desc.bit_depth) ^
+               std::hash<bool>()(desc.inverse);
 }
+
index 046787335dd5ad9c49aaa98a61db1f4ea20da849..a53a476e48919c83e3000d1de23bac96413b601d 100644 (file)
@@ -42,7 +42,7 @@
 
 
 #include <boost/thread/mutex.hpp>
-#include <map>
+#include <unordered_map>
 #include <memory>
 #include <vector>
 
@@ -60,17 +60,30 @@ public:
 
        virtual ~TransferFunction () {}
 
-       /** @return A look-up table (of size 2^bit_depth) whose values range from 0 to 1 */
-       std::vector<double> const& lut (int bit_depth, bool inverse) const;
+       /** @return A look-up table (of size 2^bit_depth) */
+       std::vector<double> const& lut (double from, double to, int bit_depth, bool inverse) const;
 
        virtual bool about_equal (std::shared_ptr<const TransferFunction> other, double epsilon) const = 0;
 
 protected:
        /** Make a LUT and return an array allocated by new */
-       virtual std::vector<double> make_lut (int bit_depth, bool inverse) const = 0;
+       virtual std::vector<double> make_lut (double from, double to, int bit_depth, bool inverse) const = 0;
 
 private:
-       mutable std::map<std::pair<int, bool>, std::vector<double>> _luts;
+       struct LUTDescriptor {
+               double from;
+               double to;
+               int bit_depth;
+               bool inverse;
+
+               bool operator==(LUTDescriptor const& other) const;
+       };
+
+       struct LUTDescriptorHasher {
+               std::size_t operator()(LUTDescriptor const& desc) const;
+       };
+
+       mutable std::unordered_map<LUTDescriptor, std::vector<double>, LUTDescriptorHasher> _luts;
        /** mutex to protect _luts */
        mutable boost::mutex _mutex;
 };
index c1e7fc1a2722459fe9c2c610ace174947c9fd65d..73de297a0a9055e82bba5a91cf5b64f8a7b950f0 100644 (file)
@@ -44,7 +44,7 @@ using namespace dcp;
 static void
 check_gamma (shared_ptr<const TransferFunction> tf, int bit_depth, bool inverse, float gamma)
 {
-       auto lut = tf->lut (bit_depth, inverse);
+       auto lut = tf->lut(0, 1, bit_depth, inverse);
        int const count = rint (pow (2.0, bit_depth));
 
        for (int i = 0; i < count; ++i) {
@@ -55,7 +55,7 @@ check_gamma (shared_ptr<const TransferFunction> tf, int bit_depth, bool inverse,
 static void
 check_modified_gamma (shared_ptr<const TransferFunction> tf, int bit_depth, bool inverse, double power, double threshold, double A, double B)
 {
-       auto lut = tf->lut (bit_depth, inverse);
+       auto lut = tf->lut(0, 1, bit_depth, inverse);
        int const count = rint (pow (2.0, bit_depth));
 
        for (int i = 0; i < count; ++i) {