From 2f6ba39b41ee587a746b5a70537a831fcf21c175 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 21 Mar 2015 18:38:01 -0700 Subject: [PATCH] obs-filters: Add color key filter Masks out a specific color range in RGB color space. Commonly used with solid image data or animated video rather than live video. --- plugins/obs-filters/CMakeLists.txt | 1 + plugins/obs-filters/color-key-filter.c | 254 ++++++++++++++++++ .../obs-filters/data/color_key_filter.effect | 92 +++++++ plugins/obs-filters/data/locale/en-US.ini | 2 + plugins/obs-filters/obs-filters.c | 2 + 5 files changed, 351 insertions(+) create mode 100644 plugins/obs-filters/color-key-filter.c create mode 100644 plugins/obs-filters/data/color_key_filter.effect diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index c3b1355ce..113770290 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -6,6 +6,7 @@ set(obs-filters_SOURCES async-delay-filter.c crop-filter.c chroma-key-filter.c + color-key-filter.c mask-filter.c) add_library(obs-filters MODULE diff --git a/plugins/obs-filters/color-key-filter.c b/plugins/obs-filters/color-key-filter.c new file mode 100644 index 000000000..bacdb94a6 --- /dev/null +++ b/plugins/obs-filters/color-key-filter.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include + +#define SETTING_OPACITY "opacity" +#define SETTING_CONTRAST "contrast" +#define SETTING_BRIGHTNESS "brightness" +#define SETTING_GAMMA "gamma" +#define SETTING_COLOR_TYPE "key_color_type" +#define SETTING_KEY_COLOR "key_color" +#define SETTING_SIMILARITY "similarity" +#define SETTING_SMOOTHNESS "smoothness" + +#define TEXT_OPACITY obs_module_text("Opacity") +#define TEXT_CONTRAST obs_module_text("Contrast") +#define TEXT_BRIGHTNESS obs_module_text("Brightness") +#define TEXT_GAMMA obs_module_text("Gamma") +#define TEXT_COLOR_TYPE obs_module_text("KeyColorType") +#define TEXT_KEY_COLOR obs_module_text("KeyColor") +#define TEXT_SIMILARITY obs_module_text("Similarity") +#define TEXT_SMOOTHNESS obs_module_text("Smoothness") + +struct color_key_filter_data { + obs_source_t *context; + + gs_effect_t *effect; + + gs_eparam_t *color_param; + gs_eparam_t *contrast_param; + gs_eparam_t *brightness_param; + gs_eparam_t *gamma_param; + + gs_eparam_t *key_color_param; + gs_eparam_t *similarity_param; + gs_eparam_t *smoothness_param; + + struct vec4 color; + float contrast; + float brightness; + float gamma; + + struct vec4 key_color; + float similarity; + float smoothness; +}; + +static const char *color_key_name(void) +{ + return obs_module_text("ColorKeyFilter"); +} + +static inline void color_settings_update( + struct color_key_filter_data *filter, obs_data_t *settings) +{ + uint32_t opacity = (uint32_t)obs_data_get_int(settings, + SETTING_OPACITY); + uint32_t color = 0xFFFFFF | (((opacity * 255) / 100) << 24); + double contrast = obs_data_get_double(settings, SETTING_CONTRAST); + double brightness = obs_data_get_double(settings, SETTING_BRIGHTNESS); + double gamma = obs_data_get_double(settings, SETTING_GAMMA); + + contrast = (contrast < 0.0) ? + (1.0 / (-contrast + 1.0)) : (contrast + 1.0); + + brightness *= 0.5; + + gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0)); + + filter->contrast = (float)contrast; + filter->brightness = (float)brightness; + filter->gamma = (float)gamma; + + vec4_from_rgba(&filter->color, color); +} + +static inline void key_settings_update( + struct color_key_filter_data *filter, obs_data_t *settings) +{ + int64_t similarity = obs_data_get_int(settings, SETTING_SIMILARITY); + int64_t smoothness = obs_data_get_int(settings, SETTING_SMOOTHNESS); + uint32_t key_color = (uint32_t)obs_data_get_int(settings, + SETTING_KEY_COLOR); + const char *key_type = obs_data_get_string(settings, + SETTING_COLOR_TYPE); + + if (strcmp(key_type, "green") == 0) + key_color = 0x00FF00; + else if (strcmp(key_type, "blue") == 0) + key_color = 0xFF0000; + else if (strcmp(key_type, "red") == 0) + key_color = 0x0000FF; + else if (strcmp(key_type, "magenta") == 0) + key_color = 0xFF00FF; + + vec4_from_rgba(&filter->key_color, key_color | 0xFF000000); + + filter->similarity = (float)similarity / 1000.0f; + filter->smoothness = (float)smoothness / 1000.0f; +} + +static void color_key_update(void *data, obs_data_t *settings) +{ + struct color_key_filter_data *filter = data; + + color_settings_update(filter, settings); + key_settings_update(filter, settings); +} + +static void color_key_destroy(void *data) +{ + struct color_key_filter_data *filter = data; + + if (filter->effect) { + obs_enter_graphics(); + gs_effect_destroy(filter->effect); + obs_leave_graphics(); + } + + bfree(data); +} + +static void *color_key_create(obs_data_t *settings, obs_source_t *context) +{ + struct color_key_filter_data *filter = + bzalloc(sizeof(struct color_key_filter_data)); + char *effect_path = obs_module_file("color_key_filter.effect"); + + filter->context = context; + + obs_enter_graphics(); + + filter->effect = gs_effect_create_from_file(effect_path, NULL); + if (filter) { + filter->color_param = gs_effect_get_param_by_name( + filter->effect, "color"); + filter->contrast_param = gs_effect_get_param_by_name( + filter->effect, "contrast"); + filter->brightness_param = gs_effect_get_param_by_name( + filter->effect, "brightness"); + filter->gamma_param = gs_effect_get_param_by_name( + filter->effect, "gamma"); + filter->key_color_param = gs_effect_get_param_by_name( + filter->effect, "key_color"); + filter->similarity_param = gs_effect_get_param_by_name( + filter->effect, "similarity"); + filter->smoothness_param = gs_effect_get_param_by_name( + filter->effect, "smoothness"); + } + + obs_leave_graphics(); + + bfree(effect_path); + + if (!filter->effect) { + color_key_destroy(filter); + return NULL; + } + + color_key_update(filter, settings); + return filter; +} + +static void color_key_render(void *data, gs_effect_t *effect) +{ + struct color_key_filter_data *filter = data; + + obs_source_process_filter_begin(filter->context, GS_RGBA, + OBS_ALLOW_DIRECT_RENDERING); + + gs_effect_set_vec4(filter->color_param, &filter->color); + gs_effect_set_float(filter->contrast_param, filter->contrast); + gs_effect_set_float(filter->brightness_param, filter->brightness); + gs_effect_set_float(filter->gamma_param, filter->gamma); + gs_effect_set_vec4(filter->key_color_param, &filter->key_color); + gs_effect_set_float(filter->similarity_param, filter->similarity); + gs_effect_set_float(filter->smoothness_param, filter->smoothness); + + obs_source_process_filter_end(filter->context, filter->effect, 0, 0); + + UNUSED_PARAMETER(effect); +} + +static bool key_type_changed(obs_properties_t *props, obs_property_t *p, + obs_data_t *settings) +{ + const char *type = obs_data_get_string(settings, SETTING_COLOR_TYPE); + bool custom = strcmp(type, "custom") == 0; + + obs_property_set_visible(obs_properties_get(props, SETTING_KEY_COLOR), + custom); + + UNUSED_PARAMETER(p); + return true; +} + +static obs_properties_t *color_key_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_property_t *p = obs_properties_add_list(props, + SETTING_COLOR_TYPE, TEXT_COLOR_TYPE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, obs_module_text("Green"), "green"); + obs_property_list_add_string(p, obs_module_text("Blue"), "blue"); + obs_property_list_add_string(p, obs_module_text("Red"), "red"); + obs_property_list_add_string(p, obs_module_text("Magenta"), "magenta"); + obs_property_list_add_string(p, obs_module_text("CustomColor"), + "custom"); + + obs_property_set_modified_callback(p, key_type_changed); + + obs_properties_add_color(props, SETTING_KEY_COLOR, TEXT_KEY_COLOR); + obs_properties_add_int_slider(props, SETTING_SIMILARITY, + TEXT_SIMILARITY, 1, 1000, 1); + obs_properties_add_int_slider(props, SETTING_SMOOTHNESS, + TEXT_SMOOTHNESS, 1, 1000, 1); + + obs_properties_add_int(props, SETTING_OPACITY, TEXT_OPACITY, 0, 100, 1); + obs_properties_add_float_slider(props, SETTING_CONTRAST, + TEXT_CONTRAST, -1.0, 1.0, 0.01); + obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, + TEXT_BRIGHTNESS, -1.0, 1.0, 0.01); + obs_properties_add_float_slider(props, SETTING_GAMMA, + TEXT_GAMMA, -1.0, 1.0, 0.01); + + UNUSED_PARAMETER(data); + return props; +} + +static void color_key_defaults(obs_data_t *settings) +{ + obs_data_set_default_int(settings, SETTING_OPACITY, 100); + obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0); + obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0); + obs_data_set_default_double(settings, SETTING_GAMMA, 0.0); + obs_data_set_default_int(settings, SETTING_KEY_COLOR, 0x00FF00); + obs_data_set_default_string(settings, SETTING_COLOR_TYPE, "green"); + obs_data_set_default_int(settings, SETTING_SIMILARITY, 80); + obs_data_set_default_int(settings, SETTING_SMOOTHNESS, 50); +} + +struct obs_source_info color_key_filter = { + .id = "color_key_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = color_key_name, + .create = color_key_create, + .destroy = color_key_destroy, + .video_render = color_key_render, + .update = color_key_update, + .get_properties = color_key_properties, + .get_defaults = color_key_defaults +}; diff --git a/plugins/obs-filters/data/color_key_filter.effect b/plugins/obs-filters/data/color_key_filter.effect new file mode 100644 index 000000000..7d611ec56 --- /dev/null +++ b/plugins/obs-filters/data/color_key_filter.effect @@ -0,0 +1,92 @@ +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float4x4 color_matrix = {1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0}; +uniform float3 color_range_min = {0.0, 0.0, 0.0}; +uniform float3 color_range_max = {1.0, 1.0, 1.0}; + +uniform float4 color; +uniform float contrast; +uniform float brightness; +uniform float gamma; + +uniform float4 key_color; +uniform float similarity; +uniform float smoothness; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData VSDefault(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; +} + +float4 CalcColor(float4 rgba) +{ + return float4(pow(rgba.rgb, float3(gamma, gamma, gamma)) * contrast + brightness, rgba.a); +} + +float GetColorDist(float3 rgb) +{ + return distance(key_color.rgb, rgb); +} + +float4 SampleYUVToRGB(float2 uv) +{ + float4 yuv = image.Sample(textureSampler, uv); + yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max); + return saturate(mul(float4(yuv.xyz, 1.0), color_matrix)); +} + +float4 ProcessColorKey(float4 rgba, VertData v_in) +{ + float colorDist = GetColorDist(rgba.rgb); + float baseMask = colorDist - similarity; + rgba.a *= saturate(max(colorDist - similarity, 0.0) / smoothness); + + return CalcColor(rgba); +} + +float4 PSColorKeyRGBA(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv) * color; + return ProcessColorKey(rgba, v_in); +} + +float4 PSColorKeyMatrix(VertData v_in) : TARGET +{ + float4 rgba = SampleYUVToRGB(v_in.uv) * color; + return ProcessColorKey(rgba, v_in); +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSColorKeyRGBA(v_in); + } +} + +technique DrawMatrix +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSColorKeyMatrix(v_in); + } +} diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index d9d7d0b85..986b8c0c4 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -3,6 +3,7 @@ MaskFilter="Image Mask/Blend" AsyncDelayFilter="Video Delay (Async)" CropFilter="Crop" ChromaKeyFilter="Chroma Key" +ColorKeyFilter="Color Key" DelayMs="Delay (milliseconds)" Type="Type" MaskBlendType.MaskColor="Alpha Mask (Color Channel)" @@ -31,6 +32,7 @@ Crop.Width="Width" Crop.Height="Height" Crop.Relative="Relative" CustomColor="Custom Color" +Red="Red" Green="Green" Blue="Blue" Magenta="Magenta" diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 7f9d0c8c5..c9602f182 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -7,6 +7,7 @@ OBS_MODULE_USE_DEFAULT_LOCALE("obs-filters", "en-US") extern struct obs_source_info mask_filter; extern struct obs_source_info crop_filter; extern struct obs_source_info color_filter; +extern struct obs_source_info color_key_filter; extern struct obs_source_info chroma_key_filter; extern struct obs_source_info async_delay_filter; @@ -15,6 +16,7 @@ bool obs_module_load(void) obs_register_source(&mask_filter); obs_register_source(&crop_filter); obs_register_source(&color_filter); + obs_register_source(&color_key_filter); obs_register_source(&chroma_key_filter); obs_register_source(&async_delay_filter); return true;