From 1604eaa23902923f5455427a2c9ec701b12d8c27 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Mon, 6 Jun 2022 23:33:36 +0200 Subject: [PATCH] Original implementation of Alpha Image filters Chromakey (to finish), lumakey and alpha fill. --- CMakeLists.txt | 2 + CloneSource.cpp | 7 ++- FrameBufferFilter.cpp | 2 +- FrameBufferFilter.h | 1 + ImGuiVisitor.cpp | 69 +++++++++++++++++++++++++++++ ImGuiVisitor.h | 1 + ImageFilter.cpp | 43 ++++++++++++++++++ ImageFilter.h | 30 +++++++++++++ SessionCreator.cpp | 24 ++++++++++ SessionCreator.h | 1 + SessionVisitor.cpp | 16 +++++++ SessionVisitor.h | 1 + Visitor.h | 2 + rsc/shaders/filters/chromakey.glsl | 5 +-- rsc/shaders/filters/coloralpha.glsl | 10 +++++ rsc/shaders/filters/lumakey.glsl | 40 +++++++++++++++++ 16 files changed, 246 insertions(+), 8 deletions(-) create mode 100644 rsc/shaders/filters/coloralpha.glsl create mode 100644 rsc/shaders/filters/lumakey.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index 0477852..8f5587a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -579,6 +579,8 @@ set(VMIX_RSC_FILES ./rsc/shaders/filters/contour_2.glsl ./rsc/shaders/filters/sharpenedge.glsl ./rsc/shaders/filters/chromakey.glsl + ./rsc/shaders/filters/lumakey.glsl + ./rsc/shaders/filters/coloralpha.glsl ./rsc/shaders/filters/bilinear.glsl ./rsc/shaders/filters/edge.glsl ./rsc/shaders/filters/sobel.glsl diff --git a/CloneSource.cpp b/CloneSource.cpp index 24232e1..1155a56 100644 --- a/CloneSource.cpp +++ b/CloneSource.cpp @@ -166,6 +166,9 @@ void CloneSource::setFilter(FrameBufferFilter::Type T) case FrameBufferFilter::FILTER_EDGE: filter_ = new EdgeFilter; break; + case FrameBufferFilter::FILTER_ALPHA: + filter_ = new AlphaFilter; + break; case FrameBufferFilter::FILTER_IMAGE: filter_ = new ImageFilter; break; @@ -174,10 +177,6 @@ void CloneSource::setFilter(FrameBufferFilter::Type T) filter_ = new PassthroughFilter; break; } - - - // TODO : resampling of renderbuffer ? - } void CloneSource::play (bool on) diff --git a/FrameBufferFilter.cpp b/FrameBufferFilter.cpp index 3000086..4ee7b0f 100644 --- a/FrameBufferFilter.cpp +++ b/FrameBufferFilter.cpp @@ -6,7 +6,7 @@ #include "FrameBufferFilter.h" const char* FrameBufferFilter::type_label[FrameBufferFilter::FILTER_INVALID] = { - "None", "Delay", "Resample", "Blur", "Sharpen", "Edge", "Shader code" + "None", "Delay", "Resample", "Blur", "Sharpen", "Edge", "Transparency", "Shader code" }; FrameBufferFilter::FrameBufferFilter() : enabled_(true), input_(nullptr) diff --git a/FrameBufferFilter.h b/FrameBufferFilter.h index c7db4d1..faad623 100644 --- a/FrameBufferFilter.h +++ b/FrameBufferFilter.h @@ -24,6 +24,7 @@ public: FILTER_BLUR, FILTER_SHARPEN, FILTER_EDGE, + FILTER_ALPHA, FILTER_IMAGE, FILTER_INVALID } Type; diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index 1f39f22..6bfa73a 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -928,6 +928,75 @@ void ImGuiVisitor::visit (EdgeFilter& f) } } +void ImGuiVisitor::visit (AlphaFilter& f) +{ + std::ostringstream oss; + oss << "Alpha "; + + // Method selection + if (ImGuiToolkit::IconButton(13, 4)) { + f.setOperation( 0 ); + oss << AlphaFilter::operation_label[0]; + Action::manager().store(oss.str()); + } + ImGui::SameLine(0, IMGUI_SAME_LINE); + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + int m = (int) f.operation(); + if (ImGui::Combo("Operation", &m, AlphaFilter::operation_label, IM_ARRAYSIZE(AlphaFilter::operation_label) )) { + f.setOperation( m ); + oss << AlphaFilter::operation_label[m]; + Action::manager().store(oss.str()); + } + + + // List of parameters + std::map filter_parameters = f.program().parameters(); + + if ( m == AlphaFilter::ALPHA_CHROMAKEY || m == AlphaFilter::ALPHA_LUMAKEY) + { + float v = filter_parameters["Tolerance"]; + if (ImGuiToolkit::IconButton(13, 14)) { + v = 0.f; + f.setProgramParameter("Tolerance", v); + } + ImGui::SameLine(0, IMGUI_SAME_LINE); + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + if (ImGui::SliderFloat( "Tolerance", &v, 0.f, 1.f, "%.2f")) { + f.setProgramParameter("Tolerance", v); + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + oss << AlphaFilter::operation_label[ f.operation() ]; + oss << " : " << "Tolerance" << " " << std::setprecision(3) << v; + Action::manager().store(oss.str()); + } + } + + if ( m == AlphaFilter::ALPHA_CHROMAKEY || m == AlphaFilter::ALPHA_FILL) + { + glm::vec4 color = glm::vec4(filter_parameters["Red"], filter_parameters["Green"], filter_parameters["Blue"], 1.f); + if (ImGuiToolkit::IconButton(13, 14)) { + color = glm::vec4(0.f, 0.8f, 0.f, 1.f); + f.setProgramParameter("Red", color.r); + f.setProgramParameter("Green", color.g); + f.setProgramParameter("Blue", color.b); + } + ImGui::SameLine(0, IMGUI_SAME_LINE); + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + if ( ImGui::ColorEdit3("Color", glm::value_ptr(color), ImGuiColorEditFlags_Float | ImGuiColorEditFlags_NoOptions) ) + { + f.setProgramParameter("Red", color.r); + f.setProgramParameter("Green", color.g); + f.setProgramParameter("Blue", color.b); + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + oss << AlphaFilter::operation_label[ f.operation() ]; + oss << " : " << "Color" << " " << color.r << " " << color.g << " " << color.b; + Action::manager().store(oss.str()); + } + } + +} + void ImGuiVisitor::visit (ImageFilter& f) { // Selection of Algorithm diff --git a/ImGuiVisitor.h b/ImGuiVisitor.h index 2914622..be93dd8 100644 --- a/ImGuiVisitor.h +++ b/ImGuiVisitor.h @@ -43,6 +43,7 @@ public: void visit (BlurFilter&) override; void visit (SharpenFilter&) override; void visit (EdgeFilter&) override; + void visit (AlphaFilter&) override; void visit (ImageFilter&) override; }; diff --git a/ImageFilter.cpp b/ImageFilter.cpp index e97ca00..fb6cfb4 100644 --- a/ImageFilter.cpp +++ b/ImageFilter.cpp @@ -770,3 +770,46 @@ void EdgeFilter::accept (Visitor& v) +//////////////////////////////////////// +///// // +//// ALPHA FILTERS /// +/// //// +//////////////////////////////////////// + +const char* AlphaFilter::operation_label[AlphaFilter::ALPHA_INVALID] = { + "Chromakey", "Lumakey", "Fill transparent" +}; + +std::vector< FilteringProgram > AlphaFilter::programs_ = { + FilteringProgram("Chromakey","shaders/filters/chromakey.glsl", "", { { "Red", 0.0}, { "Green", 1.0}, { "Blue", 0.0}, { "Tolerance", 1.0} }), + FilteringProgram("Lumakey", "shaders/filters/lumakey.glsl", "", { { "Tolerance", 0.5} }), + FilteringProgram("coloralpha","shaders/filters/coloralpha.glsl", "", { { "Red", 0.0}, { "Green", 1.0}, { "Blue", 0.0} }) +}; + +AlphaFilter::AlphaFilter (): ImageFilter(), operation_(ALPHA_INVALID) +{ +} + +void AlphaFilter::setOperation(int op) +{ + operation_ = (AlphaOperation) CLAMP(op, ALPHA_CHROMAKEY, ALPHA_INVALID-1); + setProgram( programs_[ (int) operation_] ); +} + +void AlphaFilter::draw (FrameBuffer *input) +{ + // Default + if (operation_ == ALPHA_INVALID) + setOperation( ALPHA_CHROMAKEY ); + + ImageFilter::draw( input ); +} + +void AlphaFilter::accept (Visitor& v) +{ + FrameBufferFilter::accept(v); + v.visit(*this); +} + + + diff --git a/ImageFilter.h b/ImageFilter.h index bff2ad9..5569fc8 100644 --- a/ImageFilter.h +++ b/ImageFilter.h @@ -230,4 +230,34 @@ private: +class AlphaFilter : public ImageFilter +{ +public: + + AlphaFilter(); + + // Operations on alpha + typedef enum { + ALPHA_CHROMAKEY = 0, + ALPHA_LUMAKEY, + ALPHA_FILL, + ALPHA_INVALID + } AlphaOperation; + static const char* operation_label[ALPHA_INVALID]; + AlphaOperation operation () const { return operation_; } + void setOperation(int op); + + // implementation of FrameBufferFilter + Type type() const override { return FrameBufferFilter::FILTER_ALPHA; } + + void draw (FrameBuffer *input) override; + void accept (Visitor& v) override; + +private: + AlphaOperation operation_; + static std::vector< FilteringProgram > programs_; +}; + + + #endif // IMAGEFILTER_H diff --git a/SessionCreator.cpp b/SessionCreator.cpp index c08e6ce..718bb49 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -1304,6 +1304,30 @@ void SessionLoader::visit (EdgeFilter& f) f.setProgramParameters(filter_params); } +void SessionLoader::visit (AlphaFilter& f) +{ + int m = 0; + xmlCurrent_->QueryIntAttribute("operation", &m); + f.setOperation(m); + + std::map< std::string, float > filter_params; + XMLElement* parameters = xmlCurrent_->FirstChildElement("parameters"); + if (parameters) { + XMLElement* param = parameters->FirstChildElement("uniform"); + for( ; param ; param = param->NextSiblingElement()) + { + float val = 0.f; + param->QueryFloatAttribute("value", &val); + const char * name; + param->QueryStringAttribute("name", &name); + if (name) + filter_params[name] = val; + } + } + + f.setProgramParameters(filter_params); +} + void SessionLoader::visit (ImageFilter& f) { const char * filter_name; diff --git a/SessionCreator.h b/SessionCreator.h index 0edc537..e971bfd 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -68,6 +68,7 @@ public: void visit (BlurFilter&) override; void visit (SharpenFilter&) override; void visit (EdgeFilter&) override; + void visit (AlphaFilter&) override; void visit (ImageFilter&) override; // callbacks diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index dd13c79..7de8a73 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -747,6 +747,22 @@ void SessionVisitor::visit (EdgeFilter& f) } } +void SessionVisitor::visit (AlphaFilter& f) +{ + xmlCurrent_->SetAttribute("operation", (int) f.operation()); + + std::map< std::string, float > filter_params = f.program().parameters(); + XMLElement *parameters = xmlDoc_->NewElement( "parameters" ); + xmlCurrent_->InsertEndChild(parameters); + for (auto p = filter_params.begin(); p != filter_params.end(); ++p) + { + XMLElement *param = xmlDoc_->NewElement( "uniform" ); + param->SetAttribute("name", p->first.c_str() ); + param->SetAttribute("value", p->second ); + parameters->InsertEndChild(param); + } +} + void SessionVisitor::visit (ImageFilter& f) { xmlCurrent_->SetAttribute("name", f.program().name().c_str() ); diff --git a/SessionVisitor.h b/SessionVisitor.h index 22bc2b9..3a39c19 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -76,6 +76,7 @@ public: void visit (BlurFilter&) override; void visit (SharpenFilter&) override; void visit (EdgeFilter&) override; + void visit (AlphaFilter&) override; void visit (ImageFilter&) override; // callbacks diff --git a/Visitor.h b/Visitor.h index b682e50..d751ebc 100644 --- a/Visitor.h +++ b/Visitor.h @@ -48,6 +48,7 @@ class ResampleFilter; class BlurFilter; class SharpenFilter; class EdgeFilter; +class AlphaFilter; class ImageFilter; class SourceCallback; @@ -112,6 +113,7 @@ public: virtual void visit (BlurFilter&) {} virtual void visit (SharpenFilter&) {} virtual void visit (EdgeFilter&) {} + virtual void visit (AlphaFilter&) {} virtual void visit (ImageFilter&) {} virtual void visit (SourceCallback&) {} diff --git a/rsc/shaders/filters/chromakey.glsl b/rsc/shaders/filters/chromakey.glsl index 4c11edf..efbb8c5 100644 --- a/rsc/shaders/filters/chromakey.glsl +++ b/rsc/shaders/filters/chromakey.glsl @@ -46,11 +46,10 @@ float colorclose(vec3 yuv, vec3 keyYuv, vec2 tol) void mainImage( out vec4 fragColor, in vec2 fragCoord ) { - vec2 fragPos = fragCoord.xy / iResolution.xy; - vec4 texColor0 = texture(iChannel0, fragPos); + vec4 texColor0 = texture(iChannel0, fragCoord.xy / iResolution.xy); //convert from RGB to YCvCr/YUV - vec4 keyYUV = RGBtoYUV * chromaKey; + vec4 keyYUV = RGBtoYUV * chromaKey; vec4 yuv = RGBtoYUV * texColor0; float mask = 1.0 - colorclose(yuv.rgb, keyYUV.rgb, maskRange); diff --git a/rsc/shaders/filters/coloralpha.glsl b/rsc/shaders/filters/coloralpha.glsl new file mode 100644 index 0000000..d87d39d --- /dev/null +++ b/rsc/shaders/filters/coloralpha.glsl @@ -0,0 +1,10 @@ +uniform float Red; +uniform float Green; +uniform float Blue; + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 fragPos = fragCoord.xy / iResolution.xy; + vec4 color = texture(iChannel0, fragPos); + fragColor = vec4( mix(vec3(Red, Green, Blue), color.rgb, color.a), 1.f); +} diff --git a/rsc/shaders/filters/lumakey.glsl b/rsc/shaders/filters/lumakey.glsl new file mode 100644 index 0000000..2dce34f --- /dev/null +++ b/rsc/shaders/filters/lumakey.glsl @@ -0,0 +1,40 @@ +uniform float Tolerance; + + +//float sRGBtoLin( in float v ) { +// // Send this function a decimal sRGB gamma encoded color value +// // between 0.0 and 1.0, and it returns a linearized value. +// if ( v <= 0.04045 ) { +// return v / 12.92; +// } else { +// return pow((( v + 0.055)/1.055),2.4); +// } +//} + +//// returns L* which is "perceptual lightness" +//float lightness ( in vec3 color ) +//{ +// float Y = 0.2126 * sRGBtoLin(color.r) +// + 0.7152 * sRGBtoLin(color.g) +// + 0.0722 * sRGBtoLin(color.b); + +// if ( Y <= (216./24389.) ) { // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036 +// Y = Y * (24389./27.); // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296 +// } else { +// Y = pow(Y,(1./3.)) * 116. - 16.; +// } + +// return 0.01 * Y; +//} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 fragPos = fragCoord.xy / iResolution.xy; + vec3 RGB = texture(iChannel0, fragPos).rgb; + + + float L = dot(RGB, vec3(0.299, 0.587, 0.114)); +// float L = lightness( RGB ); + + fragColor = vec4( RGB, smoothstep( 0.0, Tolerance, L ) ); +}