]> git.sesse.net Git - casparcg/commitdiff
* Merged chroma key feature, but removed unsupported parameters and color names....
authorHelge Norberg <helge.norberg@svt.se>
Tue, 9 Jun 2015 09:33:50 +0000 (11:33 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Tue, 9 Jun 2015 09:33:50 +0000 (11:33 +0200)
* Implemented support for chroma in scene_producer as well.

13 files changed:
accelerator/ogl/image/blending_glsl.h
accelerator/ogl/image/image_kernel.cpp
accelerator/ogl/image/image_shader.cpp
accelerator/ogl/util/shader.cpp
accelerator/ogl/util/shader.h
common/except.h
core/frame/frame_transform.cpp
core/frame/frame_transform.h
core/producer/binding.h
core/producer/scene/scene_producer.cpp
core/producer/scene/scene_producer.h
core/producer/scene/xml_scene_producer.cpp
protocol/amcp/AMCPCommandsImpl.cpp

index 2d5635de8185959cb5cdfcc0b71fcb25762e3196..42e95f37f83a9775a76369955d587ce49dc3c459 100644 (file)
@@ -267,3 +267,54 @@ static std::string get_blend_glsl()
 
        return glsl;
 }
+
+static std::string get_chroma_glsl()
+{
+       static std::string glsl = R"shader(
+               // Chroma keying
+               // Author: Tim Eves <timseves@googlemail.com>
+               //
+               // This implements the Chroma key algorithm described in the paper:
+               //      'Software Chroma Keying in an Imersive Virtual Environment'
+               //      by F. van den Bergh & V. Lalioti
+               // but as a pixel shader algorithm.
+               //
+
+               float       chroma_blend_w = chroma_blend.y - chroma_blend.x;
+               const vec4  grey_xfer  = vec4(0.3, 0.59, 0.11, 0.0);
+
+               float fma(float a, float b, float c) { return a*b + c; }
+
+               // This allows us to implement the paper's alphaMap curve in software
+               // rather than a largeish array
+               float alpha_map(float d)
+               {
+                   return 1.0-smoothstep(chroma_blend.x, chroma_blend.y, d);
+               }
+
+               vec4 supress_spill(vec4 c, float d)
+               {
+                   float ds = smoothstep(chroma_spill, 1.0, d/chroma_blend.y);
+                   float gl = dot(grey_xfer, c);
+                   return mix(c, vec4(vec3(gl*gl), gl), ds);
+               }
+
+               // Key on green
+               vec4 ChromaOnGreen(vec4 c)
+               {
+                   float d = fma(2.0, c.g, -c.r - c.b)/2.0;
+                   c *= alpha_map(d);
+                   return supress_spill(c, d);
+               }
+
+               //Key on blue
+               vec4 ChromaOnBlue(vec4 c)
+               {
+                   float d = fma(2.0, c.b, -c.r - c.g)/2.0;
+                   c *= alpha_map(d);
+                   return supress_spill(c, d);
+               }
+       )shader";
+
+       return glsl;
+}
index dd91168646dd4078176e27a479f8fb8417d9edeb..a90dace01356dd8851fe10081fed6bd3225860b9 100644 (file)
@@ -260,7 +260,10 @@ struct image_kernel::impl
                shader_->set("has_layer_key",   static_cast<bool>(params.layer_key));
                shader_->set("pixel_format",    params.pix_desc.format);
                shader_->set("opacity",                 params.transform.is_key ? 1.0 : params.transform.opacity);      
-                               
+
+               shader_->set("chroma_mode",             static_cast<int>(params.transform.chroma.key));
+               shader_->set("chroma_blend",    params.transform.chroma.threshold, params.transform.chroma.softness);
+               shader_->set("chroma_spill",    params.transform.chroma.spill);
 
                // Setup blend_func
                
index 35d9fe41900895e4282ab091c3f723817bd520d1..f9b49f03e97141936dcc4b6e67cbf644fc3a2588 100644 (file)
@@ -119,6 +119,29 @@ std::string get_simple_blend_color_func()
                )shader";
 }
 
