diff --git a/DigitalImageProcessing/Alograms.cs b/DigitalImageProcessing/Alograms.cs new file mode 100644 index 0000000..f63ad1d --- /dev/null +++ b/DigitalImageProcessing/Alograms.cs @@ -0,0 +1,238 @@ +using SkiaSharp; + +namespace DigitalImageProcessing +{ + internal class Alograms + { + public static void Steps(string outputDirectoryPath, string filename) + { + int blockSize = 16; + + using SKBitmap bitmap = SKBitmap.Decode(filename); + + using SKBitmap avgGraph = new(bitmap.Width, bitmap.Height); + using SKCanvas avgCanvas = new(avgGraph); + + float[,] gray = new float[bitmap.Width, bitmap.Height]; + float[,] xgradient = new float[bitmap.Width, bitmap.Height]; + float[,] ygradient = new float[bitmap.Width, bitmap.Height]; + float[,] fequency = new float[bitmap.Width, bitmap.Height]; + float[,] angle = new float[bitmap.Width, bitmap.Height]; + //float[,] direction = new float[bitmap.Width, bitmap.Height]; + + for (int y = 0; y < bitmap.Height; y++) + for (int x = 0; x < bitmap.Width; x++) + { + SKColor color = bitmap.GetPixel(x, y); + + // gray + gray[x, y] = (color.Red + color.Green + color.Blue) / 3f / 256f; + } + + for (int y = 0; y < bitmap.Height; y++) + for (int x = 0; x < bitmap.Width; x++) + { + // gradient + if (x > 0 && x < bitmap.Width - 1) + xgradient[x, y] = (gray[x + 1, y] - gray[x - 1, y]) / 2f; + if (y > 0 && y < bitmap.Height - 1) + ygradient[x, y] = (gray[x, y + 1] - gray[x, y - 1]) / 2f; + fequency[x, y] = float.Sqrt((xgradient[x, y] * xgradient[x, y]) + (ygradient[x, y] * ygradient[x, y])); + if (fequency[x, y] == 0) + continue; + angle[x, y] = float.Atan2(ygradient[x, y], xgradient[x, y]); + int blockx = x / blockSize; + int blocky = y / blockSize; + float dirx = float.Cos(angle[x, y]); + float diry = float.Sin(angle[x, y]); + avgCanvas.DrawLine( + (blockx + 0.5f - (diry / 2)) * blockSize, + (blocky + 0.5f - (-dirx / 2)) * blockSize, + (blockx + 0.5f + (diry / 2)) * blockSize, + (blocky + 0.5f + (-dirx / 2)) * blockSize, + new SKPaint { Color = SKColors.Red.WithAlpha((byte)(fequency[x, y] * 64)), StrokeWidth = 1 }); + } + + using FileStream fs = new(outputDirectoryPath + @"avgGraph.png", FileMode.Create, FileAccess.Write); + avgGraph.Encode(fs, SKEncodedImageFormat.Png, 100); + + xgradient.DrawTo(outputDirectoryPath + @"xgradient.png"); + ygradient.DrawTo(outputDirectoryPath + @"ygradient.png"); + fequency.DrawTo(outputDirectoryPath + @"fequency.png"); + angle.DrawTo(outputDirectoryPath + @"angle.png"); + } + public static void Noises(string filepath) + { + using SKBitmap bitmap = new(1024, 1024);//SKBitmap.Decode(filepath); + using SKCanvas canvas = new SKCanvas(bitmap); + canvas.Clear(SKColors.Black); + const int gap = 128; + canvas.DrawRect(new SKRectI(gap, gap, 1024 - gap, 1024 - gap), new SKPaint() + { + Color = SKColors.Gray, + Style = SKPaintStyle.Fill, + }); + canvas.DrawCircle(512, 512, 256, new SKPaint() + { + Color = SKColors.White, + Style = SKPaintStyle.Fill, + }); + + using FileStream stream = new(@"C:\Users\Surface\Downloads\" + @"original.png", FileMode.Create, FileAccess.Write); + bitmap.Encode(stream, SKEncodedImageFormat.Png, 100); + + using SKBitmap noiseBitmap = new(bitmap.Width, bitmap.Height); + using SKCanvas noiseCanvas = new(noiseBitmap); + Func noiseFunc = (v) => ApplyGaussianToRandomValue(new(), v, 10f);//NoiseFuncs.Gaussian(v, new Random(), 20f); + int[] changes = new int[256]; + //NoiseFuncs.GetFunc(NoiseType.Gaussian, new Random()); + for (int x = 0; x < bitmap.Width; x++) + for (int y = 0; y < bitmap.Height; y++) + { + byte noised = noiseFunc(bitmap.GetPixel(x, y).Red); + noiseBitmap.SetPixel(x, y, + new SKColor( + noised, + noised, + noised)); + changes[noised]++; + } + using FileStream fs = new(@"C:\Users\Surface\Downloads\" + @"noise.png", FileMode.Create, FileAccess.Write); + noiseBitmap.Encode(fs, SKEncodedImageFormat.Png, 100); + + using SKBitmap c = new(256, 256); + using SKCanvas cc = new(c); + cc.Clear(SKColors.Black); + int max = changes[1..].Max() + 1; + for (int i = 0; i < 255; i++) + { + cc.DrawLine(i, 256, i, 256 - (changes[i] * 256 / max), new SKPaint() + { + Color = i is 0 or 127 or 255 ? SKColors.Red : SKColors.White, + StrokeWidth = 1, + }); + //cc.DrawLine(i, 256, i, 256 - (NoiseFuncs.gaussianKerel[i] * 256), new SKPaint() + //{ + // Color = SKColors.LightBlue.WithAlpha(127), + // StrokeWidth = 1, + //}); + } + using FileStream fsc = new(@"C:\Users\Surface\Downloads\" + @"changes.png", FileMode.Create, FileAccess.Write); + c.Encode(fsc, SKEncodedImageFormat.Png, 100); + } + public static byte ApplyGaussianToRandomValue(Random random, float mean, float sigma) + { + // Generate a random value using the Box-Muller transform + double u1 = 1.0 - random.NextDouble(); // Uniform(0,1] random doubles + double u2 = 1.0 - random.NextDouble(); + double standardNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + + // Scale the standard normal value to the desired mean and standard deviation + double gaussianValue = mean + sigma * standardNormal; + + // Clamp the result to the byte range [0, 255] + return (byte)Math.Clamp((int)gaussianValue, 0, 255); + } + } + public static class NoiseFuncs + { + //public static Func GetFunc(NoiseType type, Random random) + //{ + // return type switch + // { + // NoiseType.SaltAndPepper => (value) => SaltAndPepper(value, random, 0.05f), + // NoiseType.Gaussian => (value) => Gaussian(value, random, 256f), + // _ => throw new NotImplementedException(), + // }; + //} + public static byte SaltAndPepper(byte value, Random random, float amount) + { + float r = (float)random.NextDouble(); + if (r < amount / 2) + return 0; + else if (r < amount) + return 255; + else + return value; + } + public static float[] gaussianKerel = []; + public static void InitGaussianKernel(float sigma) + { + gaussianKerel = new float[256]; + for (int i = 0; i < 256; i++) + { + gaussianKerel[i] = GaussianKernel(i, 127, sigma); + } + //for (int i = 1; i < 256; i++) + //{ + // gaussianKerel[i] += gaussianKerel[i - 1]; + //} + } + public static byte Gaussian(byte value, Random random, float sigma) + { + if (gaussianKerel.Length == 0) + InitGaussianKernel(sigma); + + //int randomIndex = random.Next(0, 256); + float r = random.NextSingle(); + float f = value* GaussianKernel(r * 255, value, sigma); + int newValue = (int)f;//(int)(value + (32 * (f))); + return (byte)Math.Clamp(newValue, 0, 255); + } + private static float GaussianKernel(float value, float mu, float sigma) + { + float exp = -(value - mu) * (value - mu) / (2 * sigma * sigma); + float peak = 1 / (sigma * float.Sqrt(2 * float.Pi)); + return peak * float.Exp(exp); + } + public static byte Rayleigh(byte value, Random random, float sigma) + { + double u = 1.0 - random.NextDouble(); + double noise = sigma * Math.Sqrt(-2.0 * Math.Log(u)); + int newValue = (int)(value + noise); + if (newValue < 0) newValue = 0; + if (newValue > 255) newValue = 255; + return (byte)newValue; + } + public static byte Gamma(byte value, Random random) + { + double u = 1.0 - random.NextDouble(); + double noise = -Math.Log(u); + int newValue = (int)(value + noise); + if (newValue < 0) newValue = 0; + if (newValue > 255) newValue = 255; + return (byte)newValue; + } + } + //public static class FilterFuncs + //{ + // public static Func GetFunc() + // { + + // } + // private static byte Mean(SKBitmap bitmap, SKRectI size, Func channel) + // { + // for(int x=size.Left;x<=size.Right;x++) + // for(int y=size.Top; y <= size.Bottom; y++) + // { + // byte value = channel(bitmap.GetPixel(x, y)); + + // } + // } + // private static byte Median(SKBitmap bitmap, SKRectI size) + // { + // } + + //} + public enum NoiseType + { + SaltAndPepper, + Gaussian, + Rayleigh, + } + //public enum FilterType + //{ + // Mean, + // Median, + //} +} diff --git a/DigitalImageProcessing/Noise.cs b/DigitalImageProcessing/Noise.cs new file mode 100644 index 0000000..4cc8d85 --- /dev/null +++ b/DigitalImageProcessing/Noise.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DigitalImageProcessing +{ + internal class Noise + { + } +} diff --git a/DigitalImageProcessing/Program.cs b/DigitalImageProcessing/Program.cs index acba414..262a0a2 100644 --- a/DigitalImageProcessing/Program.cs +++ b/DigitalImageProcessing/Program.cs @@ -1,61 +1,7 @@ using DigitalImageProcessing; using SkiaSharp; -string filename = @"E:\Download\a7522809e76767bec27db9650365c56a.jpeg"; - -int blockSize = 16; - -using SKBitmap bitmap = SKBitmap.Decode(filename); -using SKCanvas canvas = new(bitmap); - -using SKBitmap avgGraph = new(bitmap.Width, bitmap.Height); -using SKCanvas avgCanvas = new(avgGraph); - -float[,] gray = new float[bitmap.Width, bitmap.Height]; -float[,] xgradient = new float[bitmap.Width, bitmap.Height]; -float[,] ygradient = new float[bitmap.Width, bitmap.Height]; -float[,] fequency = new float[bitmap.Width, bitmap.Height]; -float[,] angle = new float[bitmap.Width, bitmap.Height]; -//float[,] direction = new float[bitmap.Width, bitmap.Height]; - -for (int y = 0; y < bitmap.Height; y++) - for (int x = 0; x < bitmap.Width; x++) - { - SKColor color = bitmap.GetPixel(x, y); - - // gray - gray[x, y] = (color.Red + color.Green + color.Blue) / 3f / 256f; - } - -for (int y = 0; y < bitmap.Height; y++) - for (int x = 0; x < bitmap.Width; x++) - { - // gradient - if (x > 0 && x < bitmap.Width - 1) - xgradient[x, y] = (gray[x + 1, y] - gray[x - 1, y]) / 2f; - if (y > 0 && y < bitmap.Height - 1) - ygradient[x, y] = (gray[x, y + 1] - gray[x, y - 1]) / 2f; - fequency[x, y] = float.Sqrt(xgradient[x, y] * xgradient[x, y] + ygradient[x, y] * ygradient[x, y]); - if (fequency[x, y] == 0) - continue; - angle[x, y] = float.Atan2(ygradient[x, y], xgradient[x, y]); - int blockx = x / blockSize; - int blocky = y / blockSize; - float dirx = float.Cos(angle[x, y]); - float diry = float.Sin(angle[x, y]); - avgCanvas.DrawLine( - (blockx + 0.5f - diry / 2) * blockSize, - (blocky + 0.5f - -dirx / 2) * blockSize, - (blockx + 0.5f + diry / 2) * blockSize, - (blocky + 0.5f + -dirx / 2) * blockSize, - new SKPaint { Color = SKColors.Red.WithAlpha((byte)(fequency[x, y] * 64)), StrokeWidth = 1 }); - } - -using FileStream fs = new(@"E:\Download\avgGraph.png", FileMode.Create, FileAccess.Write); -avgGraph.Encode(fs, SKEncodedImageFormat.Png, 100); - -xgradient.DrawTo(@"E:\Download\xgradient.png"); -ygradient.DrawTo(@"E:\Download\ygradient.png"); -fequency.DrawTo(@"E:\Download\fequency.png"); -angle.DrawTo(@"E:\Download\angle.png"); +string testImage = @"C:\Users\Surface\Downloads\" + @"1682575881230001.png"; +//Alograms.Steps(@"C:\Users\Surface\Downloads\", @"C:\Users\Surface\Downloads\" + @"1682575881230001.png"); +Alograms.Noises(testImage); \ No newline at end of file diff --git a/DigitalImageProcessing/Sampler.cs b/DigitalImageProcessing/Sampler.cs new file mode 100644 index 0000000..349e07c --- /dev/null +++ b/DigitalImageProcessing/Sampler.cs @@ -0,0 +1,35 @@ +using SkiaSharp; + +namespace DigitalImageProcessing +{ + internal enum SamplerMethod + { + None = 0, + Repeat = 1, + Mirror = 2, + Clamp = 3, + } + internal class Sampler + { + private SKBitmap bitmap; + public SamplerMethod Method { get; set; } + public Sampler(SKBitmap bitmap) + { + this.bitmap = bitmap; + } + public SKColor this[int x, int y] + { + get + { + return Method switch + { + SamplerMethod.None => bitmap.GetPixel(x, y), + SamplerMethod.Repeat => bitmap.GetPixel((x + bitmap.Width) % bitmap.Width, (y + bitmap.Height) % bitmap.Height), + + SamplerMethod.Clamp => bitmap.GetPixel(int.Clamp(x, 0, bitmap.Width - 1), int.Clamp(y, 0, bitmap.Height - 1)), + SamplerMethod.Mirror or _ => throw new NotImplementedException(), + }; + } + } + } +}