diff --git a/SDFMapCreator/Program.cs b/SDFMapCreator/Program.cs index 07a2374..c119c41 100644 --- a/SDFMapCreator/Program.cs +++ b/SDFMapCreator/Program.cs @@ -2,7 +2,11 @@ using System.Numerics; using System.Runtime.CompilerServices; using ImageMagick; +using float2 = System.Numerics.Vector2; +using float3 = System.Numerics.Vector3; +using float4 = System.Numerics.Vector4; +/* public struct float2(float x, float y) { public float x = x, y = y; } @@ -17,7 +21,7 @@ public struct float4(float r, float g, float b, float a) { public float r = r, g = g, b = b, a = a; public float4(float value) : this(value, value, value, value) {} public float4(float r, float g, float b) : this(r, g, b, 1f) {} -} +}*/ public record ImageData(MagickImage Image, float3[,] Pixels, List Edges); public record MaskData(float3[,] Mask, ImageData A, List Edges); @@ -39,23 +43,23 @@ public static class ArrayExt { float[] result = new float[array.GetLength(0) * array.GetLength(1) * 3]; for(int x = 0; x < array.GetLength(0); x++) { for(int y = 0; y < array.GetLength(1); y++) { - result[x*array.GetLength(1)*3 + y*3] = array[x, y].r; - result[x*array.GetLength(1)*3 + y*3+1] = array[x, y].g; - result[x*array.GetLength(1)*3 + y*3+2] = array[x, y].b; + result[x*array.GetLength(1)*3 + y*3] = array[x, y].X; + result[x*array.GetLength(1)*3 + y*3+1] = array[x, y].Y; + result[x*array.GetLength(1)*3 + y*3+2] = array[x, y].Z; } } return result; } } - - -public class Program { +public class Program { private const float MAX = 65535f; private const float MIN = 0f; - private const bool outputMasks = false; - private const bool outputSDFs = false; - private const bool outputGradients = false; + private static readonly int MAX_THREADS = Environment.ProcessorCount - 2; + private const bool outputMasks = true; + private const bool outputSDFs = true; + private const bool outputGradients = true; + static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = MAX_THREADS }; static List Images = new(); static List Masks = new(); static List SDFs = new(); @@ -85,14 +89,10 @@ public class Program { Console.WriteLine("Reading images..."); //foreach image in arguments load the image - LoadImage("01.png"); - LoadImage("02.png"); - LoadImage("03.png"); - LoadImage("04.png"); - LoadImage("05.png"); - LoadImage("06.png"); - LoadImage("07.png"); - LoadImage("08.png"); + var imagesPath = "images"; + + for (int i = 0; i < 8; i++) + LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i+1:00}.png"); //LoadImage("1.png"); //LoadImage("2.png"); @@ -155,8 +155,8 @@ public class Program { var transition = new float3[width, height]; for (var x = 0; x < mask.Mask.GetLength(0); x++) { for (var y = 0; y < mask.Mask.GetLength(1); y++) { - if (mask.Mask[x, y].r == 0) continue; - transition[x, y] = new(Lerp(Images[i].Pixels[x,y].r, MAX, gradient[x,y].r)); + if (mask.Mask[x, y].X == 0) continue; + transition[x, y] = new(Lerp(Images[i].Pixels[x,y].X, MAX, gradient[x,y].X)); } } var transitionImage = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1)); @@ -174,13 +174,13 @@ public class Program { var sw = new Stopwatch(); sw.Start(); Console.WriteLine("Running edge detection..."); - Parallel.For(0, width * height, (i) => { + Parallel.For(0, width * height, parallelOptions, (i) => { int x = (int)(i % width); int y = (int)(i / width); if (!EdgeKernel(maskData.Mask, x, y, width, height)) return; var color = maskData.Mask[x, y]; - color.g = MAX; + color.Y = MAX; maskData.Mask[x, y] = color; lock(maskData.Edges) maskData.Edges.Add(new(x, y)); iterCount++; @@ -200,20 +200,20 @@ public class Program { sw.Start(); float3[,] temp = new float3[width, height]; Console.WriteLine("Running edge detection..."); - Parallel.For(0, width * height, (i) => { + Parallel.For(0, width * height, parallelOptions, (i) => { int x = (int)(i % width); int y = (int)(i / width); - var a = (sdfA.SDF[x,y].r + sdfB.SDF[x,y].g + sdfB.SDF[x,y].b )/ 3; - var b = (sdfB.SDF[x,y].r + sdfB.SDF[x,y].g + sdfB.SDF[x,y].b )/ 3; + var a = (sdfA.SDF[x,y].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3; + var b = (sdfB.SDF[x,y].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3; var gradient = a / (a + b); temp[x, y] = new(Remap(gradient, 0, 1, MIN, MAX)); }); - Console.WriteLine($"Gradient Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/sw.Elapsed.TotalSeconds:N0} pixels/s)"); - var min = temp.Cast().Min(x => x.r); - var max = temp.Cast().Max(x => x.r); + Console.WriteLine($"Gradient Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/(float)sw.Elapsed.TotalSeconds:N0} pixels/s)"); + var min = temp.Cast().Min(x => x.X); + var max = temp.Cast().Max(x => x.X); Console.WriteLine($"Min: {min} | Max: {max}"); return temp; } @@ -226,7 +226,7 @@ public class Program { int iterCount = 0; var sw = new Stopwatch(); sw.Start(); - Parallel.For(0, width * height, (i) => { + Parallel.For(0, width * height, parallelOptions, (i) => { //convert 1D index to 2D index var x = (int)(i % width); var y = (int)(i / width); @@ -236,7 +236,7 @@ public class Program { //loop through all the pixels in the mask foreach (var edge in mask.Edges) { - float dist = EuclideanDistance(p, edge); + float dist = float2.DistanceSquared(p, edge); if (dist < minDist) minDist = dist; } @@ -250,11 +250,11 @@ public class Program { Console.WriteLine($"SDF Generation Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/sw.Elapsed.TotalSeconds:N0} pixels/s)"); sw.Restart(); - Parallel.For(0, width * height, (i) => { + Parallel.For(0, width * height, parallelOptions, (i) => { //convert 1D index to 2D index var x = (int)(i % width); var y = (int)(i / width); - temp[x, y] = new(Remap(temp[x, y].r, 0, AbsMax, MIN, MAX)); + temp[x, y] = new(Remap(temp[x, y].X, 0, AbsMax, MIN, MAX)); }); Console.WriteLine($"SDF Normalization Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/sw.Elapsed.TotalSeconds:N0} pixels/s)"); Console.WriteLine("AbsMax: " + AbsMax); @@ -263,7 +263,7 @@ public class Program { [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float EuclideanDistance(float2 a, float2 b) => - MathF.Sqrt(MathF.Pow(a.x - b.x, 2) + MathF.Pow(a.y - b.y, 2)); + MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2)); private static void ImageData(MagickImage image1, IPixelCollection pixels1) { @@ -278,13 +278,13 @@ public class Program { static float3[,] GetABMask(float3[,] A, float3[,] B, uint resX, uint resY) { var temp = new float3[resX, resY]; - Parallel.For(0, resX*resY, (i) => { + Parallel.For(0, resX*resY, parallelOptions, (i) => { uint x = (uint)(i % resX); uint y = (uint)(i / resX); var pixelA = A[x, y]; var pixelB = B[x, y]; - float lumaA = (pixelA.r+pixelA.g+pixelA.b)/3; - float lumaB = (pixelB.r+pixelB.g+pixelB.b)/3; + float lumaA = (pixelA.X+pixelA.Y+pixelA.Z)/3; + float lumaB = (pixelB.X+pixelB.Y+pixelB.Z)/3; float resultPixel = lumaB > lumaA ? MAX : MIN; temp[x, y] = new(resultPixel, 0, 0); }); @@ -293,11 +293,11 @@ public class Program { static float3[,] SelfMask(float3[,] A, uint resX, uint resY) { var temp = new float3[resX, resY]; - Parallel.For(0, resX*resY, (i) => { + Parallel.For(0, resX*resY, parallelOptions, (i) => { uint x = (uint)(i % resX); uint y = (uint)(i / resX); var pixelA = A[x, y]; - float lumaA = (pixelA.r+pixelA.g+pixelA.b)/3; + float lumaA = (pixelA.X+pixelA.Y+pixelA.Z)/3; float resultPixel = lumaA > 0 ? MAX : MIN; temp[x, y] = new(resultPixel, 0, 0); }); @@ -306,7 +306,7 @@ public class Program { static bool EdgeKernel(float3[,] mask, int x, int y, uint width, uint height) { //if we are already empty, return false - if (mask[x, y].r == 0) return false; + if (mask[x, y].X == 0) return false; //if we are on the edge of the image, return false if (x == 0 || y == 0 || x == width - 1 || y == height - 1) return false; //check the 3x3 kernel @@ -314,7 +314,7 @@ public class Program { for (int yi = y - 1; yi <= y + 1; yi++) { if (xi < 0 || xi >= width || yi < 0 || yi >= height) continue; //skip out of bounds pixels - if (mask[xi, yi].r == 0) + if (mask[xi, yi].X == 0) return true; //if we find a black pixel, return true } } @@ -329,4 +329,4 @@ public class Program { static T Remap(T value, T min, T max, T newMin, T newMax) where T : INumber, ISubtractionOperators, IMultiplyOperators, IAdditionOperators => (value - min) / (max - min) * (newMax - newMin) + newMin; -} \ No newline at end of file +} diff --git a/SDFMapCreator/SDFMapCreator.csproj b/SDFMapCreator/SDFMapCreator.csproj index 2307201..2d047f2 100644 --- a/SDFMapCreator/SDFMapCreator.csproj +++ b/SDFMapCreator/SDFMapCreator.csproj @@ -9,6 +9,7 @@ + @@ -21,6 +22,30 @@ PreserveNewest + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + diff --git a/SDFMapCreator/images/01.png b/SDFMapCreator/images/01.png new file mode 100644 index 0000000..bba6ec4 Binary files /dev/null and b/SDFMapCreator/images/01.png differ diff --git a/SDFMapCreator/images/02.png b/SDFMapCreator/images/02.png new file mode 100644 index 0000000..55ac25f Binary files /dev/null and b/SDFMapCreator/images/02.png differ diff --git a/SDFMapCreator/images/03.png b/SDFMapCreator/images/03.png new file mode 100644 index 0000000..1f19eda Binary files /dev/null and b/SDFMapCreator/images/03.png differ diff --git a/SDFMapCreator/images/04.png b/SDFMapCreator/images/04.png new file mode 100644 index 0000000..3dd2056 Binary files /dev/null and b/SDFMapCreator/images/04.png differ diff --git a/SDFMapCreator/images/05.png b/SDFMapCreator/images/05.png new file mode 100644 index 0000000..b9c4f94 Binary files /dev/null and b/SDFMapCreator/images/05.png differ diff --git a/SDFMapCreator/images/06.png b/SDFMapCreator/images/06.png new file mode 100644 index 0000000..6cc2792 Binary files /dev/null and b/SDFMapCreator/images/06.png differ diff --git a/SDFMapCreator/images/07.png b/SDFMapCreator/images/07.png new file mode 100644 index 0000000..133aaaa Binary files /dev/null and b/SDFMapCreator/images/07.png differ diff --git a/SDFMapCreator/images/08.png b/SDFMapCreator/images/08.png new file mode 100644 index 0000000..35b0502 Binary files /dev/null and b/SDFMapCreator/images/08.png differ