Skip to content

Commit de2f42f

Browse files
committed
Added filter-gaussian-blur.lua
1 parent 9c4bb24 commit de2f42f

File tree

2 files changed

+305
-0
lines changed

2 files changed

+305
-0
lines changed

src/filter-gaussian-blur.lua

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
obs = obslua
2+
bit = require("bit")
3+
4+
MAX_KERNEL_SIZE = 16 -- max.: 3 * MAX_SIGMA
5+
MAX_SIGMA = 10.0
6+
7+
SETTING_KERNEL_SIZE = 'kernel_size'
8+
SETTING_SIGMA = 'sigma'
9+
SETTING_USE_MASK = 'use_mask'
10+
SETTING_INVERT_MASK = 'invert_mask'
11+
SETTING_MASK_IMAGE = 'mask_image'
12+
13+
TEXT_KERNEL_SIZE = 'Kernel Size'
14+
TEXT_SIGMA = 'Sigma'
15+
TEXT_USE_MASK = 'Use Blur Mask'
16+
TEXT_INVERT_MASK = 'Invert Blur Mask'
17+
TEXT_MASK = 'Blur Mask Image (Alpha)'
18+
19+
IMAGE_FILTER = 'Images (*.bmp *.jpg *.jpeg *.tga *.gif *.png);; All Files (*.*)'
20+
21+
source_def = {}
22+
source_def.id = 'filter_gaussian_blur'
23+
source_def.type = obs.OBS_SOURCE_TYPE_FILTER
24+
source_def.output_flags = bit.bor(obs.OBS_SOURCE_VIDEO)
25+
26+
function gaussian(sigma, x)
27+
factor = 1.0 / math.sqrt(2 * math.pi * sigma * sigma)
28+
exponent = -1 * (x * x) / (2 * sigma * sigma);
29+
return factor * math.exp(exponent)
30+
end
31+
32+
function set_render_size(filter)
33+
target = obs.obs_filter_get_target(filter.context)
34+
35+
local width, height
36+
if target == nil then
37+
width = 0
38+
height = 0
39+
else
40+
width = obs.obs_source_get_base_width(target)
41+
height = obs.obs_source_get_base_height(target)
42+
end
43+
44+
filter.render_width = width
45+
filter.render_height = height
46+
filter.pixel_size.x = 1.0 / width
47+
filter.pixel_size.y = 1.0 / height
48+
end
49+
50+
source_def.get_name = function()
51+
return 'Gaussian Blur'
52+
end
53+
54+
source_def.destroy = function(filter)
55+
if filter.effect ~= nil then
56+
obs.obs_enter_graphics()
57+
obs.gs_effect_destroy(filter.effect)
58+
obs.obs_leave_graphics()
59+
end
60+
end
61+
62+
source_def.update = function(filter, settings)
63+
local kernel_size = obs.obs_data_get_int(settings, SETTING_KERNEL_SIZE)
64+
filter.kernel_size = math.ceil(kernel_size / 2)
65+
filter.sigma = obs.obs_data_get_double(settings, SETTING_SIGMA)
66+
filter.use_mask = obs.obs_data_get_bool(settings, SETTING_USE_MASK)
67+
filter.invert_mask = obs.obs_data_get_bool(settings, SETTING_INVERT_MASK)
68+
local mask_image_path = obs.obs_data_get_string(settings, SETTING_MASK_IMAGE)
69+
70+
local kernel = {}
71+
local sum = 0.0
72+
for i = 1, filter.kernel_size do
73+
kernel[i] = gaussian(filter.sigma, i - 1)
74+
sum = sum + kernel[i] + kernel[i]
75+
end
76+
for i = filter.kernel_size + 1, MAX_KERNEL_SIZE do
77+
kernel[i] = 0.0
78+
end
79+
sum = sum - kernel[1]
80+
local norm = 1.0 / sum
81+
for i = 1, MAX_KERNEL_SIZE do
82+
kernel[i] = kernel[i] * norm
83+
print(kernel[i])
84+
end
85+
86+
obs.vec4_set(filter.kernel0, kernel[1], kernel[2], kernel[3], kernel[4])
87+
obs.vec4_set(filter.kernel1, kernel[5], kernel[6], kernel[7], kernel[8])
88+
obs.vec4_set(filter.kernel2, kernel[9], kernel[10], kernel[11], kernel[12])
89+
obs.vec4_set(filter.kernel3, kernel[13], kernel[14], kernel[15], kernel[16])
90+
91+
if filter.use_mask then
92+
obs.obs_enter_graphics()
93+
obs.gs_image_file_free(filter.mask_image)
94+
obs.obs_leave_graphics()
95+
96+
obs.gs_image_file_init(filter.mask_image, mask_image_path)
97+
98+
obs.obs_enter_graphics()
99+
obs.gs_image_file_init_texture(filter.mask_image)
100+
obs.obs_leave_graphics()
101+
102+
if not filter.mask_image.loaded then
103+
print("failed to load texture " .. mask_image_path);
104+
end
105+
end
106+
end
107+
108+
source_def.create = function(settings, source)
109+
filter = {}
110+
effect_path = script_path() .. 'filter-gaussian-blur/filter-gaussian-blur.effect'
111+
112+
filter.context = source
113+
114+
filter.mask_image = obs.gs_image_file()
115+
116+
filter.pixel_size = obs.vec2()
117+
118+
filter.kernel_size = 4
119+
filter.kernel0 = obs.vec4()
120+
filter.kernel1 = obs.vec4()
121+
filter.kernel2 = obs.vec4()
122+
filter.kernel3 = obs.vec4()
123+
124+
set_render_size(filter)
125+
126+
obs.obs_enter_graphics()
127+
128+
filter.effect = obs.gs_effect_create_from_file(effect_path, nil)
129+
if filter.effect ~= nil then
130+
filter.mask_param = obs.gs_effect_get_param_by_name(filter.effect, 'mask')
131+
filter.use_mask_param = obs.gs_effect_get_param_by_name(filter.effect, 'use_mask')
132+
filter.invert_mask_param = obs.gs_effect_get_param_by_name(filter.effect, 'invert_mask')
133+
134+
filter.pixel_size_param = obs.gs_effect_get_param_by_name(filter.effect, 'pixel_size')
135+
136+
filter.kernel0_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel0')
137+
filter.kernel1_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel1')
138+
filter.kernel2_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel2')
139+
filter.kernel3_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel3')
140+
filter.kernel_size_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel_size')
141+
end
142+
143+
obs.obs_leave_graphics()
144+
145+
if filter.effect == nil then
146+
source_def.destroy(filter)
147+
return nil
148+
end
149+
150+
source_def.update(filter, settings)
151+
return filter
152+
end
153+
154+
source_def.get_width = function(filter)
155+
return filter.render_width
156+
end
157+
158+
source_def.get_height = function(filter)
159+
return filter.render_height
160+
end
161+
162+
source_def.video_render = function(filter, effect)
163+
obs.obs_source_process_filter_begin(filter.context, obs.GS_RGBA, obs.OBS_NO_DIRECT_RENDERING)
164+
165+
obs.gs_effect_set_texture(filter.mask_param, filter.mask_image.texture)
166+
obs.gs_effect_set_bool(filter.use_mask_param, filter.use_mask)
167+
obs.gs_effect_set_bool(filter.invert_mask_param, filter.invert_mask)
168+
169+
obs.gs_effect_set_vec2(filter.pixel_size_param, filter.pixel_size)
170+
171+
obs.gs_effect_set_vec4(filter.kernel0_param, filter.kernel0)
172+
obs.gs_effect_set_vec4(filter.kernel1_param, filter.kernel1)
173+
obs.gs_effect_set_vec4(filter.kernel2_param, filter.kernel2)
174+
obs.gs_effect_set_vec4(filter.kernel3_param, filter.kernel3)
175+
obs.gs_effect_set_int(filter.kernel_size_param, filter.kernel_size)
176+
177+
obs.obs_source_process_filter_end(filter.context, filter.effect, filter.render_width, filter.render_height)
178+
end
179+
180+
source_def.get_properties = function(settings)
181+
props = obs.obs_properties_create()
182+
183+
obs.obs_properties_add_int_slider(props, SETTING_KERNEL_SIZE, TEXT_KERNEL_SIZE, 1, MAX_KERNEL_SIZE * 2 - 1, 2)
184+
obs.obs_properties_add_float_slider(props, SETTING_SIGMA, TEXT_SIGMA, 1.0, MAX_SIGMA, 0.01)
185+
obs.obs_properties_add_bool(props, SETTING_USE_MASK, TEXT_USE_MASK)
186+
obs.obs_properties_add_bool(props, SETTING_INVERT_MASK, TEXT_INVERT_MASK)
187+
obs.obs_properties_add_path(props, SETTING_MASK_IMAGE, TEXT_MASK_IMAGE, obs.OBS_PATH_FILE, IMAGE_FILTER, nil)
188+
189+
return props
190+
end
191+
192+
source_def.get_defaults = function(settings)
193+
obs.obs_data_set_default_int(settings, SETTING_KERNEL_SIZE, 3)
194+
obs.obs_data_set_default_double(settings, SETTING_SIGMA, 1.0)
195+
obs.obs_data_set_default_bool(settings, SETTING_USE_MASK, false)
196+
obs.obs_data_set_default_bool(settings, SETTING_INVERT_MASK, false)
197+
end
198+
199+
source_def.video_tick = function(filter, seconds)
200+
set_render_size(filter)
201+
end
202+
203+
obs.obs_register_source(source_def)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
uniform float4x4 ViewProj;
2+
uniform texture2d image;
3+
4+
uniform texture2d mask;
5+
uniform bool use_mask;
6+
uniform bool invert_mask;
7+
8+
uniform float2 pixel_size;
9+
10+
uniform float4 kernel0;
11+
uniform float4 kernel1;
12+
uniform float4 kernel2;
13+
uniform float4 kernel3;
14+
uniform int kernel_size;
15+
16+
sampler_state textureSampler {
17+
Filter = Linear;
18+
AddressU = Border;
19+
AddressV = Border;
20+
BorderColor = 00000000;
21+
};
22+
23+
struct VertDataIn {
24+
float4 pos : POSITION;
25+
float2 uv : TEXCOORD0;
26+
};
27+
28+
struct VertDataOut {
29+
float4 pos : POSITION;
30+
float2 uv : TEXCOORD0;
31+
};
32+
33+
VertDataOut VSDefault(VertDataIn v_in)
34+
{
35+
VertDataOut vert_out;
36+
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
37+
vert_out.uv = v_in.uv;
38+
return vert_out;
39+
}
40+
41+
float get_kernel(int x)
42+
{
43+
int abs_x = abs(x);
44+
if(abs_x >= kernel_size)
45+
return 0.0;
46+
47+
int mod_x = abs_x % 4;
48+
switch(abs_x / 4)
49+
{
50+
case 0:
51+
return kernel0[mod_x];
52+
case 1:
53+
return kernel1[mod_x];
54+
case 2:
55+
return kernel2[mod_x];
56+
case 3:
57+
return kernel3[mod_x];
58+
}
59+
return 0.0;
60+
}
61+
62+
float get_kernel_2D(int x, int y)
63+
{
64+
return get_kernel(x) * get_kernel(y);
65+
}
66+
67+
float2 translate_pixel(float2 uv, int x, int y)
68+
{
69+
return uv + float2(x * pixel_size.x, y * pixel_size.y);
70+
}
71+
72+
float4 PassThrough(VertDataOut v_in) : TARGET
73+
{
74+
float4 rgba = float4(0.0, 0.0, 0.0, 0.0);
75+
for(int x = 1 - kernel_size; x < kernel_size; x++)
76+
{
77+
for(int y = 1 - kernel_size; y < kernel_size; y++)
78+
{
79+
rgba += get_kernel_2D(x, y) * image.Sample(textureSampler, translate_pixel(v_in.uv, x, y));
80+
}
81+
}
82+
if(use_mask)
83+
{
84+
float4 original_color = image.Sample(textureSampler, v_in.uv);
85+
float mask_alpha = mask.Sample(textureSampler, v_in.uv).a;
86+
87+
if(invert_mask)
88+
mask_alpha = 1 - mask_alpha;
89+
90+
rgba = rgba * mask_alpha + original_color * (1 - mask_alpha);
91+
}
92+
return rgba;
93+
}
94+
95+
technique Draw
96+
{
97+
pass
98+
{
99+
vertex_shader = VSDefault(v_in);
100+
pixel_shader = PassThrough(v_in);
101+
}
102+
}

0 commit comments

Comments
 (0)