TASK 51 blur 'heavy' fix: quadratic blur radius. BlurRadius(strength) = 1 + 5s^2, MaxBlurRadius 12 -> 6 downscaled px (~24 full px max). Default 0.5 is now radius 2 small px (was the old linear 6) - ~3x lighter perceived spread, matching the creator's real-camera finding. Range from the established tools: StreamFX defaults its Box blur Size to 5 and its author stops downscale-based blurs around 8px. One integration test pins the mapping (0 -> 1, 0.5 -> 2, 1 -> 6) and step-edge behaviour (light reaches 9px at default vs 26px at full); the existing slice-2 blur test passes unchanged. 419/419 pass. Refs: https://github.com/Vhonowslend/StreamFX-Public/wiki/Filter-Blur https://xaymar.com/articles/2020/04/26/optimizing-streamfx-in-your-obs-scenes/ (Unit: bg-blur heavy fix)

This commit is contained in:
2026-10-10 07:08:01 -07:00
parent 305706956e
commit c2d64dcd6d
7 changed files with 124 additions and 44 deletions
@@ -32,9 +32,12 @@ public sealed class BackgroundEffectProcessor
/// upsampled while blending — a strong blur at a fraction of the cost.</summary>
internal const int BlurDownscale = 4;
/// <summary>Box-blur radius (in downscaled pixels) at strength 0 and 1.</summary>
/// <summary>Box-blur radius (in downscaled pixels) at strength 0 and 1. The maximum was halved
/// from 12 (2026-10-09): the creator found full range too heavy — the old max spread ≈48 full
/// px. The established tools blur far less: StreamFX defaults its Box blur size to 5 and its
/// author stops downscale-based blurs around 8 px.</summary>
internal const int MinBlurRadius = 1;
internal const int MaxBlurRadius = 12;
internal const int MaxBlurRadius = 6;
private readonly IPersonSegmenter _segmenter;
private bool _loggedFailure;
@@ -161,7 +164,7 @@ public sealed class BackgroundEffectProcessor
});
// 2. Three separable box-blur passes ≈ a gaussian.
var radius = (int)Math.Round(MinBlurRadius + (MaxBlurRadius - MinBlurRadius) * strength);
var radius = BlurRadius(strength);
var scratch = new float[small.Length];
for (var pass = 0; pass < 3; pass++)
{
@@ -212,6 +215,15 @@ public sealed class BackgroundEffectProcessor
return new VideoFrame(width, height, dst) { Epoch = frame.Epoch, IsOpaque = frame.IsOpaque };
}
/// <summary>Blur radius in downscaled pixels for a strength. Quadratic (s²), not linear, so the
/// default 0.5 stays gentle (2 small px ≈ ~8–10 full px of perceived spread, ~3× lighter than
/// the old linear 6) while only the top of the slider approaches the full range.</summary>
internal static int BlurRadius(double strength)
{
var s = Math.Clamp(strength, 0, 1);
return (int)Math.Round(MinBlurRadius + (MaxBlurRadius - MinBlurRadius) * s * s);
}
/// <summary>Row-parallel pixel loops, capped so the effect worker can't starve the compositor
/// and encoder threads.</summary>
private static readonly ParallelOptions RowParallelism = new()