Compute LUTs at run-time. dynamic-lut
authorCarl Hetherington <cth@carlh.net>
Wed, 13 Mar 2013 16:46:50 +0000 (16:46 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 13 Mar 2013 16:46:50 +0000 (16:46 +0000)
15 files changed:
.gitignore
lut.py [deleted file]
src/gamma_lut.cc [new file with mode: 0644]
src/gamma_lut.h [new file with mode: 0644]
src/lut.h [new file with mode: 0644]
src/lut_cache.h [new file with mode: 0644]
src/picture_frame.cc
src/picture_frame.h
src/util.cc
src/util.h
src/wscript
src/xyz_srgb_lut.cc [new file with mode: 0644]
src/xyz_srgb_lut.h [new file with mode: 0644]
test/tests.cc
wscript

index d845eee595ae81c17cafce139fadaf0d1d7a849c..2170048b4af63cdfc2fc3f50e3636643a0fe293b 100644 (file)
@@ -6,8 +6,6 @@ build
 doc/html
 doc/latex
 src/version.cc
-src/lut.cc
-src/lut.h
 *.pyc
 __pycache__
 GPATH
diff --git a/lut.py b/lut.py
deleted file mode 100644 (file)
index 9dd4ff2..0000000
--- a/lut.py
+++ /dev/null
@@ -1,127 +0,0 @@
-from __future__ import print_function
-import math
-
-BIT_DEPTH = 12
-DCI_GAMMA = 2.6
-SRGB_GAMMA = 2.4;
-BIT_LENGTH = int(math.pow(2, BIT_DEPTH))
-COLOR_DEPTH = BIT_LENGTH - 1
-
-def boilerplate(f):
-    print("""/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-
-/* This file is auto-generated by the build scripts; edits will be lost
-   on ./waf configure.
-*/
-""", file=f)
-
-def make_luts():
-    cc = open('src/lut.cc', 'w')
-
-    boilerplate(cc)
-
-    print("#include \"lut.h\"", file=cc)
-
-    print("""
-/* sRGB color matrix for XYZ -> RGB */
-float color_matrix[3][3] = {
-  { 3.240454836, -1.537138850, -0.498531547},
-  {-0.969266390,  1.876010929,  0.041556082},
-  { 0.055643420, -0.204025854,  1.057225162}
-};\n\n
-""", file=cc)
-
-    print("""
-float lut_in[COLOR_DEPTH + 1] = {\n
-\t/* Bit depth:       %d
-\t * Reference white: DCI
-\t * Gamma:           %f
-\t */
-""" % (BIT_DEPTH, DCI_GAMMA), file=cc)
-
-    c = 0
-    for i in range(0, BIT_LENGTH):
-        v = math.pow (i / (BIT_LENGTH - 1.0), DCI_GAMMA);
-
-        if (c == 0):
-            print("    ", end='', file=cc)
-
-        if i < BIT_LENGTH - 1:
-            print("%06f, " % v, end="", file=cc)
-            if c == 12:
-                c = 0;
-                print("", file=cc)
-            else:
-                c += 1
-        else:
-            print("%06f" % v, file=cc)
-
-    print("};", file=cc)
-
-    print("""
-int lut_out[COLOR_DEPTH + 1] = {
-\t/* Bit depth:       %d
-\t * Reference white: sRGB
-\t * Gamma:           %f
-\t */
-""", file=cc)
-
-    c = 0
-    for i in range (0, BIT_LENGTH):
-        v = i / (BIT_LENGTH - 1.0)
-
-        if (v < (0.04045 / 12.92)):
-            v *= 12.92
-        else:
-            v = (1.055 * pow (v, (1 / SRGB_GAMMA))) - 0.055
-
-        v *= 255
-
-        if c == 0:
-            print("    ", end="", file=cc)
-
-        if i < BIT_LENGTH - 1:
-            print("%d, " % v, end="", file=cc)
-            if c == 12:
-                c = 0;
-                print("", file=cc)
-            else:
-                c += 1
-        else:
-            print("%d" % v, file=cc)
-
-    print("};", file=cc)
-            
-    h = open('src/lut.h', 'w')
-
-    boilerplate(h)
-
-    print("""
-#define COLOR_DEPTH     (%d)
-#define DCI_COEFFICIENT (48.0/52.37)
-
-extern float color_matrix[3][3];
-extern int lut_out[COLOR_DEPTH + 1];
-extern float lut_in[COLOR_DEPTH + 1];
-""" % COLOR_DEPTH, file=h)
-
-if __name__ == "__main__":
-    make_luts()
diff --git a/src/gamma_lut.cc b/src/gamma_lut.cc
new file mode 100644 (file)
index 0000000..acc80af
--- /dev/null
@@ -0,0 +1,16 @@
+#include <cmath>
+#include "gamma_lut.h"
+#include "lut_cache.h"
+
+using namespace libdcp;
+
+LUTCache<GammaLUT> GammaLUT::cache;
+
+GammaLUT::GammaLUT(int bits, float gamma)
+       : LUT<float> (bits, gamma)
+{
+       int const bit_length = pow(2, bits);
+       for (int i = 0; i < bit_length; ++i) {
+               _lut[i] = pow(float(i) / (bit_length - 1), gamma);
+       }
+}
diff --git a/src/gamma_lut.h b/src/gamma_lut.h
new file mode 100644 (file)
index 0000000..e41cd21
--- /dev/null
@@ -0,0 +1,13 @@
+#include "lut.h"
+#include "lut_cache.h"
+
+namespace libdcp {
+
+class GammaLUT : public LUT<float>
+{
+public:
+       GammaLUT (int bit_length, float gamma);
+       static LUTCache<GammaLUT> cache;
+};
+
+}
diff --git a/src/lut.h b/src/lut.h
new file mode 100644 (file)
index 0000000..8363e6a
--- /dev/null
+++ b/src/lut.h
@@ -0,0 +1,63 @@
+/*
+    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_LUT_H
+#define LIBDCP_LUT_H
+
+#include <cmath>
+
+namespace libdcp {
+
+template<typename T>
+class LUT
+{
+public:
+       LUT(int bit_depth, float gamma)
+               : _lut(0)
+               , _bit_depth (bit_depth)
+               , _gamma (gamma)
+       {
+               _lut = new T[int(std::pow(2, _bit_depth))];
+       }
+
+       virtual ~LUT() {
+               delete[] _lut;
+       }
+       
+       T const * lut() const {
+               return _lut;
+       }
+
+       int bit_depth () const {
+               return _bit_depth;
+       }
+
+       float gamma () const {
+               return _gamma;
+       }
+
+protected:
+       T* _lut;
+       int _bit_depth;
+       float _gamma;
+};
+
+}
+
+#endif
diff --git a/src/lut_cache.h b/src/lut_cache.h
new file mode 100644 (file)
index 0000000..b60ee10
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef LIBDCP_LUT_CACHE_H
+#define LIBDCP_LUT_CACHE_H
+
+#include <list>
+#include <boost/shared_ptr.hpp>
+
+template<class T>
+class LUTCache
+{
+public:
+       boost::shared_ptr<T> get (int bit_depth, float gamma)
+       {
+               for (typename std::list<boost::shared_ptr<T> >::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+                       if ((*i)->bit_depth() == bit_depth && (*i)->gamma() == gamma) {
+                               return *i;
+                       }
+               }
+
+               boost::shared_ptr<T> lut (new T (bit_depth, gamma));
+               _cache.push_back (lut);
+               return lut;
+       }
+
+private:
+       std::list<boost::shared_ptr<T> > _cache;
+};
+
+#endif
index 907f70abbff051442770d8ea5ba015d249419218..5d5f12c2df46a43465c2f86490654475f54f5377 100644 (file)
 #include "argb_frame.h"
 #include "lut.h"
 #include "util.h"
+#include "gamma_lut.h"
+#include "xyz_srgb_lut.h"
+
+#define DCI_GAMMA 2.6
 
 using std::string;
 using boost::shared_ptr;
@@ -76,11 +80,11 @@ MonoPictureFrame::j2k_size () const
  *
  */
 shared_ptr<ARGBFrame>
-MonoPictureFrame::argb_frame (int reduce) const
+MonoPictureFrame::argb_frame (int reduce, float srgb_gamma) const
 {
        opj_image_t* xyz_frame = decompress_j2k (const_cast<uint8_t*> (_buffer->RoData()), _buffer->Size(), reduce);
        assert (xyz_frame->numcomps == 3);
-       shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame);
+       shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA), XYZsRGBLUT::cache.get (12, srgb_gamma));
        opj_image_destroy (xyz_frame);
        return f;
 }
