diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-03-13 16:46:50 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-03-13 16:46:50 +0000 |
| commit | 45625f3116a09d3c8415a54bf8d19fdbb3a3aa9b (patch) | |
| tree | 3619eb2ba2d9e754f9122b367f4eeffe56239bf9 | |
| parent | 6e5411a943ef9b3f23cfb8dd9dcc1a756b55bfbe (diff) | |
Compute LUTs at run-time.dynamic-lut
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | lut.py | 127 | ||||
| -rw-r--r-- | src/gamma_lut.cc | 16 | ||||
| -rw-r--r-- | src/gamma_lut.h | 13 | ||||
| -rw-r--r-- | src/lut.h | 63 | ||||
| -rw-r--r-- | src/lut_cache.h | 28 | ||||
| -rw-r--r-- | src/picture_frame.cc | 12 | ||||
| -rw-r--r-- | src/picture_frame.h | 4 | ||||
| -rw-r--r-- | src/util.cc | 40 | ||||
| -rw-r--r-- | src/util.h | 4 | ||||
| -rw-r--r-- | src/wscript | 3 | ||||
| -rw-r--r-- | src/xyz_srgb_lut.cc | 24 | ||||
| -rw-r--r-- | src/xyz_srgb_lut.h | 13 | ||||
| -rw-r--r-- | test/tests.cc | 1 | ||||
| -rw-r--r-- | wscript | 3 |
15 files changed, 199 insertions, 154 deletions
@@ -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 index 9dd4ff20..00000000 --- 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 index 00000000..acc80af0 --- /dev/null +++ b/src/gamma_lut.cc @@ -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 index 00000000..e41cd21f --- /dev/null +++ b/src/gamma_lut.h @@ -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 index 00000000..8363e6a4 --- /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 index 00000000..b60ee109 --- /dev/null +++ b/src/lut_cache.h @@ -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 diff --git a/src/picture_frame.cc b/src/picture_frame.cc index 907f70ab..5d5f12c2 100644 --- a/src/picture_frame.cc +++ b/src/picture_frame.cc @@ -25,6 +25,10 @@ #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; } diff --git a/src/picture_frame.h b/src/picture_frame.h index 20ce069e..42c5d629 100644 --- a/src/picture_frame.h +++ b/src/picture_frame.h @@ -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; diff --git a/src/util.cc b/src/util.cc index 1cbec719..bd5f32ed 100644 --- a/src/util.cc +++ b/src/util.cc @@ -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; } @@ -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>); } diff --git a/src/wscript b/src/wscript index d243ae46..efba2502 100644 --- a/src/wscript +++ b/src/wscript @@ -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 index 00000000..3d207195 --- /dev/null +++ b/src/xyz_srgb_lut.cc @@ -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 index 00000000..63f89178 --- /dev/null +++ b/src/xyz_srgb_lut.h @@ -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; +}; + +} diff --git a/test/tests.cc b/test/tests.cc index 62cab54c..30c9f406 100644 --- a/test/tests.cc +++ b/test/tests.cc @@ -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 @@ -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') |