+std::string get_chroma_func()
+{
+       return
+
+               get_chroma_glsl()
+               
+               +
+               
+               R"shader(
+                               vec4 chroma_key(vec4 c)
+                               {
+                                       switch (chroma_mode)
+                                       {
+                                       case 0: return c;
+                                       case 1: return ChromaOnGreen(c.bgra).bgra;
+                                       case 2: return ChromaOnBlue(c.bgra).bgra;
+                                       }
+
+                                       return c;
+                               }
+               )shader";
+}
+
 std::string get_vertex()
 {
        return R"shader(
@@ -167,6 +190,9 @@ std::string get_fragment(bool blend_modes)
                        uniform float           sat;
                        uniform float           con;
 
+                       uniform int                     chroma_mode;
+                       uniform vec2            chroma_blend;
+                       uniform float           chroma_spill;
        )shader"
 
        +
@@ -175,6 +201,10 @@ std::string get_fragment(bool blend_modes)
 
        +
 
+       get_chroma_func()
+
+       +
+
        R"shader(
 
                        vec4 ycbcra_to_rgba_sd(float Y, float Cb, float Cr, float A)
@@ -263,7 +293,8 @@ std::string get_fragment(bool blend_modes)
                        void main()
                        {
                                vec4 color = get_rgba_color();
-                          if(levels)
+                               color = chroma_key(color);
+                               if(levels)
                                        color.rgb = LevelsControl(color.rgb, min_input, gamma, max_input, min_output, max_output);
                                if(csb)
                                        color.rgb = ContrastSaturationBrightness(color.rgb, brt, sat, con);
index 98e3b8a5dbdca463216b94c97dbb960f70f2f799..63aca4fc2e7918b99ac27a88e0ee6d2f58012865 100644 (file)
@@ -128,9 +128,9 @@ public:
                GL(glUniform1f(get_location(name.c_str()), value));
        }
        
-       void set(const std::string& name, float value0, float value1)
+       void set(const std::string& name, double value0, double value1)
        {
-               GL(glUniform2f(get_location(name.c_str()), value0, value1));
+               GL(glUniform2f(get_location(name.c_str()), static_cast<float>(value0), static_cast<float>(value1)));
        }
 
        void set(const std::string& name, double value)
@@ -149,7 +149,7 @@ shader::~shader(){}
 void shader::set(const std::string& name, bool value){impl_->set(name, value);}
 void shader::set(const std::string& name, int value){impl_->set(name, value);}
 void shader::set(const std::string& name, float value){impl_->set(name, value);}
-void shader::set(const std::string& name, float value0, float value1){impl_->set(name, value0, value1);}
+void shader::set(const std::string& name, double value0, double value1){impl_->set(name, value0, value1);}
 void shader::set(const std::string& name, double value){impl_->set(name, value);}
 int shader::id() const{return impl_->program_;}
 void shader::use()const{impl_->use();}
index bb8976464b37730b2a5e133d9e2e03b4140fcb9f..242947059b9fa184b977436378550b6ef9a9f35a 100644 (file)
@@ -47,7 +47,7 @@ public:
        void set(const std::string& name, bool value);
        void set(const std::string& name, int value);
        void set(const std::string& name, float value);
-       void set(const std::string& name, float value0, float value1);
+       void set(const std::string& name, double value0, double value1);
        void set(const std::string& name, double value);
 
        template<typename E>
index f28b6d27604c4d016cc423e0e0409a26c009ca13..9bdc05ccff4a2cdaec3cf6b42d94d65025eec550 100644 (file)
@@ -79,6 +79,7 @@ struct file_write_error         : virtual io_error {};
 struct invalid_argument                        : virtual caspar_exception {};
 struct null_argument                   : virtual invalid_argument {};
 struct out_of_range                            : virtual invalid_argument {};
+struct programming_error               : virtual caspar_exception {};
 struct bad_alloc                               : virtual caspar_exception {};
 
 struct invalid_operation               : virtual caspar_exception {};