@@ -122,7 +126,7 @@ StereoPictureFrame::~StereoPictureFrame ()
  *
  */
 shared_ptr<ARGBFrame>
-StereoPictureFrame::argb_frame (Eye eye, int reduce) const
+StereoPictureFrame::argb_frame (Eye eye, int reduce, float srgb_gamma) const
 {
        opj_image_t* xyz_frame = 0;
        switch (eye) {
@@ -135,7 +139,7 @@ StereoPictureFrame::argb_frame (Eye eye, int reduce) const
        }
        
        assert (xyz_frame->numcomps == 3);
-       shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame);
+       shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA), XYZsRGBLUT::cache.get (12, srgb_gamma));
        opj_image_destroy (xyz_frame);
        return f;
 }
index 20ce069e1c14195c136c1b1848e610d5db720b63..42c5d629c688820896ff695579307d86b0896f38 100644 (file)
@@ -40,7 +40,7 @@ public:
        MonoPictureFrame (std::string mxf_path, int n);
        ~MonoPictureFrame ();
 
-       boost::shared_ptr<ARGBFrame> argb_frame (int reduce = 0) const;
+       boost::shared_ptr<ARGBFrame> argb_frame (int reduce = 0, float srgb_gamma = 2.4) const;
        uint8_t const * j2k_data () const;
        int j2k_size () const;
 
