This commit is contained in:
2025-10-28 22:17:09 +08:00
parent 8835968009
commit d19f606b8d
5 changed files with 281 additions and 80 deletions
+124 -52
View File
@@ -61,9 +61,10 @@ namespace DigitalImageProcessing
fequency.DrawTo(outputDirectoryPath + @"fequency.png"); fequency.DrawTo(outputDirectoryPath + @"fequency.png");
angle.DrawTo(outputDirectoryPath + @"angle.png"); angle.DrawTo(outputDirectoryPath + @"angle.png");
} }
public static void Noises(string filepath)
public static SKBitmap GetDefaultBitmap(SKSizeI size)
{ {
using SKBitmap bitmap = new(1024, 1024);//SKBitmap.Decode(filepath); using SKBitmap bitmap = new(size.Width, size.Height);//SKBitmap.Decode(filepath);
using SKCanvas canvas = new SKCanvas(bitmap); using SKCanvas canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Black); canvas.Clear(SKColors.Black);
const int gap = 128; const int gap = 128;
@@ -81,9 +82,16 @@ namespace DigitalImageProcessing
using FileStream stream = new(@"C:\Users\Surface\Downloads\" + @"original.png", FileMode.Create, FileAccess.Write); using FileStream stream = new(@"C:\Users\Surface\Downloads\" + @"original.png", FileMode.Create, FileAccess.Write);
bitmap.Encode(stream, SKEncodedImageFormat.Png, 100); bitmap.Encode(stream, SKEncodedImageFormat.Png, 100);
using SKBitmap noiseBitmap = new(bitmap.Width, bitmap.Height); return bitmap.Copy();
}
public static SKBitmap Noises(SKBitmap input)
{
using SKBitmap bitmap = input.Copy();
SKBitmap noiseBitmap = new(bitmap.Width, bitmap.Height);
using SKCanvas noiseCanvas = new(noiseBitmap); using SKCanvas noiseCanvas = new(noiseBitmap);
Func<byte, byte> noiseFunc = (v) => ApplyGaussianToRandomValue(new(), v, 10f);//NoiseFuncs.Gaussian(v, new Random(), 20f); Func<byte, byte> noiseFunc = (v) => ApplyGaussianToRandomValue(new(), v, 10f);
Func<byte, byte> noiseFunc2 = (v) => NoiseFuncs.Rayleigh(v, new(), 0.01f);
int[] changes = new int[256]; int[] changes = new int[256];
//NoiseFuncs.GetFunc(NoiseType.Gaussian, new Random()); //NoiseFuncs.GetFunc(NoiseType.Gaussian, new Random());
for (int x = 0; x < bitmap.Width; x++) for (int x = 0; x < bitmap.Width; x++)
@@ -97,40 +105,41 @@ namespace DigitalImageProcessing
noised)); noised));
changes[noised]++; changes[noised]++;
} }
using FileStream fs = new(@"C:\Users\Surface\Downloads\" + @"noise.png", FileMode.Create, FileAccess.Write); return noiseBitmap;
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 SKBitmap Filt(SKBitmap input)
{
using SKBitmap bitmap = input.Copy();
SKBitmap filtedBitmap = new(bitmap.Width, bitmap.Height);
using SKCanvas filtedCanvas = new(filtedBitmap);
byte filterFunc(SKPointI p) =>
FilterFuncs.Mean(SKRectI.Create(p, new SKSizeI(4, 4)),
(int x, int y) => Sampler.NearestNeighbor(bitmap, x, y,
(SKColor c) => c.Red)
);
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++)
{
byte gray = filterFunc(new(x, y));
SKColor c = new(gray, gray, gray);
filtedBitmap.SetPixel(x, y, c);
}
return filtedBitmap;
}
public static byte ApplyGaussianToRandomValue(Random random, float mean, float sigma) 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();
double u1 = 1.0 - random.NextDouble(); // Uniform(0,1] random doubles
double u2 = 1.0 - random.NextDouble(); double u2 = 1.0 - random.NextDouble();
double standardNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); 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);
double gaussianValue = mean + sigma * standardNormal;
// Clamp the result to the byte range [0, 255]
return (byte)Math.Clamp((int)gaussianValue, 0, 255); return (byte)Math.Clamp((int)gaussianValue, 0, 255);
} }
} }
@@ -175,11 +184,11 @@ namespace DigitalImageProcessing
//int randomIndex = random.Next(0, 256); //int randomIndex = random.Next(0, 256);
float r = random.NextSingle(); float r = random.NextSingle();
float f = value* GaussianKernel(r * 255, value, sigma); float f = value * GaussianKernel(r * 255, value, sigma);
int newValue = (int)f;//(int)(value + (32 * (f))); int newValue = (int)f;//(int)(value + (32 * (f)));
return (byte)Math.Clamp(newValue, 0, 255); return (byte)Math.Clamp(newValue, 0, 255);
} }
private static float GaussianKernel(float value, float mu, float sigma) public static float GaussianKernel(float value, float mu, float sigma)
{ {
float exp = -(value - mu) * (value - mu) / (2 * sigma * sigma); float exp = -(value - mu) * (value - mu) / (2 * sigma * sigma);
float peak = 1 / (sigma * float.Sqrt(2 * float.Pi)); float peak = 1 / (sigma * float.Sqrt(2 * float.Pi));
@@ -204,26 +213,89 @@ namespace DigitalImageProcessing
return (byte)newValue; return (byte)newValue;
} }
} }
//public static class FilterFuncs public static class FilterFuncs
//{ {
// public static Func<SKBitmap, SKSizeI, byte> GetFunc() //public static Func<SKBitmap, SKSizeI, byte> GetFunc()
// { //{
// } //}
// private static byte Mean(SKBitmap bitmap, SKRectI size, Func<SKColor, byte> channel) public static byte Mean(SKRectI size, Func<int, int, byte> channel)
// { {
// for(int x=size.Left;x<=size.Right;x++) double sum = 0;
// for(int y=size.Top; y <= size.Bottom; y++) 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)); {
byte value = channel(x, y);
// } sum += value;
// } }
// private static byte Median(SKBitmap bitmap, SKRectI size) byte v = (byte)(sum / ((size.Width + 1) * (size.Height + 1)));
// { return v;
// } }
public static byte Median(SKRectI size, Func<int, int, byte> channel)
//} {
byte[] bs = new byte[(size.Width + 1) * (size.Height + 1)];
for (int x = size.Left; x <= size.Right; x++)
for (int y = size.Top; y <= size.Bottom; y++)
{
byte value = channel(x, y);
bs[((y - size.Top) * size.Width) + (x - size.Left)] = value;
}
Array.Sort(bs);
if ((bs.Length & 1) == 0)
return bs[bs.Length / 2];
return (byte)((bs[bs.Length / 2] + bs[(bs.Length / 2) + 1]) / 2);
}
public static byte Gaussian(SKRectI size, Func<int, int, byte> channel)
{
double sum = 0;
double weightSum = 0;
int centerX = size.Left + size.Width / 2;
int centerY = size.Top + size.Height / 2;
float sigma = size.Width / 6f; // Example sigma value
for (int x = size.Left; x <= size.Right; x++)
for (int y = size.Top; y <= size.Bottom; y++)
{
byte value = channel(x, y);
float weight = NoiseFuncs.GaussianKernel(float.Sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)), 0, sigma);
sum += value * weight;
weightSum += weight;
}
byte v = (byte)(sum / weightSum);
return v;
}
}
public static class Sampler
{
public static byte NearestNeighbor(SKBitmap bitmap, float x, float y, Func<SKColor, byte> channel)
{
int ix = (int)MathF.Round(x);
int iy = (int)MathF.Round(y);
ix = Math.Clamp(ix, 0, bitmap.Width - 1);
iy = Math.Clamp(iy, 0, bitmap.Height - 1);
return channel(bitmap.GetPixel(ix, iy));
}
public static byte Bilinear(SKBitmap bitmap, float x, float y, Func<SKColor, byte> channel)
{
int x1 = (int)MathF.Floor(x);
int y1 = (int)MathF.Floor(y);
int x2 = (int)MathF.Ceiling(x);
int y2 = (int)MathF.Ceiling(y);
x1 = Math.Clamp(x1, 0, bitmap.Width - 1);
y1 = Math.Clamp(y1, 0, bitmap.Height - 1);
x2 = Math.Clamp(x2, 0, bitmap.Width - 1);
y2 = Math.Clamp(y2, 0, bitmap.Height - 1);
float fx = x - x1;
float fy = y - y1;
byte c11 = channel(bitmap.GetPixel(x1, y1));
byte c12 = channel(bitmap.GetPixel(x1, y2));
byte c21 = channel(bitmap.GetPixel(x2, y1));
byte c22 = channel(bitmap.GetPixel(x2, y2));
float c1 = (c11 * (1 - fx)) + (c21 * fx);
float c2 = (c12 * (1 - fx)) + (c22 * fx);
float c = (c1 * (1 - fy)) + (c2 * fy);
return (byte)Math.Clamp((int)c, 0, 255);
}
}
public enum NoiseType public enum NoiseType
{ {
SaltAndPepper, SaltAndPepper,
@@ -5,6 +5,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+145 -4
View File
@@ -23,11 +23,152 @@ namespace DigitalImageProcessing
{ {
float color = (values[x, y] - min) / (max - min); float color = (values[x, y] - min) / (max - min);
output.SetPixel(x, y, new SKColor( output.SetPixel(x, y, new SKColor(
(byte)(color * 128 + 128), (byte)((color * 128) + 128),
(byte)(color * 128 + 128), (byte)((color * 128) + 128),
(byte)(color * 128 + 128))); (byte)((color * 128) + 128)));
} }
output.Encode(fs, SKEncodedImageFormat.Png, 100); output.Encode(fs, SKEncodedImageFormat.Png, 100);
} }
} public static void ToCmy(this SKColor color, out byte c, out int m, out int y)
{
c = (byte)(255 - color.Red);
m = (byte)(255 - color.Green);
y = (byte)(255 - color.Blue);
}
public static void FromCmy(this ref SKColor color, byte c, byte m, byte y)
{
color = new SKColor(
(byte)(255 - c),
(byte)(255 - m),
(byte)(255 - y));
}
public static void ToCmyk(this SKColor color, out byte c, out byte m, out byte y, out byte k)
{
k = byte.Min(byte.Min((byte)(255 - color.Red), (byte)(255 - color.Green)), (byte)(255 - color.Blue));
if (k == 255)
{
c = 0;
m = 0;
y = 0;
}
else
{
c = (byte)((255 - color.Red - k) * 255 / (255 - k));
m = (byte)((255 - color.Green - k) * 255 / (255 - k));
y = (byte)((255 - color.Blue - k) * 255 / (255 - k));
}
}
public static void FromCmyk(this ref SKColor color, byte c, byte m, byte y, byte k)
{
color = new SKColor(
(byte)(255 - Math.Min(255, c * (255 - k) / 255 + k)),
(byte)(255 - Math.Min(255, m * (255 - k) / 255 + k)),
(byte)(255 - Math.Min(255, y * (255 - k) / 255 + k)));
}
public static void ToHsi(this SKColor color, out int h, out int s, out int i)
{
float r = color.Red / 255f;
float g = color.Green / 255f;
float b = color.Blue / 255f;
float min = MathF.Min(r, MathF.Min(g, b));
i = (int)(((r + g + b) / 3f) * 255);
if (i == 0)
{
h = 0;
s = 0;
return;
}
s = (int)((1 - (min / (i / 255f))) * 255);
if (s == 0)
{
h = 0;
return;
}
float numerator = 0.5f * ((r - g) + (r - b));
float denominator = MathF.Sqrt((r - g) * (r - g) + (r - b) * (g - b));
float theta = MathF.Acos(numerator / denominator);
if (b <= g)
h = (int)((theta / (2 * MathF.PI)) * 360);
else
h = (int)(((2 * MathF.PI - theta) / (2 * MathF.PI)) * 360);
}
public static void FromHsi(this ref SKColor color, int h, int s, int i)
{
float r = 0;
float g = 0;
float b = 0;
float H = h;
float S = s / 255f;
float I = i / 255f;
if (S == 0)
{
r = I;
g = I;
b = I;
}
else
{
if (H < 120)
{
b = I * (1 - S);
r = I * (1 + (S * MathF.Cos(H * MathF.PI / 180) / MathF.Cos((60 - H) * MathF.PI / 180)));
g = 3 * I - (r + b);
}
else if (H < 240)
{
H = H - 120;
r = I * (1 - S);
g = I * (1 + (S * MathF.Cos(H * MathF.PI / 180) / MathF.Cos((60 - H) * MathF.PI / 180)));
b = 3 * I - (r + g);
}
else
{
H = H - 240;
g = I * (1 - S);
b = I * (1 + (S * MathF.Cos(H * MathF.PI / 180) / MathF.Cos((60 - H) * MathF.PI / 180)));
r = 3 * I - (g + b);
}
}
color = new SKColor(
(byte)(float.Clamp(r * 255, 0, 255)),
(byte)(float.Clamp(g * 255, 0, 255)),
(byte)(float.Clamp(b * 255, 0, 255)));
}
//public static void DrawTo(string filename, params float[][,] values)
//{
// int width = values.Max(i => i.GetLength(0));
// int height = values.Max(i => i.GetLength(1));
// float[] min = new float[values.Length];
// float[] max = new float[values.Length];
// Array.Fill(min, float.MaxValue);
// Array.Fill(max, float.MinValue);
// for (int i = 0; i < values.Length; i++)
// for (int y = 0; y < height; y++)
// for (int x = 0; x < width; x++)
// {
// if (values[i][x, y] < min[i]) min[i] = values[i][x, y];
// if (values[i][x, y] > max[i]) max[i] = values[i][x, y];
// }
// using SKBitmap output = new(width, height);
// using FileStream fs = new(filename, FileMode.Create, FileAccess.Write);
// for (int y = 0; y < height; y++)
// for (int x = 0; x < width; x++)
// {
// double color = (values[x, y] - min) / (max - min);
// output.SetPixel(x, y, new SKColor(
// (byte)((color * 128) + 128),
// (byte)((color * 128) + 128),
// (byte)((color * 128) + 128)));
// }
// output.Encode(fs, SKEncodedImageFormat.Png, 100);
//}
}
} }
+11 -1
View File
@@ -4,4 +4,14 @@ using SkiaSharp;
string testImage = @"C:\Users\Surface\Downloads\" + @"1682575881230001.png"; string testImage = @"C:\Users\Surface\Downloads\" + @"1682575881230001.png";
//Alograms.Steps(@"C:\Users\Surface\Downloads\", @"C:\Users\Surface\Downloads\" + @"1682575881230001.png"); //Alograms.Steps(@"C:\Users\Surface\Downloads\", @"C:\Users\Surface\Downloads\" + @"1682575881230001.png");
Alograms.Noises(testImage); SKBitmap d = Alograms.GetDefaultBitmap(new(1024, 512));
var noised = Alograms.Noises(d);
using FileStream fs = new(@"C:\Users\Surface\Downloads\" + @"noise.png", FileMode.Create, FileAccess.Write);
noised.Encode(fs, SKEncodedImageFormat.Png, 100);
var filtered = Alograms.Filt(d);
using FileStream fs2 = new(@"C:\Users\Surface\Downloads\" + @"filtered.png", FileMode.Create, FileAccess.Write);
filtered.Encode(fs2, SKEncodedImageFormat.Png, 100);
-23
View File
@@ -9,27 +9,4 @@ namespace DigitalImageProcessing
Mirror = 2, Mirror = 2,
Clamp = 3, 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(),
};
}
}
}
} }