index 52e33d3cf74b123e16d2626594150eb8b13cc9f1..c5e70239465470a79ad4a74230da51b683acba36 100644 (file)
@@ -91,6 +91,10 @@ image_transform& image_transform::operator*=(const image_transform &other)
        levels.min_output                = std::max(levels.min_output, other.levels.min_output);
        levels.max_output                = std::min(levels.max_output, other.levels.max_output);
        levels.gamma                    *= other.levels.gamma;
+       chroma.key                               = std::max(chroma.key, other.chroma.key);
+       chroma.threshold                += other.chroma.threshold;
+       chroma.softness                 += other.chroma.softness;
+       chroma.spill                    += other.chroma.spill;
        field_mode                               = field_mode & other.field_mode;
        is_key                                  |= other.is_key;
        is_mix                                  |= other.is_mix;
@@ -132,7 +136,7 @@ void do_tween_corners(const corners& source, const corners& dest, corners& out,
 };
 
 image_transform image_transform::tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener& tween)
-{      
+{
        image_transform result; 
 
        result.brightness                       = do_tween(time, source.brightness,                             dest.brightness,                        duration, tween);
@@ -155,6 +159,10 @@ image_transform image_transform::tween(double time, const image_transform& sourc
        result.levels.max_output        = do_tween(time, source.levels.max_output,              dest.levels.max_output,         duration, tween);
        result.levels.min_output        = do_tween(time, source.levels.min_output,              dest.levels.min_output,         duration, tween);
        result.levels.gamma                     = do_tween(time, source.levels.gamma,                   dest.levels.gamma,                      duration, tween);
+       result.chroma.threshold         = do_tween(time, source.chroma.threshold,               dest.chroma.threshold,          duration, tween);
+       result.chroma.softness          = do_tween(time, source.chroma.softness,                dest.chroma.softness,           duration, tween);
+       result.chroma.spill                     = do_tween(time, source.chroma.spill,                   dest.chroma.spill,                      duration, tween);
+       result.chroma.key                       = dest.chroma.key;
        result.field_mode                       = source.field_mode & dest.field_mode;
        result.is_key                           = source.is_key | dest.is_key;
        result.is_mix                           = source.is_mix | dest.is_mix;
@@ -288,6 +296,34 @@ bool operator!=(const frame_transform& lhs, const frame_transform& rhs)
        return !(lhs == rhs);
 }
 
+
+core::chroma::type get_chroma_mode(const std::wstring& str)
+{
+       if (boost::iequals(str, L"none"))
+               return core::chroma::type::none;
+       else if (boost::iequals(str, L"green"))
+               return core::chroma::type::green;
+       else if (boost::iequals(str, L"blue"))
+               return core::chroma::type::blue;
+       else
+               CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("chroma mode has to be one of none, green or blue"));
+}
+
+std::wstring get_chroma_mode(core::chroma::type type)
+{
+       switch (type)
+       {
+       case core::chroma::type::none:
+               return L"none";
+       case core::chroma::type::green:
+               return L"green";
+       case core::chroma::type::blue:
+               return L"blue";
+       default:
+               CASPAR_THROW_EXCEPTION(programming_error() << msg_info("Unhandled enum constant"));
+       };
+}
+
 namespace detail {
 
 boost::thread_specific_ptr<double>& get_thread_local_aspect_ratio()
index d0d2277cc1b7201a96e0522f9268c1ffc26c59c3..277c1c6fcfc6d26e35abf34491b617d0732768f3 100644 (file)
 #include <boost/property_tree/ptree.hpp>
 
 namespace caspar { namespace core {
-                       
+
+struct chroma
+{
+       enum class type
+       {
+               none,
+               green,
+               blue
+       };
+
+       type    key                     = type::none;
+       double  threshold       = 0.0;
+       double  softness        = 0.0;
+       double  spill           = 0.0;
+};
+
 struct levels final
 {
        double min_input        = 0.0;
@@ -74,6 +89,7 @@ struct image_transform final
        rectangle                               crop;
        corners                                 perspective;
        core::levels                    levels;
+       core::chroma                    chroma;
 
        core::field_mode                field_mode                      = core::field_mode::progressive;
        bool                                    is_key                          = false;
@@ -162,6 +178,9 @@ public:
        }
 };
 
+chroma::type get_chroma_mode(const std::wstring& str);
+std::wstring get_chroma_mode(chroma::type type);
+
 namespace detail {
 
 void set_current_aspect_ratio(double aspect_ratio);
index b136057663eab31c889a060b86454b00757162b2..77e4261cb080774f2ca5cb1cea3c63bb33850359 100644 (file)
@@ -49,7 +49,7 @@ struct impl_base : std::enable_shared_from_this<impl_base>
        {
        }
 
-       virtual void evaluate() = 0;
+       virtual void evaluate() const = 0;
 
        void depend_on(const std::shared_ptr<impl_base>& dependency)
        {
@@ -93,8 +93,9 @@ private:
 
        struct impl : public detail::impl_base
        {
-               T value_;
+               mutable T                       value_;
                std::function<T ()> expression_;
+               mutable bool            evaluated_              = false;
 
                impl()
                        : value_()
@@ -114,6 +115,9 @@ private:
 
                T get() const
                {
+                       if (!evaluated_)
+                               evaluate();
+
                        return value_;
                }
 
@@ -137,7 +141,7 @@ private:
                        on_change();
                }
 
-               void evaluate() override
+               void evaluate() const override
                {
                        if (expression_)
                        {
@@ -149,10 +153,12 @@ private:
                                        on_change();
                                }
                        }
+
+                       evaluated_ = true;
                }
 
                using impl_base::on_change;
-               void on_change()
+               void on_change() const
                {
                        auto copy = on_change_;
 
@@ -172,7 +178,7 @@ private:
                        unbind();
                        depend_on(other);
                        expression_ = [other]{ return other->get(); };
-                       evaluate();
+                       //evaluate();
                }
 
                void unbind()
@@ -204,7 +210,7 @@ public:
        explicit binding(const Expr& expression)
                : impl_(new impl(expression))
        {
-               impl_->evaluate();
+               //impl_->evaluate();
        }
 
        // Expr -> T ()
@@ -213,7 +219,7 @@ public:
                : impl_(new impl(expression))
        {
                depend_on(dep);
-               impl_->evaluate();
+               //impl_->evaluate();
        }
 
        // Expr -> T ()
@@ -226,7 +232,7 @@ public:
        {
                depend_on(dep1);
                depend_on(dep2);
-               impl_->evaluate();
+               //impl_->evaluate();
        }
 
        void* identity() const
index 88b7dabef3995d170fcf5fa6c3b0d750b0afb0b7..b58b6c33eba0d159167d5b9d10ce9f827f72ba19 100644 (file)
@@ -193,10 +193,14 @@ struct scene_producer::impl
 
                angle           = layer.rotation.get() * PI / 180.0;
 
-               transform.image_transform.opacity = layer.adjustments.opacity.get();
-               transform.image_transform.is_key = layer.is_key.get();
-               transform.image_transform.use_mipmap = layer.use_mipmap.get();
-               transform.image_transform.blend_mode = layer.blend_mode.get();
+               transform.image_transform.opacity                       = layer.adjustments.opacity.get();
+               transform.image_transform.is_key                        = layer.is_key.get();
+               transform.image_transform.use_mipmap            = layer.use_mipmap.get();
+               transform.image_transform.blend_mode            = layer.blend_mode.get();
+               transform.image_transform.chroma.key            = layer.chroma_key.key.get();
+               transform.image_transform.chroma.threshold      = layer.chroma_key.threshold.get();
+               transform.image_transform.chroma.softness       = layer.chroma_key.softness.get();
+               transform.image_transform.chroma.spill          = layer.chroma_key.spill.get();
 
                // Mark as sublayer, so it will be composited separately by the mixer.
                transform.image_transform.layer_depth = 1;
index 541aa37f30e925ebe7239c35cc9648f18a286a64..f08a550de000c49b2383db104e970b83b084d6fc 100644 (file)
@@ -61,6 +61,14 @@ struct adjustments
        adjustments();
 };
 
+struct chroma_key
+{
+       binding<core::chroma::type>     key;
+       binding<double>                         threshold;
+       binding<double>                         softness;
+       binding<double>                         spill;
+};
+
 struct layer
 {
        binding<std::wstring>                                           name;
@@ -75,6 +83,7 @@ struct layer
        binding<bool>                                                           is_key;
        binding<bool>                                                           use_mipmap;
        binding<core::blend_mode>                                       blend_mode;
+       scene::chroma_key                                                       chroma_key;
 
        explicit layer(const std::wstring& name, const spl::shared_ptr<frame_producer>& producer);
 };
index 1ed285e573d49b5690f44fb03a54d3f49dcf7f9c..7475fafc3adde4d95fd8358f5babcc6e324d2435 100644 (file)
@@ -134,6 +134,10 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                layer.is_key = scene->create_variable<bool>(variable_prefix + L"is_key", false, elem.second.get(L"is_key", L"false"));
                layer.use_mipmap = scene->create_variable<bool>(variable_prefix + L"use_mipmap", false, elem.second.get(L"use_mipmap", L"false"));
                layer.blend_mode = scene->create_variable<std::wstring>(variable_prefix + L"blend_mode", false, elem.second.get(L"blend_mode", L"normal")).transformed([](const std::wstring& b) { return get_blend_mode(b); });
+               layer.chroma_key.key = scene->create_variable<std::wstring>(variable_prefix + L"chroma_key.key", false, elem.second.get(L"chroma_key.key", L"none")).transformed([](const std::wstring& k) { return get_chroma_mode(k); });
+               layer.chroma_key.threshold = scene->create_variable<double>(variable_prefix + L"chroma_key.threshold", false, elem.second.get(L"chroma_key.threshold", L"0.0"));
+               layer.chroma_key.softness = scene->create_variable<double>(variable_prefix + L"chroma_key.softness", false, elem.second.get(L"chroma_key.softness", L"0.0"));
+               layer.chroma_key.spill = scene->create_variable<double>(variable_prefix + L"chroma_key.spill", false, elem.second.get(L"chroma_key.spill", L"0.0"));
 
                scene->create_variable<double>(variable_prefix + L"width", false) = layer.producer.get()->pixel_constraints().width;
                scene->create_variable<double>(variable_prefix + L"height", false) = layer.producer.get()->pixel_constraints().height;
index e079c88498b970031116aab09d1281029961d19d..06b3c70886c1115150470d97a790fc0508d7c47a 100644 (file)
@@ -640,7 +640,35 @@ bool MixerCommand::DoExecute()
                                return transform;
                        }, 0, L"linear"));
                }
