Compare commits
3 Commits
84a49bb2a8
...
f3fca33606
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3fca33606 | ||
|
|
4c45884c96 | ||
|
|
92529b562e |
5
SDFMapCreator.sln.DotSettings.user
Normal file
5
SDFMapCreator.sln.DotSettings.user
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVector3_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F6edafe13d8727aa238b865f5dc91dbc984b5abfbc60bece3744f6311c2c_003FVector3_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue"><AssemblyExplorer>
|
||||||
|
<Assembly Path="/mnt/nvme2/Railgun/SDFMapCreator/SDFMapCreator/bin/Debug/net8.0/Magick.NET-Q16-HDRI-OpenMP-x64.dll" />
|
||||||
|
</AssemblyExplorer></s:String></wpf:ResourceDictionary>
|
||||||
@@ -24,7 +24,8 @@ public struct float4(float r, float g, float b, float a) {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
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 Image, List<float2> Edges);
|
||||||
|
public record TransitionMaskData(float3[,] Mask, ImageData ImageA, ImageData ImageB);
|
||||||
|
|
||||||
public record SDFData(float3[,] SDF);
|
public record SDFData(float3[,] SDF);
|
||||||
|
|
||||||
@@ -61,7 +62,8 @@ public class Program {
|
|||||||
private const bool outputGradients = true;
|
private const bool outputGradients = true;
|
||||||
static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = MAX_THREADS };
|
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<TransitionMaskData> TransitionMasks = new();
|
||||||
|
static List<MaskData> ImageMasks = new();
|
||||||
static List<SDFData> SDFs = new();
|
static List<SDFData> SDFs = new();
|
||||||
static List<float3[,]> Gradients = new();
|
static List<float3[,]> Gradients = new();
|
||||||
|
|
||||||
@@ -91,9 +93,14 @@ public class Program {
|
|||||||
|
|
||||||
var imagesPath = "images";
|
var imagesPath = "images";
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++)
|
||||||
LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i+1:00}.png");
|
LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i+1:00}.png");
|
||||||
|
|
||||||
|
|
||||||
|
//LoadImage("spherecut.png");
|
||||||
|
//LoadImage("spherefull.png");
|
||||||
|
|
||||||
//LoadImage("1.png");
|
//LoadImage("1.png");
|
||||||
//LoadImage("2.png");
|
//LoadImage("2.png");
|
||||||
//check if all the images in Images are the same resolution
|
//check if all the images in Images are the same resolution
|
||||||
@@ -103,29 +110,36 @@ public class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Creating masks...");
|
Console.WriteLine("Creating masks...");
|
||||||
Masks.Add(new (SelfMask(Images[0].Pixels, Images[0].Image.Width, Images[0].Image.Height), Images[0], new()));
|
|
||||||
//for each image pair, create a mask
|
//for each image pair, create a mask
|
||||||
for (int i = 1; i < Images.Count; i++) {
|
var width = Images[0].Image.Width;
|
||||||
var mask = GetABMask(Images[i-1].Pixels, Images[i].Pixels, Images[i].Image.Width, Images[i].Image.Height);
|
var height = Images[0].Image.Height;
|
||||||
Masks.Add(new(mask, Images[i], new()));
|
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);
|
||||||
|
TransitionMasks.Add(new(mask, Images[i], Images[i + 1]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Edge detecting masks...");
|
Console.WriteLine("Edge detecting masks...");
|
||||||
//EdgeDetect all masks
|
//EdgeDetect all masks
|
||||||
foreach (var t in Masks) { EdgeDetect(t); }
|
foreach (var t in ImageMasks) { EdgeDetect(t); }
|
||||||
|
|
||||||
if(outputMasks) {
|
if(outputMasks) {
|
||||||
Console.WriteLine("Writing masks...");
|
Console.WriteLine("Writing masks...");
|
||||||
for (int i = 0; i < Masks.Count; i++) {
|
for (int i = 0; i < TransitionMasks.Count; i++) {
|
||||||
var mask = new MagickImage(MagickColors.Black, (uint)Masks[i].Mask.GetLength(0), (uint)Masks[i].Mask.GetLength(1));
|
var mask = new MagickImage(MagickColors.Black, (uint)TransitionMasks[i].Mask.GetLength(0), (uint)TransitionMasks[i].Mask.GetLength(1));
|
||||||
mask.GetPixels().SetPixels(Masks[i].Mask.ToFloatArray());
|
mask.GetPixels().SetPixels(TransitionMasks[i].Mask.ToFloatArray());
|
||||||
mask.Write($"mask{i}.png", MagickFormat.Png24);
|
mask.Write($"mask{i}.png", MagickFormat.Png24);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Creating SDFs...");
|
Console.WriteLine("Creating SDFs...");
|
||||||
for (var i = 0; i < Masks.Count; i++) {
|
for (var i = 0; i < ImageMasks.Count; i++) {
|
||||||
var mask = Masks[i];
|
var mask = ImageMasks[i];
|
||||||
SDFs.Add(SDF(mask));
|
SDFs.Add(SDF(mask));
|
||||||
if(!outputSDFs) continue;
|
if(!outputSDFs) continue;
|
||||||
var sdf = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1));
|
var sdf = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1));
|
||||||
@@ -134,42 +148,42 @@ public class Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Creating gradients...");
|
Console.WriteLine("Creating gradients...");
|
||||||
for (var i = 0; i < Masks.Count - 1; i++) {
|
for (var i = 0; i < TransitionMasks.Count; i++) {
|
||||||
var gradientData = Gradient(Masks[i], SDFs[i], SDFs[i + 1]);
|
Console.WriteLine($"Generating gradient {i}...");
|
||||||
|
var gradientData = Gradient(TransitionMasks[i], SDFs[i], SDFs[i + 1]);
|
||||||
Gradients.Add(gradientData);
|
Gradients.Add(gradientData);
|
||||||
if(!outputGradients) continue;
|
if(!outputGradients) continue;
|
||||||
var gradient = new MagickImage(MagickColors.Black, (uint)Masks[i].Mask.GetLength(0), (uint)Masks[i].Mask.GetLength(1));
|
var gradient = new MagickImage(MagickColors.Black, (uint)TransitionMasks[i].Mask.GetLength(0), (uint)TransitionMasks[i].Mask.GetLength(1));
|
||||||
gradient.GetPixels().SetPixels(gradientData.ToFloatArray());
|
gradient.GetPixels().SetPixels(gradientData.ToFloatArray());
|
||||||
gradient.Write($"gradient{i}.png", MagickFormat.Png24);
|
gradient.Write($"gradient{i}.png", MagickFormat.Png24);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Preparing transitions...");
|
// generate final image
|
||||||
//for each gradient read the corresponding mask
|
var finalImage = new float3[width, height];
|
||||||
//for each pixel in the mask, lerp the pixel value with the gradient value
|
var currStep = 0f;
|
||||||
//write the result to a new image
|
var stepIncrement = MAX / (Gradients.Count + 1);
|
||||||
int width = Masks[0].Mask.GetLength(0);
|
|
||||||
int height = Masks[0].Mask.GetLength(1);
|
|
||||||
for (var i = 0; i < Gradients.Count; i++) {
|
for (var i = 0; i < Gradients.Count; i++) {
|
||||||
var mask = Masks[i + 1];
|
var mask = ImageMasks[i + 1];
|
||||||
var gradient = Gradients[i];
|
var gradient = Gradients[i];
|
||||||
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].X == 0) continue;
|
if (mask.Mask[x, y].X == 0 || mask.Mask[x, y].Y > 0) continue;
|
||||||
transition[x, y] = new(Lerp(Images[i].Pixels[x,y].X, MAX, gradient[x,y].X));
|
finalImage[x, y] = new(Remap(gradient[x,y].X, MAX, MIN, currStep, currStep + stepIncrement));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var transitionImage = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1));
|
currStep += stepIncrement;
|
||||||
transitionImage.GetPixels().SetPixels(transition.ToFloatArray());
|
|
||||||
transitionImage.Write($"transition{i}.png", MagickFormat.Png24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var finalImageMagick = new MagickImage(MagickColors.Black, (uint)width, (uint)height);
|
||||||
|
finalImageMagick.GetPixels().SetPixels(finalImage.ToFloatArray());
|
||||||
|
finalImageMagick.Write("final.png", MagickFormat.Png24);
|
||||||
|
|
||||||
Console.WriteLine("Done!");
|
Console.WriteLine("Done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EdgeDetect(MaskData maskData) {
|
private static void EdgeDetect(MaskData maskData) {
|
||||||
uint width = maskData.A.Image.Width;
|
uint width = maskData.Image.Image.Width;
|
||||||
uint height = maskData.A.Image.Height;
|
uint height = maskData.Image.Image.Height;
|
||||||
int iterCount = 0;
|
int iterCount = 0;
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
@@ -185,35 +199,42 @@ public class Program {
|
|||||||
lock(maskData.Edges) maskData.Edges.Add(new(x, y));
|
lock(maskData.Edges) maskData.Edges.Add(new(x, y));
|
||||||
iterCount++;
|
iterCount++;
|
||||||
if (iterCount % (width * height / 100) == 0) {
|
if (iterCount % (width * height / 100) == 0) {
|
||||||
ConsoleUpdateLine($"Progress: {iterCount/(width*height):P}% | {iterCount/(sw.Elapsed.TotalSeconds):N0} pixels/s");
|
ConsoleUpdateLine($"Progress: {iterCount/(float)(width*height):P} | {iterCount/(sw.Elapsed.TotalSeconds):N0} pixels/s");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
Console.WriteLine($"Edge pixels: {maskData.Edges.Count} | {maskData.Edges.Count/sw.ElapsedMilliseconds} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s");
|
Console.WriteLine($"Edge pixels: {maskData.Edges.Count} | {maskData.Edges.Count/sw.ElapsedMilliseconds} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
static float3[,] Gradient(MaskData mask, SDFData sdfA, SDFData sdfB) {
|
static float3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
|
||||||
uint width = mask.A.Image.Width;
|
uint width = mask.ImageA.Image.Width;
|
||||||
uint height = mask.A.Image.Height;
|
uint height = mask.ImageA.Image.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];
|
float3[,] temp = new float3[width, height];
|
||||||
Console.WriteLine("Running edge detection...");
|
|
||||||
|
var min = MAX;
|
||||||
|
var max = MIN;
|
||||||
|
|
||||||
|
Console.WriteLine("Running gradient generation...");
|
||||||
Parallel.For(0, width * height, parallelOptions, (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].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3;
|
if(mask.Mask[x,y].X == 0 || mask.Mask[x,y].Y > 0) return;
|
||||||
var b = (sdfB.SDF[x,y].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3;
|
|
||||||
|
var a = sdfA.SDF[x, y].X;
|
||||||
|
var b = sdfB.SDF[x, y].X;
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
|
if (gradient < min) min = gradient;
|
||||||
|
if (gradient > max) max = gradient;
|
||||||
});
|
});
|
||||||
|
|
||||||
Console.WriteLine($"Gradient Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/(float)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.X);
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -237,10 +258,10 @@ 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 = float2.DistanceSquared(p, edge);
|
float dist = float2.DistanceSquared(p, edge);
|
||||||
if (dist < minDist) minDist = dist;
|
if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp[x, y] = new(float.Abs(minDist));
|
temp[x, y] = new(minDist);
|
||||||
if (minDist > AbsMax) AbsMax = minDist;
|
if (minDist > AbsMax) AbsMax = minDist;
|
||||||
iterCount++;
|
iterCount++;
|
||||||
if (iterCount % (width * height / 100) == 0) {
|
if (iterCount % (width * height / 100) == 0) {
|
||||||
@@ -256,6 +277,8 @@ public class Program {
|
|||||||
var y = (int)(i / width);
|
var y = (int)(i / width);
|
||||||
temp[x, y] = new(Remap(temp[x, y].X, 0, AbsMax, MIN, MAX));
|
temp[x, y] = new(Remap(temp[x, y].X, 0, AbsMax, MIN, MAX));
|
||||||
});
|
});
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
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);
|
||||||
return new(temp);
|
return new(temp);
|
||||||
@@ -265,7 +288,6 @@ public class Program {
|
|||||||
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) {
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"""
|
$"""
|
||||||
|
|||||||
@@ -46,6 +46,12 @@
|
|||||||
<None Update="images\08.png">
|
<None Update="images\08.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="spherecut.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="spherefull.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
BIN
SDFMapCreator/spherecut.png
Normal file
BIN
SDFMapCreator/spherecut.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
BIN
SDFMapCreator/spherefull.png
Normal file
BIN
SDFMapCreator/spherefull.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user