Compare commits
2 Commits
3ba22ac3b2
...
809068e270
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
809068e270 | ||
|
|
e3dbb82587 |
@@ -20,7 +20,7 @@ public struct float4(float r, float g, float b, float a) {
|
||||
}
|
||||
|
||||
public record ImageData(MagickImage Image, float3[,] Pixels, List<float2> Edges);
|
||||
public record MaskData(float3[,] Mask, ImageData A, ImageData B, List<float2> Edges);
|
||||
public record MaskData(float3[,] Mask, ImageData A, List<float2> Edges);
|
||||
|
||||
public record SDFData(float3[,] SDF);
|
||||
|
||||
@@ -51,10 +51,13 @@ public static class ArrayExt {
|
||||
public class Program {
|
||||
private const float MAX = 65535f;
|
||||
private const float MIN = 0f;
|
||||
private const bool outputMasks = true;
|
||||
private const bool outputMasks = false;
|
||||
private const bool outputSDFs = false;
|
||||
private const bool outputGradients = false;
|
||||
static List<ImageData> Images = new();
|
||||
static List<MaskData> Masks = new();
|
||||
static List<SDFData> SDFs = new();
|
||||
static List<float3[,]> Gradients = new();
|
||||
|
||||
static void LoadImage(string imgPath) {
|
||||
var image = new MagickImage(imgPath);
|
||||
@@ -75,6 +78,7 @@ public class Program {
|
||||
public static void Main(string[] args) {
|
||||
Console.WriteLine("Reading images...");
|
||||
//foreach image in arguments load the image
|
||||
|
||||
LoadImage("01.png");
|
||||
LoadImage("02.png");
|
||||
LoadImage("03.png");
|
||||
@@ -93,10 +97,11 @@ public class Program {
|
||||
}
|
||||
|
||||
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 (int i = 0; i < Images.Count - 1; i++) {
|
||||
var mask = GetABMask(Images[i].Pixels, Images[i + 1].Pixels, Images[i].Image.Width, Images[i].Image.Height);
|
||||
Masks.Add(new(mask, Images[i], Images[i + 1], new()));
|
||||
for (int i = 1; i < Images.Count; i++) {
|
||||
var mask = GetABMask(Images[i-1].Pixels, Images[i].Pixels, Images[i].Image.Width, Images[i].Image.Height);
|
||||
Masks.Add(new(mask, Images[i], new()));
|
||||
}
|
||||
|
||||
Console.WriteLine("Edge detecting masks...");
|
||||
@@ -115,11 +120,44 @@ public class Program {
|
||||
Console.WriteLine("Creating SDFs...");
|
||||
for (var i = 0; i < Masks.Count; i++) {
|
||||
var mask = Masks[i];
|
||||
var sdf = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1));
|
||||
SDFs.Add(SDF(mask));
|
||||
if(!outputSDFs) continue;
|
||||
var sdf = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1));
|
||||
sdf.GetPixels().SetPixels(SDFs[i].SDF.ToFloatArray());
|
||||
sdf.Write($"sdf{i}.png", MagickFormat.Png48);
|
||||
}
|
||||
|
||||
Console.WriteLine("Creating gradients...");
|
||||
for (var i = 0; i < Masks.Count - 1; i++) {
|
||||
var gradientData = Gradient(Masks[i], SDFs[i], SDFs[i + 1]);
|
||||
Gradients.Add(gradientData);
|
||||
if(!outputGradients) continue;
|
||||
var gradient = new MagickImage(MagickColors.Black, (uint)Masks[i].Mask.GetLength(0), (uint)Masks[i].Mask.GetLength(1));
|
||||
gradient.GetPixels().SetPixels(gradientData.ToFloatArray());
|
||||
gradient.Write($"gradient{i}.png", MagickFormat.Png24);
|
||||
}
|
||||
|
||||
Console.WriteLine("Preparing transitions...");
|
||||
//for each gradient read the corresponding mask
|
||||
//for each pixel in the mask, lerp the pixel value with the gradient value
|
||||
//write the result to a new image
|
||||
int width = Masks[0].Mask.GetLength(0);
|
||||
int height = Masks[0].Mask.GetLength(1);
|
||||
for (var i = 0; i < Gradients.Count; i++) {
|
||||
var mask = Masks[i + 1];
|
||||
var gradient = Gradients[i];
|
||||
var transition = new float3[width, height];
|
||||
for (var x = 0; x < mask.Mask.GetLength(0); x++) {
|
||||
for (var y = 0; y < mask.Mask.GetLength(1); y++) {
|
||||
if (mask.Mask[x, y].r == 0) continue;
|
||||
transition[x, y] = new(Lerp(Images[i].Pixels[x,y].r, MAX, gradient[x,y].r));
|
||||
}
|
||||
}
|
||||
var transitionImage = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1));
|
||||
transitionImage.GetPixels().SetPixels(transition.ToFloatArray());
|
||||
transitionImage.Write($"transition{i}.png", MagickFormat.Png24);
|
||||
}
|
||||
|
||||
Console.WriteLine("Done!");
|
||||
}
|
||||
|
||||
@@ -148,6 +186,32 @@ public class Program {
|
||||
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) {
|
||||
uint width = mask.A.Image.Width;
|
||||
uint height = mask.A.Image.Height;
|
||||
int iterCount = 0;
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
float3[,] temp = new float3[width, height];
|
||||
Console.WriteLine("Running edge detection...");
|
||||
Parallel.For(0, width * height, (i) => {
|
||||
int x = (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 b = (sdfB.SDF[x,y].r + sdfB.SDF[x,y].g + sdfB.SDF[x,y].b )/ 3;
|
||||
|
||||
var gradient = a / (a + b);
|
||||
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)");
|
||||
var min = temp.Cast<float3>().Min(x => x.r);
|
||||
var max = temp.Cast<float3>().Max(x => x.r);
|
||||
Console.WriteLine($"Min: {min} | Max: {max}");
|
||||
return temp;
|
||||
}
|
||||
|
||||
static SDFData SDF(MaskData mask) {
|
||||
var width = (uint)mask.Mask.GetLength(0);
|
||||
var height = (uint)mask.Mask.GetLength(1);
|
||||
@@ -161,11 +225,6 @@ public class Program {
|
||||
var x = (int)(i % width);
|
||||
var y = (int)(i / width);
|
||||
float2 p = new(x, y);
|
||||
//skip all pixels we don't care about
|
||||
if(mask.Mask[x, y].r == 0) {
|
||||
temp[x, y] = new(MIN);
|
||||
return;
|
||||
}
|
||||
|
||||
float minDist = MAX; //initialize the minimum distance to the maximum possible value
|
||||
|
||||
@@ -226,6 +285,19 @@ public class Program {
|
||||
return temp;
|
||||
}
|
||||
|
||||
static float3[,] SelfMask(float3[,] A, uint resX, uint resY) {
|
||||
var temp = new float3[resX, resY];
|
||||
Parallel.For(0, resX*resY, (i) => {
|
||||
uint x = (uint)(i % resX);
|
||||
uint y = (uint)(i / resX);
|
||||
var pixelA = A[x, y];
|
||||
float lumaA = (pixelA.r+pixelA.g+pixelA.b)/3;
|
||||
float resultPixel = lumaA > 0 ? MAX : MIN;
|
||||
temp[x, y] = new(resultPixel, 0, 0);
|
||||
});
|
||||
return temp;
|
||||
}
|
||||
|
||||
static bool EdgeKernel(float3[,] mask, int x, int y, uint width, uint height) {
|
||||
//if we are already empty, return false
|
||||
if (mask[x, y].r == 0) return false;
|
||||
|
||||
Reference in New Issue
Block a user