Giga Refactor
This commit is contained in:
86
SDFMapCreator/ImageUtil.cs
Normal file
86
SDFMapCreator/ImageUtil.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Numerics;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace SDFMapCreator;
|
||||
|
||||
public static class ImageUtil {
|
||||
public static T[,] LoadImage<T>(string path) where T : struct, IEquatable<T> {
|
||||
var image = SixLabors.ImageSharp.Image.Load(path);
|
||||
using var image16 = image as Image<Rgba64> ?? 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;
|
||||
}
|
||||
|
||||
public static void SaveImage<T>(this T[,] array, string path) where T : struct, IEquatable<T> {
|
||||
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<Rgb48> 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 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
|
||||
|
||||
""");
|
||||
}
|
||||
}
|
||||
@@ -1,124 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
//using ImageMagick;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using float2 = System.Numerics.Vector2;
|
||||
using float3 = System.Numerics.Vector3;
|
||||
|
||||
public record Image(float3[,] Pixels, int Width, int Height);
|
||||
|
||||
public record MaskData(float3[,] Mask, Image Image, List<float2> Edges);
|
||||
|
||||
public record TransitionMaskData(float3[,] Mask, Image ImageA, Image ImageB);
|
||||
|
||||
public record SDFData(float3[,] SDF);
|
||||
|
||||
public static class ArrayExt {
|
||||
public static float3[,] To2DFloat3(this float[] array, uint width, uint height) {
|
||||
float3[,] result = new float3[width, height];
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
uint x = (uint)(i % width);
|
||||
uint y = (uint)(i / width);
|
||||
result[y, x] = new(array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float[] ToFloatArray(this float3[,] array) {
|
||||
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].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 static class ImageUtil {
|
||||
public static T[,] LoadImage<T>(string path) where T : struct, IEquatable<T> {
|
||||
var image = SixLabors.ImageSharp.Image.Load(path);
|
||||
using var image16 = image as Image<Rgba64> ?? 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;
|
||||
}
|
||||
|
||||
public static void SaveImage<T>(this T[,] array, string path) where T : struct, IEquatable<T> {
|
||||
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<Rgb48> 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 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
|
||||
|
||||
""");
|
||||
}
|
||||
}
|
||||
using SDFMapCreator;
|
||||
|
||||
public class Program {
|
||||
private const float MAX = 65535f;
|
||||
@@ -132,13 +15,12 @@ public class Program {
|
||||
static List<MaskData> ImageMasks = new();
|
||||
static List<TransitionMaskData> TransitionMasks = new();
|
||||
static List<SDFData> SDFs = new();
|
||||
static List<float3[,]> Gradients = new();
|
||||
static List<Vector3[,]> Gradients = new();
|
||||
|
||||
static void ConsoleUpdateLine(string s) => Console.Write("\r" + s);
|
||||
|
||||
public static void Main(string[] args) {
|
||||
Console.WriteLine("Reading images...");
|
||||
//foreach image in arguments load the image
|
||||
|
||||
var imagesPath = "images";
|
||||
|
||||
@@ -191,7 +73,7 @@ public class Program {
|
||||
}
|
||||
|
||||
// generate final image
|
||||
var finalImage = new float3[width, height];
|
||||
var finalImage = new Vector3[width, height];
|
||||
var currStep = 0f;
|
||||
var stepIncrement = MAX / (Gradients.Count + 1);
|
||||
for (var i = 0; i < Gradients.Count; i++) {
|
||||
@@ -237,13 +119,13 @@ public class Program {
|
||||
$"\nEdge pixels: {maskData.Edges.Count} | {maskData.Edges.Count / sw.ElapsedMilliseconds} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s");
|
||||
}
|
||||
|
||||
static float3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
|
||||
static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
|
||||
uint width = (uint)mask.ImageA.Width;
|
||||
uint height = (uint)mask.ImageA.Height;
|
||||
int iterCount = 0;
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
float3[,] temp = new float3[width, height];
|
||||
Vector3[,] temp = new Vector3[width, height];
|
||||
|
||||
var min = MAX;
|
||||
var max = MIN;
|
||||
@@ -275,7 +157,7 @@ public class Program {
|
||||
static SDFData SDF(MaskData mask) {
|
||||
var width = (uint)mask.Mask.GetLength(0);
|
||||
var height = (uint)mask.Mask.GetLength(1);
|
||||
var temp = new float3[width, height];
|
||||
var temp = new Vector3[width, height];
|
||||
float AbsMax = MIN;
|
||||
int iterCount = 0;
|
||||
var sw = new Stopwatch();
|
||||
@@ -285,13 +167,13 @@ public class Program {
|
||||
//convert 1D index to 2D index
|
||||
var x = (int)(i % width);
|
||||
var y = (int)(i / width);
|
||||
float2 p = new(x, y);
|
||||
Vector2 p = new(x, y);
|
||||
|
||||
float minDist = MAX; //initialize the minimum distance to the maximum possible value
|
||||
|
||||
//loop through all the pixels in the mask
|
||||
foreach (var edge in mask.Edges) {
|
||||
float dist = float2.DistanceSquared(p, edge);
|
||||
float dist = Vector2.DistanceSquared(p, edge);
|
||||
if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist;
|
||||
}
|
||||
|
||||
@@ -323,13 +205,12 @@ public class Program {
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float EuclideanDistance(float2 a, float2 b) =>
|
||||
private static float EuclideanDistance(Vector2 a, Vector2 b) =>
|
||||
MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2));
|
||||
|
||||
|
||||
|
||||
static float3[,] GetABMask(float3[,] A, float3[,] B, uint resX, uint resY) {
|
||||
var temp = new float3[resX, resY];
|
||||
static Vector3[,] GetABMask(Vector3[,] A, Vector3[,] B, uint resX, uint resY) {
|
||||
var temp = new Vector3[resX, resY];
|
||||
Parallel.For(0, resX * resY, parallelOptions, (i) =>
|
||||
{
|
||||
uint x = (uint)(i % resX);
|
||||
@@ -344,8 +225,8 @@ public class Program {
|
||||
return temp;
|
||||
}
|
||||
|
||||
static float3[,] SelfMask(float3[,] A, uint resX, uint resY) {
|
||||
var temp = new float3[resX, resY];
|
||||
static Vector3[,] SelfMask(Vector3[,] A, uint resX, uint resY) {
|
||||
var temp = new Vector3[resX, resY];
|
||||
Parallel.For(0, resX * resY, parallelOptions, (i) =>
|
||||
{
|
||||
uint x = (uint)(i % resX);
|
||||
@@ -358,7 +239,7 @@ public class Program {
|
||||
return temp;
|
||||
}
|
||||
|
||||
static bool EdgeKernel(float3[,] mask, int x, int y, uint width, uint height) {
|
||||
static bool EdgeKernel(Vector3[,] mask, int x, int y, uint width, uint height) {
|
||||
//if we are already empty, return false
|
||||
if (mask[x, y].X == 0) return false;
|
||||
//if we are on the edge of the image, return false
|
||||
|
||||
8
SDFMapCreator/Records.cs
Normal file
8
SDFMapCreator/Records.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace SDFMapCreator;
|
||||
|
||||
public record Image(Vector3[,] Pixels, int Width, int Height);
|
||||
public record MaskData(Vector3[,] Mask, Image Image, List<Vector2> Edges);
|
||||
public record SDFData(Vector3[,] SDF);
|
||||
public record TransitionMaskData(Vector3[,] Mask, Image ImageA, Image ImageB);
|
||||
Reference in New Issue
Block a user