Merge remote-tracking branch 'origin/master'
@@ -2,7 +2,11 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using ImageMagick;
|
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 struct float2(float x, float y) {
|
||||||
public float x = x, y = 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 float r = r, g = g, b = b, a = a;
|
||||||
public float4(float value) : this(value, value, value, value) {}
|
public float4(float value) : this(value, value, value, value) {}
|
||||||
public float4(float r, float g, float b) : this(r, g, b, 1f) {}
|
public float4(float r, float g, float b) : this(r, g, b, 1f) {}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public record ImageData(MagickImage Image, float3[,] Pixels, List<float2> Edges);
|
public record ImageData(MagickImage Image, float3[,] Pixels, List<float2> Edges);
|
||||||
public record MaskData(float3[,] Mask, ImageData A, List<float2> Edges);
|
public record MaskData(float3[,] Mask, ImageData A, List<float2> Edges);
|
||||||
@@ -39,23 +43,23 @@ public static class ArrayExt {
|
|||||||
float[] result = new float[array.GetLength(0) * array.GetLength(1) * 3];
|
float[] result = new float[array.GetLength(0) * array.GetLength(1) * 3];
|
||||||
for(int x = 0; x < array.GetLength(0); x++) {
|
for(int x = 0; x < array.GetLength(0); x++) {
|
||||||
for(int y = 0; y < array.GetLength(1); y++) {
|
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] = array[x, y].X;
|
||||||
result[x*array.GetLength(1)*3 + y*3+1] = array[x, y].g;
|
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].b;
|
result[x*array.GetLength(1)*3 + y*3+2] = array[x, y].Z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class Program {
|
public class Program {
|
||||||
private const float MAX = 65535f;
|
private const float MAX = 65535f;
|
||||||
private const float MIN = 0f;
|
private const float MIN = 0f;
|
||||||
private const bool outputMasks = false;
|
private static readonly int MAX_THREADS = Environment.ProcessorCount - 2;
|
||||||
private const bool outputSDFs = false;
|
private const bool outputMasks = true;
|
||||||
private const bool outputGradients = false;
|
private const bool outputSDFs = true;
|
||||||
|
private const bool outputGradients = true;
|
||||||
|
static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = MAX_THREADS };
|
||||||
static List<ImageData> Images = new();
|
static List<ImageData> Images = new();
|
||||||
static List<MaskData> Masks = new();
|
static List<MaskData> Masks = new();
|
||||||
static List<SDFData> SDFs = new();
|
static List<SDFData> SDFs = new();
|
||||||
@@ -85,14 +89,10 @@ public class Program {
|
|||||||
Console.WriteLine("Reading images...");
|
Console.WriteLine("Reading images...");
|
||||||
//foreach image in arguments load the image
|
//foreach image in arguments load the image
|
||||||
|
|
||||||
LoadImage("01.png");
|
var imagesPath = "images";
|
||||||
LoadImage("02.png");
|
|
||||||
LoadImage("03.png");
|
for (int i = 0; i < 8; i++)
|
||||||
LoadImage("04.png");
|
LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i+1:00}.png");
|
||||||
LoadImage("05.png");
|
|
||||||
LoadImage("06.png");
|
|
||||||
LoadImage("07.png");
|
|
||||||
LoadImage("08.png");
|
|
||||||
|
|
||||||
//LoadImage("1.png");
|
//LoadImage("1.png");
|
||||||
//LoadImage("2.png");
|
//LoadImage("2.png");
|
||||||
@@ -155,8 +155,8 @@ public class Program {
|
|||||||
var transition = new float3[width, height];
|
var transition = new float3[width, height];
|
||||||
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].r == 0) continue;
|
if (mask.Mask[x, y].X == 0) continue;
|
||||||
transition[x, y] = new(Lerp(Images[i].Pixels[x,y].r, MAX, gradient[x,y].r));
|
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));
|
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();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
Console.WriteLine("Running edge detection...");
|
Console.WriteLine("Running edge detection...");
|
||||||
Parallel.For(0, width * height, (i) => {
|
Parallel.For(0, width * height, parallelOptions, (i) => {
|
||||||
int x = (int)(i % width);
|
int x = (int)(i % width);
|
||||||
int y = (int)(i / width);
|
int y = (int)(i / width);
|
||||||
|
|
||||||
if (!EdgeKernel(maskData.Mask, x, y, width, height)) return;
|
if (!EdgeKernel(maskData.Mask, x, y, width, height)) return;
|
||||||
var color = maskData.Mask[x, y];
|
var color = maskData.Mask[x, y];
|
||||||
color.g = MAX;
|
color.Y = MAX;
|
||||||
maskData.Mask[x, y] = color;
|
maskData.Mask[x, y] = color;
|
||||||
lock(maskData.Edges) maskData.Edges.Add(new(x, y));
|
lock(maskData.Edges) maskData.Edges.Add(new(x, y));
|
||||||
iterCount++;
|
iterCount++;
|
||||||
@@ -200,20 +200,20 @@ public class Program {
|
|||||||
sw.Start();
|
sw.Start();
|
||||||
float3[,] temp = new float3[width, height];
|
float3[,] temp = new float3[width, height];
|
||||||
Console.WriteLine("Running edge detection...");
|
Console.WriteLine("Running edge detection...");
|
||||||
Parallel.For(0, width * height, (i) => {
|
Parallel.For(0, width * height, parallelOptions, (i) => {
|
||||||
int x = (int)(i % width);
|
int x = (int)(i % width);
|
||||||
int y = (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 a = (sdfA.SDF[x,y].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3;
|
||||||
var b = (sdfB.SDF[x,y].r + sdfB.SDF[x,y].g + sdfB.SDF[x,y].b )/ 3;
|
var b = (sdfB.SDF[x,y].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3;
|
||||||
|
|
||||||
var gradient = a / (a + b);
|
var gradient = a / (a + b);
|
||||||
temp[x, y] = new(Remap(gradient, 0, 1, MIN, MAX));
|
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)");
|
Console.WriteLine($"Gradient Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/(float)sw.Elapsed.TotalSeconds:N0} pixels/s)");
|
||||||
var min = temp.Cast<float3>().Min(x => x.r);
|
var min = temp.Cast<float3>().Min(x => x.X);
|
||||||
var max = temp.Cast<float3>().Max(x => x.r);
|
var max = temp.Cast<float3>().Max(x => x.X);
|
||||||
Console.WriteLine($"Min: {min} | Max: {max}");
|
Console.WriteLine($"Min: {min} | Max: {max}");
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ public class Program {
|
|||||||
int iterCount = 0;
|
int iterCount = 0;
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
Parallel.For(0, width * height, (i) => {
|
Parallel.For(0, width * height, parallelOptions, (i) => {
|
||||||
//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);
|
||||||
@@ -236,7 +236,7 @@ public class Program {
|
|||||||
|
|
||||||
//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 = EuclideanDistance(p, edge);
|
float dist = float2.DistanceSquared(p, edge);
|
||||||
if (dist < minDist) minDist = dist;
|
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)");
|
Console.WriteLine($"SDF Generation Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/sw.Elapsed.TotalSeconds:N0} pixels/s)");
|
||||||
sw.Restart();
|
sw.Restart();
|
||||||
Parallel.For(0, width * height, (i) => {
|
Parallel.For(0, width * height, parallelOptions, (i) => {
|
||||||
//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);
|
||||||
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($"SDF Normalization Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/sw.Elapsed.TotalSeconds:N0} pixels/s)");
|
||||||
Console.WriteLine("AbsMax: " + AbsMax);
|
Console.WriteLine("AbsMax: " + AbsMax);
|
||||||
@@ -263,7 +263,7 @@ public class Program {
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static float EuclideanDistance(float2 a, float2 b) =>
|
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<float> pixels1) {
|
private static void ImageData(MagickImage image1, IPixelCollection<float> pixels1) {
|
||||||
@@ -278,13 +278,13 @@ public class Program {
|
|||||||
|
|
||||||
static float3[,] GetABMask(float3[,] A, float3[,] B, uint resX, uint resY) {
|
static float3[,] GetABMask(float3[,] A, float3[,] B, uint resX, uint resY) {
|
||||||
var temp = new float3[resX, 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 x = (uint)(i % resX);
|
||||||
uint y = (uint)(i / resX);
|
uint y = (uint)(i / resX);
|
||||||
var pixelA = A[x, y];
|
var pixelA = A[x, y];
|
||||||
var pixelB = B[x, y];
|
var pixelB = B[x, y];
|
||||||
float lumaA = (pixelA.r+pixelA.g+pixelA.b)/3;
|
float lumaA = (pixelA.X+pixelA.Y+pixelA.Z)/3;
|
||||||
float lumaB = (pixelB.r+pixelB.g+pixelB.b)/3;
|
float lumaB = (pixelB.X+pixelB.Y+pixelB.Z)/3;
|
||||||
float resultPixel = lumaB > lumaA ? MAX : MIN;
|
float resultPixel = lumaB > lumaA ? MAX : MIN;
|
||||||
temp[x, y] = new(resultPixel, 0, 0);
|
temp[x, y] = new(resultPixel, 0, 0);
|
||||||
});
|
});
|
||||||
@@ -293,11 +293,11 @@ public class Program {
|
|||||||
|
|
||||||
static float3[,] SelfMask(float3[,] A, uint resX, uint resY) {
|
static float3[,] SelfMask(float3[,] A, uint resX, uint resY) {
|
||||||
var temp = new float3[resX, 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 x = (uint)(i % resX);
|
||||||
uint y = (uint)(i / resX);
|
uint y = (uint)(i / resX);
|
||||||
var pixelA = A[x, y];
|
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;
|
float resultPixel = lumaA > 0 ? MAX : MIN;
|
||||||
temp[x, y] = new(resultPixel, 0, 0);
|
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) {
|
static bool EdgeKernel(float3[,] 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].r == 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
|
||||||
if (x == 0 || y == 0 || x == width - 1 || y == height - 1) return false;
|
if (x == 0 || y == 0 || x == width - 1 || y == height - 1) return false;
|
||||||
//check the 3x3 kernel
|
//check the 3x3 kernel
|
||||||
@@ -314,7 +314,7 @@ public class Program {
|
|||||||
for (int yi = y - 1; yi <= y + 1; yi++) {
|
for (int yi = y - 1; yi <= y + 1; yi++) {
|
||||||
if (xi < 0 || xi >= width || yi < 0 || yi >= height)
|
if (xi < 0 || xi >= width || yi < 0 || yi >= height)
|
||||||
continue; //skip out of bounds pixels
|
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
|
return true; //if we find a black pixel, return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Magick.NET-Q16-HDRI-OpenMP-x64" Version="14.5.0" />
|
<PackageReference Include="Magick.NET-Q16-HDRI-OpenMP-x64" Version="14.5.0" />
|
||||||
|
<PackageReference Include="System.Numerics.Vectors" Version="4.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -21,6 +22,30 @@
|
|||||||
<None Update="TestPattern.png">
|
<None Update="TestPattern.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="images\01.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="images\02.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="images\03.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="images\04.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="images\05.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="images\06.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="images\07.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="images\08.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
BIN
SDFMapCreator/images/01.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
SDFMapCreator/images/02.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
SDFMapCreator/images/03.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
SDFMapCreator/images/04.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
SDFMapCreator/images/05.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
SDFMapCreator/images/06.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
SDFMapCreator/images/07.png
Normal file
|
After Width: | Height: | Size: 199 KiB |
BIN
SDFMapCreator/images/08.png
Normal file
|
After Width: | Height: | Size: 178 KiB |