From 2d1368a908a92d462ee737ca92404d331475b262 Mon Sep 17 00:00:00 2001 From: mm00 Date: Thu, 3 Apr 2025 18:48:21 +0200 Subject: [PATCH] Added directional blur --- SDFMapCreator/Program.cs | 28 +++++++++++++++ SDFMapCreator/SdfKernels.Kernels.cs | 56 +++++++++++++++++++++++++++++ SDFMapCreator/SdfKernels.cs | 30 ++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/SDFMapCreator/Program.cs b/SDFMapCreator/Program.cs index 53b158a..7000caf 100644 --- a/SDFMapCreator/Program.cs +++ b/SDFMapCreator/Program.cs @@ -119,6 +119,18 @@ public class Program { } currStep += stepIncrement; } + + // apply directional blur + var iterations = 10; + var radius = 3f; + var step = .5f; + var sigma = 1f; + for (int i = 0; i < iterations; i++) + { + Console.WriteLine($"Applying directional blur {i + 1}/{iterations}..."); + finalImage = DirectionalBlur(finalImage, ImageMasks[0].Mask, radius, step, sigma); + } + finalImage.SaveImage("final.png"); Console.WriteLine("Done!"); } @@ -219,6 +231,22 @@ public class Program { return new(temp); } + + static Vector3[,] DirectionalBlur(Vector3[,] image, Vector3[,] mask, float radius = 3f, float step = .5f, float sigma = 1f) { + var width = (uint)image.GetLength(0); + var height = (uint)image.GetLength(1); + var output = new Vector3[width, height]; + + var sw = new Stopwatch(); + sw.Start(); + + kernels.DirectionalBlur(image, mask, out var temp, radius, step, sigma); + + Console.WriteLine( + $"Directional Blur Time: {sw.Elapsed.TotalSeconds:N4}s ({width * height / sw.Elapsed.TotalSeconds:N0} pixels/s)"); + + return temp; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float EuclideanDistance(Vector2 a, Vector2 b) => diff --git a/SDFMapCreator/SdfKernels.Kernels.cs b/SDFMapCreator/SdfKernels.Kernels.cs index a10f793..1a54571 100644 --- a/SDFMapCreator/SdfKernels.Kernels.cs +++ b/SDFMapCreator/SdfKernels.Kernels.cs @@ -79,5 +79,61 @@ public partial class SdfKernels { if(mask[index].X == 0f || mask[index].Y == 0f) return; float a = sdfa[index].X; float b = sdfb[index].X; gradient[index] = new (a / (a + b)); + + private static void DirectionalBlurKernel(Index2D index, + ArrayView2D image, + ArrayView2D mask, + ArrayView2D output, + float radius, float step, float sigma, + int width, int height + ) + { + int x = index.X; + int y = index.Y; + Vector3 value = image[x, y]; + Vector3 maskValue = mask[x, y]; + if (maskValue.X == 0f) + { + output[x, y] = value; + return; + } + + var gradient = Vector2.Zero; + + for (int dx = -1; dx <= 1; dx++) + { + for (int dy = -1; dy <= 1; dy++) + { + if (x + dx < 0 || x + dx >= width || y + dy < 0 || y + dy >= height) continue; + gradient += new Vector2(dx, dy) * image[x + dx, y + dy].X; + } + } + + if (gradient == Vector2.Zero) + { + output[x, y] = value; + return; + } + + gradient = Vector2.Normalize(gradient); + float sum = 0; + + // now we follow the direction line and sample the image for length; + for (float l = -radius; l <= radius; l += step) + { + int xOffset = (int)(gradient.X * l); + int yOffset = (int)(gradient.Y * l); + int xSample = x + xOffset; + int ySample = y + yOffset; + + if (xSample < 0 || xSample >= width || ySample < 0 || ySample >= height) continue; + + Vector3 sampleValue = image[xSample, ySample]; + float weight = MathF.Exp(-l * l / (2f * sigma * sigma)); + output[x, y] += sampleValue * weight; + sum += weight; + } + + output[x, y] /= sum; } } diff --git a/SDFMapCreator/SdfKernels.cs b/SDFMapCreator/SdfKernels.cs index 21dd878..5cea7c4 100644 --- a/SDFMapCreator/SdfKernels.cs +++ b/SDFMapCreator/SdfKernels.cs @@ -5,6 +5,7 @@ using ILGPU.Runtime; using ILGPU.Runtime.CPU; using ILGPU.Runtime.Cuda; using ILGPU.Runtime.OpenCL; +using ILGPU.Algorithms; namespace SDFMapCreator; @@ -18,6 +19,7 @@ public partial class SdfKernels { .Cuda() .CPU() .Math(MathMode.Fast32BitOnly) + .EnableAlgorithms() ); Console.WriteLine("Reading available accelerators (CUDA only)..."); @@ -157,6 +159,34 @@ public partial class SdfKernels { accelerator.Synchronize(); gradient = bufferMask.GetAsArray2D(); + + public void DirectionalBlur(Vector3[,] image, Vector3[,] mask, out Vector3[,] output, float radius = 3f, float step = .5f, float sigma = 1f) { + var dev = gpuContext.GetPreferredDevice(preferCPU:false); + int width = image.GetLength(0); + int height = image.GetLength(1); + output = new Vector3[width, height]; + using Accelerator accelerator = dev.CreateAccelerator(gpuContext); + + using var imageBuffer = accelerator.Allocate2DDenseX(new (width, height)); + imageBuffer.CopyFromCPU(image); + + using var maskBuffer = accelerator.Allocate2DDenseX(new (width, height)); + maskBuffer.CopyFromCPU(mask); + + using var outputBuffer = accelerator.Allocate2DDenseX(new (width, height)); + + var blurKernel = accelerator.LoadAutoGroupedStreamKernel, + ArrayView2D, + ArrayView2D, + float, float, float, int, int>(DirectionalBlurKernel); + + blurKernel(new (width, height), imageBuffer.View, maskBuffer.View, outputBuffer.View, + radius, step, sigma, width, height); + + accelerator.Synchronize(); + + output = outputBuffer.GetAsArray2D(); } private static string GetInfoString(Accelerator a)