From 78efd4fcc20b431452d61ebd4fc67373f9fb8397 Mon Sep 17 00:00:00 2001 From: Samuele Lorefice Date: Sat, 29 Mar 2025 00:42:21 +0100 Subject: [PATCH] Removed imageMagick --- SDFMapCreator.sln.DotSettings.user | 3 + SDFMapCreator/Program.cs | 255 +++++++++++------------------ SDFMapCreator/SDFMapCreator.csproj | 2 + 3 files changed, 100 insertions(+), 160 deletions(-) diff --git a/SDFMapCreator.sln.DotSettings.user b/SDFMapCreator.sln.DotSettings.user index 085f3a7..da277de 100644 --- a/SDFMapCreator.sln.DotSettings.user +++ b/SDFMapCreator.sln.DotSettings.user @@ -1,4 +1,7 @@  + ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded <AssemblyExplorer> <Assembly Path="/mnt/nvme2/Railgun/SDFMapCreator/SDFMapCreator/bin/Debug/net8.0/Magick.NET-Q16-HDRI-OpenMP-x64.dll" /> diff --git a/SDFMapCreator/Program.cs b/SDFMapCreator/Program.cs index 70a9fef..bc855e7 100644 --- a/SDFMapCreator/Program.cs +++ b/SDFMapCreator/Program.cs @@ -1,33 +1,17 @@ using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; -using ImageMagick; +//using ImageMagick; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; 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; -} +public record Image(float3[,] Pixels, int Width, int Height); -public struct float3(float r, float g, float b) { - public float r = r, g = g, b = b; - public float3(float value) : this(value, value, value) {} - public float3(float r, float g) : this(r, g, 0f) {} -} +public record MaskData(float3[,] Mask, Image Image, List Edges); -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 Image, List Edges); - -public record TransitionMaskData(float3[,] Mask, ImageData ImageA, ImageData ImageB); +public record TransitionMaskData(float3[,] Mask, Image ImageA, Image ImageB); public record SDFData(float3[,] SDF); @@ -57,83 +41,82 @@ public static class ArrayExt { public static class ImageUtil { public static T[,] LoadImage(string path) where T : struct, IEquatable { - var image = new MagickImage(path); - var pixels = image.GetPixels().ToArray(); - uint width = image.Width; - uint height = image.Height; - if (pixels == null) throw new ("Failed to read image."); - - T[,] result = new T[image.Width, image.Height]; - uint channels = image.ChannelCount; - - if(channels > 4) throw new NotSupportedException($"Image has {channels} channels, only 1-4 channels are supported."); - - switch (result) { - case float[,] f: - //remainder of index / channels must be 0 - var data = pixels.Where((_, i) => i % channels != 0).ToArray(); - return LoadFloat(data, width, height) as T[,] ?? throw new InvalidOperationException(); - case Vector2[,]: - //can't read 1 channel images - if (channels < 2) throw new NotSupportedException($"Image has {channels} channels, only 2+ channels are supported."); - //remainder of index / channels must be 0 or 1 (2 values) - var data2 = pixels.Where((_, i) => i % channels >= 1).ToArray(); - return LoadVec2(data2, width, height) as T[,] ?? throw new InvalidOperationException(); - case Vector3[,]: - //can't read <2 channel images - if(channels<3) throw new NotSupportedException($"Image has {channels} channels, only 3+ channels are supported."); - //remainder of index / channels must be 0, 1 or 2 (3 values) - var data3 = pixels.Where((_, i) => i % channels >= 2).ToArray(); - return LoadVec3(data3, width, height) as T[,] ?? throw new InvalidOperationException(); - case Vector4[,]v4: - //can't read <3 channel images - if(channels<4) throw new NotSupportedException($"Image has {channels} channels, only 4+ channels are supported."); - //remainder of index / channels must be between 0 or 3 (4 values) - var data4 = pixels.Where((_, i) => i % channels >= 3).ToArray(); - return LoadVec4(data4, width, height) as T[,] ?? throw new InvalidOperationException(); - default: - throw new NotSupportedException($"Type {typeof(T)} is not supported."); - } - } - - private static float[,] LoadFloat(float[] array, uint width, uint height) { - float[,] result = new float[width, height]; - for (int i = 0; i < width * height; i++) { - uint x = (uint)(i % width); - uint y = (uint)(i / width); - result[x, y] = array[i]; - } + var image = SixLabors.ImageSharp.Image.Load(path); + using var image16 = image as Image ?? throw new NotSupportedException($"Image format not supported"); + int width = image.Width; + int height = image.Height; + var result = new T[image.Width, image.Height]; + image16.ProcessPixelRows(accessor => { + //we use Y as the row index and X as the column index + for (int y = 0; y < height; y++) { + var span = accessor.GetRowSpan(y); + for (int x = 0; x < width; x++) { + switch (result) { + case float[,] f: + f[x, y] = span[x].R; + break; + case Vector2[,] f: + f[x,y] = new (span[x].R, span[x].G); + break; + case Vector3[,] f: + f[x,y] = new (span[x].R, span[x].G, span[x].B); + break; + case Vector4[,] f: + f[x,y] = new (span[x].R, span[x].G, span[x].B, 1f); + break; + } + } + } + }); + ImageData(image, path); return result; } - private static Vector2[,] LoadVec2(float[] array, uint width, uint height) { - Vector2[,] result = new Vector2[width, height]; - for (int i = 0; i < width * height; i++) { - uint x = (uint)(i % width); - uint y = (uint)(i / width); - result[x, y] = new(array[i * 2], array[i * 2 + 1]); - } - return result; + public static void SaveImage(this T[,] array, string path) where T : struct, IEquatable { + var width = array.GetLength(0); + var height = array.GetLength(1); + uint channels = array switch { + float[,] => 1, + Vector2[,] => 2, + Vector3[,] => 3, + Vector4[,] => 4, + _ => throw new NotSupportedException($"Type {typeof(T)} is not supported.") + }; + Console.Write($"Writing image {path}..."); + using Image image = new(width, height); + image.ProcessPixelRows(accessor => { + for (int y = 0; y < height; y++) { + var span = accessor.GetRowSpan(y); + for (int x = 0; x < width; x++) { + switch (array) { + case float[,] f: + span[x] = new Rgb48((ushort)f[x, y], (ushort)f[x, y], (ushort)f[x,y]); + break; + case Vector2[,] f: + span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, 0); + break; + case Vector3[,] f: + span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); + break; + case Vector4[,] f: + span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); + break; + } + } + } + }); + Console.WriteLine($"Done!"); + image.Save(path); } - private static Vector3[,] LoadVec3(float[] array, uint width, uint height) { - Vector3[,] result = new Vector3[width, height]; - for (int i = 0; i < width * height; i++) { - uint x = (uint)(i % width); - uint y = (uint)(i / width); - result[x, y] = new(array[i * 3], array[i * 3 + 1], array[i * 3 + 2]); - } - return result; - } - - private static Vector4[,] LoadVec4(float[] array, uint width, uint height) { - Vector4[,] result = new Vector4[width, height]; - for (int i = 0; i < width * height; i++) { - uint x = (uint)(i % width); - uint y = (uint)(i / width); - result[x, y] = new(array[i * 4], array[i * 4 + 1], array[i * 4 + 2], array[i * 4 + 3]); - } - return result; + private static void ImageData(SixLabors.ImageSharp.Image image1, string path) { + Console.WriteLine( + $""" + Image file: {path} + Resolution: {image1.Width}x{image1.Height} + Total Pixels: {image1.Width*image1.Height} |{"NaN"} channels, {image1.PixelType.BitsPerPixel/4} bits per channel + + """); } } @@ -145,59 +128,37 @@ public class Program { private const bool outputSDFs = true; private const bool outputGradients = true; static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = MAX_THREADS }; - static List Images = new(); + static List Images = new(); static List ImageMasks = new(); static List TransitionMasks = new(); static List SDFs = new(); static List Gradients = new(); static void ConsoleUpdateLine(string s) => Console.Write("\r" + s); - - static void LoadImage(string imgPath) { - var image = new MagickImage(imgPath); - float3[,] pixels; - if (image.Channels.Count() == 4) - //skip ever 4th value if we have an alpha channel - pixels = image.GetPixels().ToArray()! - .Where((_, i) => (i + 1) % 4 != 0).ToArray() - .To2DFloat3(image.Width, image.Height); - else - pixels = image.GetPixels().ToArray()! - .To2DFloat3(image.Width, image.Height); - Images.Add(new(image, pixels, new())); - Console.WriteLine($"Loaded image: {imgPath}"); - ImageData(image, image.GetPixels()); - } - + public static void Main(string[] args) { Console.WriteLine("Reading images..."); //foreach image in arguments load the image var imagesPath = "images"; + for (int i = 0; i < 8; i++) { + var pixels = ImageUtil.LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i + 1:00}.png"); + Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1))); + } - for (int i = 0; i < 8; i++) - LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i + 1:00}.png"); - - - //LoadImage("spherecut.png"); - //LoadImage("spherefull.png"); - - //LoadImage("1.png"); - //LoadImage("2.png"); //check if all the images in Images are the same resolution - if (Images.Select(img => (img.Image.Width, img.Image.Height)).Distinct().Count() > 1) { + if (Images.Select(img => (img.Width, img.Height)).Distinct().Count() > 1) { Console.WriteLine("Error: Not all images have the same resolution."); Environment.Exit(1); } Console.WriteLine("Creating masks..."); //for each image pair, create a mask - var width = Images[0].Image.Width; - var height = Images[0].Image.Height; + var width = (uint)Images[0].Width; + var height = (uint)Images[0].Height; for (int i = 0; i < Images.Count; i++) { ImageMasks.Add(new(SelfMask(Images[i].Pixels, width, height), Images[i], new())); - if (i < Images.Count - 1) { Console.WriteLine($"Creating mask {i}..."); var mask = GetABMask(Images[i].Pixels, Images[i + 1].Pixels, width, height); @@ -207,28 +168,18 @@ public class Program { Console.WriteLine("Edge detecting masks..."); //EdgeDetect all masks - foreach (var t in ImageMasks) { - EdgeDetect(t); - } + foreach (var t in ImageMasks) EdgeDetect(t); if (outputMasks) { Console.WriteLine("Writing masks..."); - for (int i = 0; i < TransitionMasks.Count; i++) { - var mask = new MagickImage(MagickColors.Black, (uint)TransitionMasks[i].Mask.GetLength(0), - (uint)TransitionMasks[i].Mask.GetLength(1)); - mask.GetPixels().SetPixels(TransitionMasks[i].Mask.ToFloatArray()); - mask.Write($"mask{i}.png", MagickFormat.Png24); - } + for (int i = 0; i < TransitionMasks.Count; i++) ImageMasks[i].Mask.SaveImage($"mask{i}.png"); } Console.WriteLine("Creating SDFs..."); for (var i = 0; i < ImageMasks.Count; i++) { var mask = ImageMasks[i]; SDFs.Add(SDF(mask)); - if (!outputSDFs) continue; - var sdf = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1)); - sdf.GetPixels().SetPixels(SDFs[i].SDF.ToFloatArray()); - sdf.Write($"sdf{i}.png", MagickFormat.Png48); + if (outputSDFs) mask.Mask.SaveImage($"sdf{i}.png"); } Console.WriteLine("Creating gradients..."); @@ -236,11 +187,7 @@ public class Program { Console.WriteLine($"Generating gradient {i}..."); var gradientData = Gradient(TransitionMasks[i], SDFs[i], SDFs[i + 1]); Gradients.Add(gradientData); - if (!outputGradients) continue; - var gradient = new MagickImage(MagickColors.Black, (uint)TransitionMasks[i].Mask.GetLength(0), - (uint)TransitionMasks[i].Mask.GetLength(1)); - gradient.GetPixels().SetPixels(gradientData.ToFloatArray()); - gradient.Write($"gradient{i}.png", MagickFormat.Png24); + if (outputGradients) gradientData.SaveImage($"gradient{i}.png"); } // generate final image @@ -258,17 +205,13 @@ public class Program { } currStep += stepIncrement; } - - var finalImageMagick = new MagickImage(MagickColors.Black, (uint)width, (uint)height); - finalImageMagick.GetPixels().SetPixels(finalImage.ToFloatArray()); - finalImageMagick.Write("final.png", MagickFormat.Png24); - + finalImage.SaveImage("final.png"); Console.WriteLine("Done!"); } private static void EdgeDetect(MaskData maskData) { - uint width = maskData.Image.Image.Width; - uint height = maskData.Image.Image.Height; + uint width = (uint)maskData.Image.Width; + uint height = (uint)maskData.Image.Height; int iterCount = 0; var sw = new Stopwatch(); sw.Start(); @@ -295,8 +238,8 @@ public class Program { } static float3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) { - uint width = mask.ImageA.Image.Width; - uint height = mask.ImageA.Image.Height; + uint width = (uint)mask.ImageA.Width; + uint height = (uint)mask.ImageA.Height; int iterCount = 0; var sw = new Stopwatch(); sw.Start(); @@ -383,15 +326,7 @@ public class Program { private static float EuclideanDistance(float2 a, float2 b) => MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2)); - private static void ImageData(MagickImage image1, IPixelCollection pixels1) { - Console.WriteLine( - $""" - Image file: {image1.Format.ToString()} - Resolution: {image1.Width}x{image1.Height} - Total Pixels: {pixels1.Count()} |{pixels1.Channels} channels, {image1.Depth} bits per channel - """); - } static float3[,] GetABMask(float3[,] A, float3[,] B, uint resX, uint resY) { var temp = new float3[resX, resY]; diff --git a/SDFMapCreator/SDFMapCreator.csproj b/SDFMapCreator/SDFMapCreator.csproj index 63e0f22..45218a6 100644 --- a/SDFMapCreator/SDFMapCreator.csproj +++ b/SDFMapCreator/SDFMapCreator.csproj @@ -9,6 +9,8 @@ + +