@@ -55,7 +55,7 @@ public:
        StereoPictureFrame (std::string mxf_path, int n);
        ~StereoPictureFrame ();
 
-       boost::shared_ptr<ARGBFrame> argb_frame (Eye eye, int reduce = 0) const;
+       boost::shared_ptr<ARGBFrame> argb_frame (Eye eye, int reduce = 0, float srgb_gamma = 2.4) const;
        uint8_t const * left_j2k_data () const;
        int left_j2k_size () const;
        uint8_t const * right_j2k_data () const;
index 1cbec71923abd56fac975961df55410f3283c857..bd5f32ed5d8ae7770276a06afb80775cd3f6f27b 100644 (file)
@@ -34,7 +34,8 @@
 #include "exceptions.h"
 #include "types.h"
 #include "argb_frame.h"
-#include "lut.h"
+#include "gamma_lut.h"
+#include "xyz_srgb_lut.h"
 
 using std::string;
 using std::stringstream;
@@ -202,8 +203,19 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
  *  @return RGB image.
  */
 shared_ptr<ARGBFrame>
-libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
+libdcp::xyz_to_rgb (opj_image_t* xyz_frame, shared_ptr<const GammaLUT> lut_in, shared_ptr<const XYZsRGBLUT> lut_out)
 {
+       float const dci_coefficient = 48.0 / 52.37;
+
+        /* sRGB color matrix for XYZ -> RGB */
+       float const colour_matrix[3][3] = {
+               { 3.240454836, -1.537138850, -0.498531547},
+               {-0.969266390,  1.876010929,  0.041556082},
+               { 0.055643420, -0.204025854,  1.057225162}
+       };
+
+       int const max_colour = pow (2, lut_out->bit_depth()) - 1;
+
        struct {
                double x, y, z;
        } s;
@@ -227,19 +239,19 @@ libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
                        assert (*xyz_x >= 0 && *xyz_y >= 0 && *xyz_z >= 0 && *xyz_x < 4096 && *xyz_x < 4096 && *xyz_z < 4096);
                        
                        /* In gamma LUT */
-                       s.x = lut_in[*xyz_x++];
-                       s.y = lut_in[*xyz_y++];
-                       s.z = lut_in[*xyz_z++];
+                       s.x = lut_in->lut()[*xyz_x++];
+                       s.y = lut_in->lut()[*xyz_y++];
+                       s.z = lut_in->lut()[*xyz_z++];
                        
                        /* DCI companding */
-                       s.x /= DCI_COEFFICIENT;
-                       s.y /= DCI_COEFFICIENT;
-                       s.z /= DCI_COEFFICIENT;
+                       s.x /= dci_coefficient;
+                       s.y /= dci_coefficient;
+                       s.z /= dci_coefficient;
                        
                        /* XYZ to RGB */
-                       d.r = ((s.x * color_matrix[0][0]) + (s.y * color_matrix[0][1]) + (s.z * color_matrix[0][2]));
-                       d.g = ((s.x * color_matrix[1][0]) + (s.y * color_matrix[1][1]) + (s.z * color_matrix[1][2]));
-                       d.b = ((s.x * color_matrix[2][0]) + (s.y * color_matrix[2][1]) + (s.z * color_matrix[2][2]));
+                       d.r = ((s.x * colour_matrix[0][0]) + (s.y * colour_matrix[0][1]) + (s.z * colour_matrix[0][2]));
+                       d.g = ((s.x * colour_matrix[1][0]) + (s.y * colour_matrix[1][1]) + (s.z * colour_matrix[1][2]));
+                       d.b = ((s.x * colour_matrix[2][0]) + (s.y * colour_matrix[2][1]) + (s.z * colour_matrix[2][2]));
                        
                        d.r = min (d.r, 1.0);
                        d.r = max (d.r, 0.0);
@@ -251,9 +263,9 @@ libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
                        d.b = max (d.b, 0.0);
                        
                        /* Out gamma LUT */
-                       *argb_line++ = lut_out[(int) (d.b * COLOR_DEPTH)];
-                       *argb_line++ = lut_out[(int) (d.g * COLOR_DEPTH)];
-                       *argb_line++ = lut_out[(int) (d.r * COLOR_DEPTH)];
+                       *argb_line++ = lut_out->lut()[(int) (d.b * max_colour)];
+                       *argb_line++ = lut_out->lut()[(int) (d.g * max_colour)];
+                       *argb_line++ = lut_out->lut()[(int) (d.r * max_colour)];
                        *argb_line++ = 0xff;
                }
                