-               else if(boost::iequals(parameters()[0], L"MASTERVOLUME"))
+               else if (boost::iequals(parameters()[0], L"CHROMA"))
+               {
+                       if (parameters().size() == 1)
+                       {
+                               auto chroma = get_current_transform().image_transform.chroma;
+                               SetReplyString(
+                                       L"201 MIXER OK\r\n"
+                                       + core::get_chroma_mode(chroma.key) + L" "
+                                       + boost::lexical_cast<std::wstring>(chroma.threshold) + L" "
+                                       + boost::lexical_cast<std::wstring>(chroma.softness) + L" "
+                                       + boost::lexical_cast<std::wstring>(chroma.spill) + L"\r\n");
+                               return true;
+                       }
+
+                       int duration = parameters().size() > 5 ? boost::lexical_cast<int>(parameters().at(5)) : 0;
+                       std::wstring tween = parameters().size() > 6 ? parameters().at(6) : L"linear";
+
+                       core::chroma chroma;
+                       chroma.key                      = get_chroma_mode(parameters().at(1));
+                       chroma.threshold        = boost::lexical_cast<double>(parameters().at(2));
+                       chroma.softness         = boost::lexical_cast<double>(parameters().at(3));
+                       chroma.spill            = parameters().size() > 4 ? boost::lexical_cast<double>(parameters().at(4)) : 0.0;
+                       transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
+                       {
+                               transform.image_transform.chroma = chroma;
+                               return transform;
+                       }, duration, tween));
+               }
+               else if (boost::iequals(parameters()[0], L"MASTERVOLUME"))
                {
                        if (parameters().size() == 1)
                        {