completed refactoring, added 32x32 test images
This commit is contained in:
@@ -7,10 +7,6 @@ namespace SDFMapCreator;
|
|||||||
public static class ImageUtil {
|
public static class ImageUtil {
|
||||||
public static T[,] LoadImage<T>(string path) where T : struct, IEquatable<T> {
|
public static T[,] LoadImage<T>(string path) where T : struct, IEquatable<T> {
|
||||||
var image = SixLabors.ImageSharp.Image.Load(path);
|
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;
|
|
||||||
//result = new T[image.Width, image.Height];
|
|
||||||
return image switch {
|
return image switch {
|
||||||
Image<Rgba64> img => img.ProcessPixelsRgba64<T>(),
|
Image<Rgba64> img => img.ProcessPixelsRgba64<T>(),
|
||||||
Image<Rgb24> img => img.ProcessPixelsRgb24<T>(),
|
Image<Rgb24> img => img.ProcessPixelsRgb24<T>(),
|
||||||
@@ -21,6 +17,7 @@ public static class ImageUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static T[,] ProcessPixelsRgba64<T>(this Image<Rgba64> image) where T : struct, IEquatable<T> {
|
static T[,] ProcessPixelsRgba64<T>(this Image<Rgba64> image) where T : struct, IEquatable<T> {
|
||||||
|
var max = 65535f;
|
||||||
int width = image.Width;
|
int width = image.Width;
|
||||||
int height = image.Height;
|
int height = image.Height;
|
||||||
var result = new T[image.Width, image.Height];
|
var result = new T[image.Width, image.Height];
|
||||||
@@ -31,16 +28,16 @@ public static class ImageUtil {
|
|||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case float[,] f:
|
case float[,] f:
|
||||||
f[x, y] = span[x].R;
|
f[x, y] = span[x].R / max;
|
||||||
break;
|
break;
|
||||||
case Vector2[,] f:
|
case Vector2[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G);
|
f[x, y] = new(span[x].R / max, span[x].G / max);
|
||||||
break;
|
break;
|
||||||
case Vector3[,] f:
|
case Vector3[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
|
||||||
break;
|
break;
|
||||||
case Vector4[,] f:
|
case Vector4[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,6 +47,7 @@ public static class ImageUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static T[,] ProcessPixelsRgb24<T>(this Image<Rgb24> image) where T : struct, IEquatable<T> {
|
static T[,] ProcessPixelsRgb24<T>(this Image<Rgb24> image) where T : struct, IEquatable<T> {
|
||||||
|
var max = 255f;
|
||||||
int width = image.Width;
|
int width = image.Width;
|
||||||
int height = image.Height;
|
int height = image.Height;
|
||||||
var result = new T[image.Width, image.Height];
|
var result = new T[image.Width, image.Height];
|
||||||
@@ -60,16 +58,16 @@ public static class ImageUtil {
|
|||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case float[,] f:
|
case float[,] f:
|
||||||
f[x, y] = span[x].R;
|
f[x, y] = span[x].R / max;
|
||||||
break;
|
break;
|
||||||
case Vector2[,] f:
|
case Vector2[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G);
|
f[x, y] = new(span[x].R / max, span[x].G / max);
|
||||||
break;
|
break;
|
||||||
case Vector3[,] f:
|
case Vector3[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
|
||||||
break;
|
break;
|
||||||
case Vector4[,] f:
|
case Vector4[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, 1f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +77,7 @@ public static class ImageUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static T[,] ProcessPixelsRgba32<T>(this Image<Rgba32> image) where T : struct, IEquatable<T> {
|
static T[,] ProcessPixelsRgba32<T>(this Image<Rgba32> image) where T : struct, IEquatable<T> {
|
||||||
|
var max = 255f;
|
||||||
int width = image.Width;
|
int width = image.Width;
|
||||||
int height = image.Height;
|
int height = image.Height;
|
||||||
var result = new T[image.Width, image.Height];
|
var result = new T[image.Width, image.Height];
|
||||||
@@ -89,16 +88,16 @@ public static class ImageUtil {
|
|||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case float[,] f:
|
case float[,] f:
|
||||||
f[x, y] = span[x].R;
|
f[x, y] = span[x].R / max;
|
||||||
break;
|
break;
|
||||||
case Vector2[,] f:
|
case Vector2[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G);
|
f[x, y] = new(span[x].R / max, span[x].G / max);
|
||||||
break;
|
break;
|
||||||
case Vector3[,] f:
|
case Vector3[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
|
||||||
break;
|
break;
|
||||||
case Vector4[,] f:
|
case Vector4[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,27 +107,27 @@ public static class ImageUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static T[,] ProcessPixelsRgb48<T>(this Image<Rgb48> image) where T : struct, IEquatable<T> {
|
static T[,] ProcessPixelsRgb48<T>(this Image<Rgb48> image) where T : struct, IEquatable<T> {
|
||||||
using var image16 = image as Image<Rgb48> ?? throw new NotSupportedException($"Image format not supported");
|
var max = 65535f;
|
||||||
int width = image.Width;
|
int width = image.Width;
|
||||||
int height = image.Height;
|
int height = image.Height;
|
||||||
var result = new T[image.Width, image.Height];
|
var result = new T[image.Width, image.Height];
|
||||||
image16.ProcessPixelRows(accessor => {
|
image.ProcessPixelRows(accessor => {
|
||||||
//we use Y as the row index and X as the column index
|
//we use Y as the row index and X as the column index
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
var span = accessor.GetRowSpan(y);
|
var span = accessor.GetRowSpan(y);
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case float[,] f:
|
case float[,] f:
|
||||||
f[x, y] = span[x].R;
|
f[x, y] = span[x].R / max;
|
||||||
break;
|
break;
|
||||||
case Vector2[,] f:
|
case Vector2[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G);
|
f[x, y] = new(span[x].R / max, span[x].G / max);
|
||||||
break;
|
break;
|
||||||
case Vector3[,] f:
|
case Vector3[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
|
||||||
break;
|
break;
|
||||||
case Vector4[,] f:
|
case Vector4[,] f:
|
||||||
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
|
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, 1f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,16 +154,16 @@ public static class ImageUtil {
|
|||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
switch (array) {
|
switch (array) {
|
||||||
case float[,] f:
|
case float[,] f:
|
||||||
span[x] = new Rgb48((ushort)f[x, y], (ushort)f[x, y], (ushort)f[x,y]);
|
span[x] = new Rgb48((ushort)(f[x, y] * 65535), (ushort)(f[x, y] * 65535), (ushort)(f[x,y] * 65535));
|
||||||
break;
|
break;
|
||||||
case Vector2[,] f:
|
case Vector2[,] f:
|
||||||
span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, 0);
|
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), 0);
|
||||||
break;
|
break;
|
||||||
case Vector3[,] f:
|
case Vector3[,] f:
|
||||||
span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z);
|
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535));
|
||||||
break;
|
break;
|
||||||
case Vector4[,] f:
|
case Vector4[,] f:
|
||||||
span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z);
|
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using SDFMapCreator;
|
using SDFMapCreator;
|
||||||
|
|
||||||
public class Program {
|
public class Program {
|
||||||
private const float MAX = 65535f;
|
private const float MAX = 1f;
|
||||||
private const float MIN = 0f;
|
private const float MIN = 0f;
|
||||||
private static readonly int MAX_THREADS = Environment.ProcessorCount - 2;
|
private static readonly int MAX_THREADS = Environment.ProcessorCount - 2;
|
||||||
private const bool outputMasks = true;
|
private const bool outputMasks = true;
|
||||||
@@ -24,10 +24,23 @@ public class Program {
|
|||||||
|
|
||||||
var imagesPath = "images";
|
var imagesPath = "images";
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
var pixels = ImageUtil.LoadImage<Vector3>($"./{imagesPath}{Path.DirectorySeparatorChar}{i + 1:00}.png");
|
var pixels = ImageUtil.LoadImage<Vector3>($"./{imagesPath}{Path.DirectorySeparatorChar}{i + 1:00}.png");
|
||||||
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
|
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
var pixels = ImageUtil.LoadImage<Vector3>($"./sphereempty.png");
|
||||||
|
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
|
||||||
|
pixels = ImageUtil.LoadImage<Vector3>($"./spherehalf.png");
|
||||||
|
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
|
||||||
|
pixels = ImageUtil.LoadImage<Vector3>($"./spherecut.png");
|
||||||
|
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
|
||||||
|
pixels = ImageUtil.LoadImage<Vector3>($"./spherefull.png");
|
||||||
|
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
//check if all the images in Images are the same resolution
|
//check if all the images in Images are the same resolution
|
||||||
if (Images.Select(img => (img.Width, img.Height)).Distinct().Count() > 1) {
|
if (Images.Select(img => (img.Width, img.Height)).Distinct().Count() > 1) {
|
||||||
@@ -61,7 +74,7 @@ public class Program {
|
|||||||
for (var i = 0; i < ImageMasks.Count; i++) {
|
for (var i = 0; i < ImageMasks.Count; i++) {
|
||||||
var mask = ImageMasks[i];
|
var mask = ImageMasks[i];
|
||||||
SDFs.Add(SDF(mask));
|
SDFs.Add(SDF(mask));
|
||||||
if (outputSDFs) mask.Mask.SaveImage($"sdf{i}.png");
|
if (outputSDFs) SDFs[i].SDF.SaveImage($"sdf{i}.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Creating gradients...");
|
Console.WriteLine("Creating gradients...");
|
||||||
@@ -75,13 +88,14 @@ public class Program {
|
|||||||
// generate final image
|
// generate final image
|
||||||
var finalImage = new Vector3[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);
|
||||||
for (var i = 0; i < Gradients.Count; i++) {
|
for (var i = 0; i < Gradients.Count; i++) {
|
||||||
var mask = ImageMasks[i + 1];
|
var mask = ImageMasks[i + 1];
|
||||||
var gradient = Gradients[i];
|
var gradient = Gradients[i];
|
||||||
|
Console.WriteLine($"Applying gradient {i}..., {currStep} -> {currStep + stepIncrement}");
|
||||||
for (var x = 0; x < mask.Mask.GetLength(0); x++) {
|
for (var x = 0; x < mask.Mask.GetLength(0); x++) {
|
||||||
for (var y = 0; y < mask.Mask.GetLength(1); y++) {
|
for (var y = 0; y < mask.Mask.GetLength(1); y++) {
|
||||||
if (mask.Mask[x, y].X == 0 || mask.Mask[x, y].Y > 0) continue;
|
if (mask.Mask[x, y].X == 0) continue;
|
||||||
finalImage[x, y] = new(Remap(gradient[x, y].X, MAX, MIN, currStep, currStep + stepIncrement));
|
finalImage[x, y] = new(Remap(gradient[x, y].X, MAX, MIN, currStep, currStep + stepIncrement));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +130,7 @@ public class Program {
|
|||||||
});
|
});
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"\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+1)} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
|
static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
|
||||||
@@ -167,16 +181,20 @@ 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);
|
||||||
Vector2 p = new(x, y);
|
Vector2 p = new(x/(float)width, y/(float)height); //get the pixel position as a Vector2
|
||||||
|
|
||||||
float minDist = MAX; //initialize the minimum distance to the maximum possible value
|
float minDist = MAX + 1; //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 = Vector2.DistanceSquared(p, edge);
|
Vector2 edgeNorm = new(edge.X / (float)width, edge.Y / (float)height);
|
||||||
if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist;
|
float dist = Vector2.DistanceSquared(p, edgeNorm);
|
||||||
|
if (dist < minDist) minDist = dist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (minDist > MAX)
|
||||||
|
minDist = MAX;
|
||||||
|
|
||||||
temp[x, y] = new(minDist);
|
temp[x, y] = new(minDist);
|
||||||
if (minDist > AbsMax) AbsMax = minDist;
|
if (minDist > AbsMax) AbsMax = minDist;
|
||||||
iterCount++;
|
iterCount++;
|
||||||
|
|||||||
@@ -55,6 +55,18 @@
|
|||||||
<None Update="spherehalf.png">
|
<None Update="spherehalf.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="spherehalf32.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="spherefull32.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="spherecut32.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="sphereempty.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
BIN
SDFMapCreator/spherecut32.png
Normal file
BIN
SDFMapCreator/spherecut32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 680 B |
BIN
SDFMapCreator/sphereempty.png
Normal file
BIN
SDFMapCreator/sphereempty.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
SDFMapCreator/spherefull32.png
Normal file
BIN
SDFMapCreator/spherefull32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 693 B |
BIN
SDFMapCreator/spherehalf32.png
Normal file
BIN
SDFMapCreator/spherehalf32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 540 B |
Reference in New Issue
Block a user