index f568597023f238c34444a92b2c4c526103b9791f..2036a7ce4a75376936f480eb6924910c357e8a9b 100644 (file)
@@ -33,6 +33,8 @@
 namespace libdcp {
 
 class ARGBFrame;
+class GammaLUT;
+class XYZsRGBLUT;
 
 struct Size {
        Size ()
@@ -58,7 +60,7 @@ extern std::string content_kind_to_string (ContentKind kind);
 extern ContentKind content_kind_from_string (std::string kind);
 extern bool empty_or_white_space (std::string s);
 extern opj_image_t* decompress_j2k (uint8_t* data, int64_t size, int reduce);
-extern boost::shared_ptr<ARGBFrame> xyz_to_rgb (opj_image_t* xyz_frame);
+extern boost::shared_ptr<ARGBFrame> xyz_to_rgb (opj_image_t* xyz_frame, boost::shared_ptr<const GammaLUT>, boost::shared_ptr<const XYZsRGBLUT>);
 
 }
 
index d243ae467642bda867d089547de41c72ae72b30b..efba25025bb2e6021e22a7551752bf614e72a9ce 100644 (file)
@@ -15,7 +15,7 @@ def build(bld):
                  cpl_file.cc
                  dcp.cc        
                  dcp_time.cc
-                 lut.cc
+                 gamma_lut.cc
                  metadata.cc
                  mxf_asset.cc
                  picture_asset.cc
@@ -31,6 +31,7 @@ def build(bld):
                  util.cc
                  version.cc
                  xml.cc
+                 xyz_srgb_lut.cc
                  """
 
     headers = """
diff --git a/src/xyz_srgb_lut.cc b/src/xyz_srgb_lut.cc
new file mode 100644 (file)
index 0000000..3d20719
--- /dev/null
@@ -0,0 +1,24 @@
+#include <iostream>
+#include <cmath>
+#include "xyz_srgb_lut.h"
+
+using namespace libdcp;
+
+LUTCache<XYZsRGBLUT> XYZsRGBLUT::cache;
+
+XYZsRGBLUT::XYZsRGBLUT(int bits, float gamma)
+       : LUT<int> (bits, gamma)
+{
+       int const bit_length = pow(2, bits);
+
+       for (int i = 0; i < bit_length; ++i) {
+               float v = float(i) / (bit_length - 1);
+               if (v < (0.04045 / 12.92)) {
+                       v *= 12.92;
+               } else {
+                       v = (1.055 * pow (v, (1 / gamma))) - 0.055;
+               }
+
+               _lut[i] = int(v * 255);
+       }
+}
diff --git a/src/xyz_srgb_lut.h b/src/xyz_srgb_lut.h
new file mode 100644 (file)
index 0000000..63f8917
--- /dev/null
@@ -0,0 +1,13 @@
+#include "lut.h"
+#include "lut_cache.h"
+
+namespace libdcp {
+
+class XYZsRGBLUT : public LUT<int>
+{
+public:
+       XYZsRGBLUT(int colour_depth, float gamma);
+       static LUTCache<XYZsRGBLUT> cache;
+};
+
+}
index 62cab54ce9bea4612e004853a3fe3080077347e5..30c9f40613666fd84258cd3380f057eb1fb366f9 100644 (file)
@@ -29,6 +29,7 @@
 #include "picture_asset.h"
 #include "sound_asset.h"
 #include "reel.h"
+#include "gamma_lut.h"
 
 #define BOOST_TEST_DYN_LINK
 #define BOOST_TEST_MODULE libdcp_test
diff --git a/wscript b/wscript
index 27483bace4729d19788f5e0f880eedf8b2a2fe8d..8829ccd870d9106a235ff9cb4e7357da5abed040 100644 (file)
--- a/wscript
+++ b/wscript
@@ -1,6 +1,5 @@
 import subprocess
 import os
-import lut
 
 APPNAME = 'libdcp'
 VERSION = '0.40pre'
@@ -83,8 +82,6 @@ def configure(conf):
                    msg = 'Checking for boost signals2 library',
                    uselib_store = 'BOOST_SIGNALS2')
 
-    lut.make_luts()
-
     conf.recurse('test')
     conf.recurse('asdcplib')