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.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
//using ImageMagick;
|
using SDFMapCreator;
|
||||||
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
|
|
||||||
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Program {
|
public class Program {
|
||||||
private const float MAX = 65535f;
|
private const float MAX = 65535f;
|
||||||
@@ -132,14 +15,13 @@ public class Program {
|
|||||||
static List<MaskData> ImageMasks = new();
|
static List<MaskData> ImageMasks = new();
|
||||||
static List<TransitionMaskData> TransitionMasks = new();
|
static List<TransitionMaskData> TransitionMasks = new();
|
||||||
static List<SDFData> SDFs = 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);
|
static void ConsoleUpdateLine(string s) => Console.Write("\r" + s);
|
||||||
|
|
||||||
public static void Main(string[] args) {
|
public static void Main(string[] args) {
|
||||||
Console.WriteLine("Reading images...");
|
Console.WriteLine("Reading images...");
|
||||||
//foreach image in arguments load the image
|
|
||||||
|
|
||||||
var imagesPath = "images";
|
var imagesPath = "images";
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
@@ -191,7 +73,7 @@ public class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate final image
|
// generate final image
|
||||||
var finalImage = new float3[width, height];
|
var finalImage = new Vector3[width, height];
|
||||||
var currStep = 0f;
|
var currStep = 0f;
|
||||||
var stepIncrement = MAX / (Gradients.Count + 1);
|
var stepIncrement = MAX / (Gradients.Count + 1);
|
||||||
for (var i = 0; i < Gradients.Count; i++) {
|
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");
|
$"\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 width = (uint)mask.ImageA.Width;
|
||||||
uint height = (uint)mask.ImageA.Height;
|
uint height = (uint)mask.ImageA.Height;
|
||||||
int iterCount = 0;
|
int iterCount = 0;
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
float3[,] temp = new float3[width, height];
|
Vector3[,] temp = new Vector3[width, height];
|
||||||
|
|
||||||
var min = MAX;
|
var min = MAX;
|
||||||
var max = MIN;
|
var max = MIN;
|
||||||
@@ -275,7 +157,7 @@ public class Program {
|
|||||||
static SDFData SDF(MaskData mask) {
|
static SDFData SDF(MaskData mask) {
|
||||||
var width = (uint)mask.Mask.GetLength(0);
|
var width = (uint)mask.Mask.GetLength(0);
|
||||||
var height = (uint)mask.Mask.GetLength(1);
|
var height = (uint)mask.Mask.GetLength(1);
|
||||||
var temp = new float3[width, height];
|
var temp = new Vector3[width, height];
|
||||||
float AbsMax = MIN;
|
float AbsMax = MIN;
|
||||||
int iterCount = 0;
|
int iterCount = 0;
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
@@ -285,13 +167,13 @@ public class Program {
|
|||||||
//convert 1D index to 2D index
|
//convert 1D index to 2D index
|
||||||
var x = (int)(i % width);
|
var x = (int)(i % width);
|
||||||
var y = (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
|
float minDist = MAX; //initialize the minimum distance to the maximum possible value
|
||||||
|
|
||||||
//loop through all the pixels in the mask
|
//loop through all the pixels in the mask
|
||||||
foreach (var edge in mask.Edges) {
|
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;
|
if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,13 +205,12 @@ public class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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));
|
MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2));
|
||||||
|
|
||||||
|
|
||||||
|
static Vector3[,] GetABMask(Vector3[,] A, Vector3[,] B, uint resX, uint resY) {
|
||||||
static float3[,] GetABMask(float3[,] A, float3[,] B, uint resX, uint resY) {
|
var temp = new Vector3[resX, resY];
|
||||||
var temp = new float3[resX, resY];
|
|
||||||
Parallel.For(0, resX * resY, parallelOptions, (i) =>
|
Parallel.For(0, resX * resY, parallelOptions, (i) =>
|
||||||
{
|
{
|
||||||
uint x = (uint)(i % resX);
|
uint x = (uint)(i % resX);
|
||||||
@@ -344,8 +225,8 @@ public class Program {
|
|||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float3[,] SelfMask(float3[,] A, uint resX, uint resY) {
|
static Vector3[,] SelfMask(Vector3[,] A, uint resX, uint resY) {
|
||||||
var temp = new float3[resX, resY];
|
var temp = new Vector3[resX, resY];
|
||||||
Parallel.For(0, resX * resY, parallelOptions, (i) =>
|
Parallel.For(0, resX * resY, parallelOptions, (i) =>
|
||||||
{
|
{
|
||||||
uint x = (uint)(i % resX);
|
uint x = (uint)(i % resX);
|
||||||
@@ -358,7 +239,7 @@ public class Program {
|
|||||||
return temp;
|
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 we are already empty, return false
|
||||||
if (mask[x, y].X == 0) return false;
|
if (mask[x, y].X == 0) return false;
|
||||||
//if we are on the edge of the image, 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