This commit is contained in:
2025-10-20 11:14:41 +08:00
parent bf4bc32c55
commit 8835968009
4 changed files with 288 additions and 57 deletions
+238
View File
@@ -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<byte, byte> 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<byte, byte> 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<SKBitmap, SKSizeI, byte> GetFunc()
// {
// }
// private static byte Mean(SKBitmap bitmap, SKRectI size, Func<SKColor, byte> 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,
//}
}
+12
View File
@@ -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
{
}
}
+3 -57
View File
@@ -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);
+35
View File
@@ -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(),
};
}
}
}
}