filter-blur: Add Linear Gaussian Blur

Similar to Linear Box Blur, this version of Gaussian Blur reduces the total number of sample by up to n. This results in a total sample count (per pass) of O(n+1) for even radii and O(n+2) for odd radii. The quality sacrificed to do this is higher this time, though careful adjustment of the halfTexelDelta value can bring it much closer to normal Gaussian Blur. The current offset however had no noticable effects on visual quality.

See Also: #21 Blur Quality
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2018-12-22 22:54:17 +01:00
parent 93df9b50b8
commit 92c4b54177
4 changed files with 73 additions and 15 deletions

View file

@ -155,6 +155,66 @@ technique Gaussian
}
}
/// Blur: Gaussian Linear
float4 PSGaussianLinearBlur(VertDataOut vtx) : TARGET {
// Origin sample must always be sampled.
// Even, Odd must decide differently than Box Blur
// Radius 5 (Odd):
// [-5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5]
// S ^-S-^ ^-S-^ S ^-S-^ ^-S-^ S
// S ^-S-^ ^-S-^ S ^-S-^ ^-S-^ S
// Total Samples: 7 (n+2)
// Radius 4 (Even):
// [-4, -3, -2, -1, 0, +1, +2, +3, +4]
// ^-S-^ ^-S-^ S ^-S-^ ^-S-^
// Total Samples: 5 (n+1)
// Radius 3 (Odd):
// [-3, -2, -1, 0, +1, +2, +3]
// S ^-S-^ S ^-S-^ S
// Total Samples: 5 (n+2)
// Radius 2 (Even):
// [-2, -1, 0, +1, +2]
// ^-S-^ S ^-S-^
// Total Samples: 3 (n+1)
float4 origin = u_image.SampleLevel(pointSampler, vtx.uv, 0);
float4 final = origin * kernel.SampleLevel(pointSampler, (float2(0, u_radius - 1) * kernelTexel), 0).r;
float2 halfTexelDelta = u_texelDelta / 2.0;
for (int k = 1; k < u_radius; k+=2) {
float2 offset = k * u_texelDelta + halfTexelDelta;
float l_g0 = kernel.SampleLevel(pointSampler, (float2(k, u_radius - 1) * kernelTexel), 0).r;
float l_g1 = kernel.SampleLevel(pointSampler, (float2(k + 1, u_radius - 1) * kernelTexel), 0).r;
float4 l_p = u_image.SampleLevel(linearSampler, vtx.uv + offset, 0);
float4 l_n = u_image.SampleLevel(linearSampler, vtx.uv - offset, 0);
final += (l_p + l_n) * l_g0;
final += (l_p + l_n) * l_g1;
}
if (u_radius % 2 == 1) {
// Odd numbers require treatment of ends.
float4 left = u_image.SampleLevel(pointSampler, vtx.uv + u_texelDelta * u_radius, 0);
float4 right = u_image.SampleLevel(pointSampler, vtx.uv - u_texelDelta * u_radius, 0);
float krn = kernel.SampleLevel(pointSampler, (float2(u_radius, u_radius - 1) * kernelTexel), 0).r;
final += (left + right) * krn;
}
return final;
}
technique GaussianLinear
{
pass
{
vertex_shader = VSDefault(vtx);
pixel_shader = PSGaussianLinearBlur(vtx);
}
}
/// Blur: Bilateral
float Bilateral(float x, float sigma) {
return 0.39894 * exp(-0.5 * (x*x) / (sigma*sigma)) / sigma;

View file

@ -28,10 +28,11 @@ CustomShader.Texture.Type.Source="Source"
# Filter - Blur
Filter.Blur="Blur"
Filter.Blur.Type="Type"
Filter.Blur.Type.Description="The type of blur to apply:\n- 'Box' smoothes pixels equally.\n- 'Box Linear' uses linear sampling to reduce the GPU usage, sacrificing minimal quality.\n- 'Gaussian' applies a gaussian curve to the smoothed pixels.\n- 'Bilateral' is an edge detection variant of 'Gaussian'."
Filter.Blur.Type.Description="The type of blur to apply:\n- 'Box' smoothes pixels equally.\n- 'Box Linear' uses linear sampling to reduce the GPU usage, sacrificing minimal quality.\n- 'Gaussian' applies a gaussian curve to the smoothed pixels.\n- 'Gaussian Linear' again uses linear sampling to reduce the total samples, sacrificing more quality this time.\n- 'Bilateral' is an edge detection variant of 'Gaussian'."
Filter.Blur.Type.Box="Box"
Filter.Blur.Type.BoxLinear="Box Linear"
Filter.Blur.Type.Gaussian="Gaussian"
Filter.Blur.Type.GaussianLinear="Gaussian Linear"
Filter.Blur.Type.Bilateral="Bilateral"
Filter.Blur.Size="Size (Pixel)"
Filter.Blur.Size.Description="Area size of the blur, large sizes may cause:\n- Skipped frames\n- Frame loss or drops\n- Input lag\n- GPU overheating\n- or other issues."

View file

@ -41,6 +41,7 @@ extern "C" {
#define P_TYPE_BOX "Filter.Blur.Type.Box"
#define P_TYPE_BOXLINEAR "Filter.Blur.Type.BoxLinear"
#define P_TYPE_GAUSSIAN "Filter.Blur.Type.Gaussian"
#define P_TYPE_GAUSSIANLINEAR "Filter.Blur.Type.GaussianLinear"
#define P_TYPE_BILATERAL "Filter.Blur.Type.Bilateral"
#define P_SIZE "Filter.Blur.Size"
#define P_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing"
@ -125,8 +126,6 @@ bool filter::blur::blur_instance::apply_gaussian_param()
if (blur_effect->has_parameter("kernel")) {
blur_effect->get_parameter("kernel").set_texture(kernel);
} else {
return false;
}
if (blur_effect->has_parameter("kernelTexel")) {
@ -300,6 +299,7 @@ obs_properties_t* filter::blur::blur_instance::get_properties()
obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BOX), filter::blur::type::Box);
obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BOXLINEAR), filter::blur::type::BoxLinear);
obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_GAUSSIAN), filter::blur::type::Gaussian);
obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_GAUSSIANLINEAR), filter::blur::type::GaussianLinear);
obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BILATERAL), filter::blur::type::Bilateral);
p = obs_properties_add_int_slider(pr, P_SIZE, P_TRANSLATE(P_SIZE), 1, 25, 1);
@ -630,14 +630,8 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
if (!apply_shared_param(intermediate, xpel, ypel))
break;
switch (type) {
case Gaussian:
apply_gaussian_param();
break;
case Bilateral:
apply_bilateral_param();
break;
}
gs_texrender_reset(rt);
if (!gs_texrender_begin(rt, baseW, baseH)) {
@ -1025,12 +1019,14 @@ std::string filter::blur::blur_factory::get_technique(filter::blur::type type)
switch (type) {
case type::Box:
return "Box";
case type::BoxLinear:
return "BoxLinear";
case type::Gaussian:
return "Gaussian";
case type::Bilateral:
return "Bilateral";
case type::BoxLinear:
return "BoxLinear";
case type::GaussianLinear:
return "GaussianLinear";
}
return "";
}

View file

@ -42,9 +42,10 @@ namespace filter {
namespace blur {
enum type : int64_t {
Box,
BoxLinear,
Gaussian,
Bilateral,
BoxLinear,
GaussianLinear,
};
enum mask_type : int64_t {