Bläddra i källkod

obs-filters: Add "Color Grading" filter

This allows for color grading based on a color look-up table. Commonly
used to color correct and/or grade video that has been shot in Log.

Closes jp9000/obs-studio#747
mape 8 år sedan
förälder
incheckning
7d9a68c18d

+ 1 - 0
plugins/obs-filters/CMakeLists.txt

@@ -27,6 +27,7 @@ set(obs-filters_SOURCES
 	scroll-filter.c
 	chroma-key-filter.c
 	color-key-filter.c
+	color-grade-filter.c
 	sharpness-filter.c
 	gain-filter.c
 	noise-gate-filter.c

+ 159 - 0
plugins/obs-filters/color-grade-filter.c

@@ -0,0 +1,159 @@
+#include <obs-module.h>
+#include <graphics/image-file.h>
+#include <util/dstr.h>
+
+#define SETTING_IMAGE_PATH             "image_path"
+#define SETTING_CLUT_AMOUNT            "clut_amount"
+
+#define TEXT_IMAGE_PATH                obs_module_text("Path")
+#define TEXT_AMOUNT                    obs_module_text("Amount")
+
+struct lut_filter_data {
+	obs_source_t                   *context;
+	gs_effect_t                    *effect;
+	gs_texture_t                   *target;
+	gs_image_file_t                image;
+
+	char                           *file;
+	float                          clut_amount;
+};
+
+static const char *color_grade_filter_get_name(void *unused)
+{
+	UNUSED_PARAMETER(unused);
+	return obs_module_text("ColorGradeFilter");
+}
+
+static void color_grade_filter_update(void *data, obs_data_t *settings)
+{
+	struct lut_filter_data *filter = data;
+
+	const char *path = obs_data_get_string(settings, SETTING_IMAGE_PATH);
+	double clut_amount = obs_data_get_double(settings, SETTING_CLUT_AMOUNT);
+
+	bfree(filter->file);
+	if (path)
+		filter->file = bstrdup(path);
+
+	obs_enter_graphics();
+	gs_image_file_free(&filter->image);
+	obs_leave_graphics();
+
+	gs_image_file_init(&filter->image, path);
+
+	obs_enter_graphics();
+
+	gs_image_file_init_texture(&filter->image);
+
+	filter->target = filter->image.texture;
+	filter->clut_amount = (float)clut_amount;
+
+	char *effect_path = obs_module_file("color_grade_filter.effect");
+	gs_effect_destroy(filter->effect);
+	filter->effect = gs_effect_create_from_file(effect_path, NULL);
+	bfree(effect_path);
+
+	obs_leave_graphics();
+}
+
+static void color_grade_filter_defaults(obs_data_t *settings)
+{
+	obs_data_set_default_double(settings, SETTING_CLUT_AMOUNT, 1);
+}
+
+static obs_properties_t *color_grade_filter_properties(void *data)
+{
+	struct lut_filter_data *s = data;
+	struct dstr path = {0};
+	const char *slash;
+
+	obs_properties_t *props = obs_properties_create();
+	struct dstr filter_str = {0};
+
+	dstr_cat(&filter_str, "(*.png)");
+
+	if (s && s->file && *s->file) {
+		dstr_copy(&path, s->file);
+	} else {
+		dstr_copy(&path, obs_module_file("LUTs"));
+		dstr_cat_ch(&path, '/');
+	}
+
+	dstr_replace(&path, "\\", "/");
+	slash = strrchr(path.array, '/');
+	if (slash)
+		dstr_resize(&path, slash - path.array + 1);
+
+	obs_properties_add_path(props, SETTING_IMAGE_PATH, TEXT_IMAGE_PATH,
+			OBS_PATH_FILE, filter_str.array, path.array);
+	obs_properties_add_float_slider(props, SETTING_CLUT_AMOUNT,
+			TEXT_AMOUNT, 0, 1, 0.01);
+
+	dstr_free(&filter_str);
+
+	UNUSED_PARAMETER(data);
+	return props;
+}
+
+static void *color_grade_filter_create(
+		obs_data_t *settings, obs_source_t *context)
+{
+	struct lut_filter_data *filter =
+		bzalloc(sizeof(struct lut_filter_data));
+	filter->context = context;
+
+	obs_source_update(context, settings);
+	return filter;
+}
+
+static void color_grade_filter_destroy(void *data)
+{
+	struct lut_filter_data *filter = data;
+
+	obs_enter_graphics();
+	gs_effect_destroy(filter->effect);
+	gs_image_file_free(&filter->image);
+	obs_leave_graphics();
+
+	bfree(filter->file);
+	bfree(filter);
+}
+
+static void color_grade_filter_render(void *data, gs_effect_t *effect)
+{
+	struct lut_filter_data *filter = data;
+	obs_source_t *target = obs_filter_get_target(filter->context);
+	gs_eparam_t *param;
+
+	if (!target || !filter->target || !filter->effect) {
+		obs_source_skip_video_filter(filter->context);
+		return;
+	}
+
+	if (!obs_source_process_filter_begin(filter->context, GS_RGBA,
+				OBS_ALLOW_DIRECT_RENDERING))
+		return;
+
+	param = gs_effect_get_param_by_name(filter->effect, "clut");
+	gs_effect_set_texture(param, filter->target);
+
+	param = gs_effect_get_param_by_name(filter->effect, "clut_amount");
+	gs_effect_set_float(param, filter->clut_amount);
+
+	obs_source_process_filter_end(filter->context, filter->effect, 0, 0);
+
+	UNUSED_PARAMETER(effect);
+}
+
+struct obs_source_info color_grade_filter = {
+	.id                            = "clut_filter",
+	.type                          = OBS_SOURCE_TYPE_FILTER,
+	.output_flags                  = OBS_SOURCE_VIDEO,
+	.get_name                      = color_grade_filter_get_name,
+	.create                        = color_grade_filter_create,
+	.destroy                       = color_grade_filter_destroy,
+	.update                        = color_grade_filter_update,
+	.get_defaults                  = color_grade_filter_defaults,
+	.get_properties                = color_grade_filter_properties,
+	.video_render                  = color_grade_filter_render
+};

BIN
plugins/obs-filters/data/LUTs/black_and_white.png


BIN
plugins/obs-filters/data/LUTs/original.png


BIN
plugins/obs-filters/data/LUTs/posterize.png


BIN
plugins/obs-filters/data/LUTs/red_isolated.png


BIN
plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png


+ 66 - 0
plugins/obs-filters/data/color_grade_filter.effect

@@ -0,0 +1,66 @@
+uniform float4x4 ViewProj;
+uniform texture2d image;
+
+uniform texture2d clut;
+uniform float clut_amount;
+
+sampler_state textureSampler {
+	Filter    = Linear;
+	AddressU  = Clamp;
+	AddressV  = Clamp;
+};
+
+struct VertDataIn {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+struct VertDataOut {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertDataOut VSDefault(VertDataIn v_in)
+{
+	VertDataOut vert_out;
+	vert_out.uv = v_in.uv;
+	vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
+	return vert_out;
+}
+
+float4 LUT(VertDataOut v_in) : TARGET
+{
+	float4 textureColor = image.Sample(textureSampler, v_in.uv);
+	float blueColor = textureColor.b * 63.0;
+
+	float2 quad1;
+	quad1.y = floor(floor(blueColor) / 8.0);
+	quad1.x = floor(blueColor) - (quad1.y * 8.0);
+
+	float2 quad2;
+	quad2.y = floor(ceil(blueColor) / 8.0);
+	quad2.x = ceil(blueColor) - (quad2.y * 8.0);
+
+	float2 texPos1;
+	texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
+	texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
+
+	float2 texPos2;
+	texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
+	texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
+
+	float4 newColor1 = clut.Sample(textureSampler, texPos1);
+	float4 newColor2 = clut.Sample(textureSampler, texPos2);
+	float4 luttedColor = lerp(newColor1, newColor2, frac(blueColor));
+
+	return lerp(textureColor, luttedColor, clut_amount);
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = LUT(v_in);
+	}
+}

+ 2 - 0
plugins/obs-filters/data/locale/en-US.ini

@@ -1,4 +1,5 @@
 ColorFilter="Color Correction"
+ColorGradeFilter="Color Grade"
 MaskFilter="Image Mask/Blend"
 AsyncDelayFilter="Video Delay (Async)"
 CropFilter="Crop/Pad"
@@ -64,3 +65,4 @@ ScaleFiltering.Lanczos="Lanczos"
 NoiseSuppress.SuppressLevel="Suppression Level (dB)"
 Saturation="Saturation"
 HueShift="Hue Shift"
+Amount="Amount"

+ 2 - 0
plugins/obs-filters/obs-filters.c

@@ -12,6 +12,7 @@ extern struct obs_source_info color_filter;
 extern struct obs_source_info scale_filter;
 extern struct obs_source_info scroll_filter;
 extern struct obs_source_info color_key_filter;
+extern struct obs_source_info color_grade_filter;
 extern struct obs_source_info sharpness_filter;
 extern struct obs_source_info chroma_key_filter;
 extern struct obs_source_info async_delay_filter;
@@ -29,6 +30,7 @@ bool obs_module_load(void)
 	obs_register_source(&scale_filter);
 	obs_register_source(&scroll_filter);
 	obs_register_source(&color_key_filter);
+	obs_register_source(&color_grade_filter);
 	obs_register_source(&sharpness_filter);
 	obs_register_source(&chroma_key_filter);
 	obs_register_source(&async_delay_filter);