Browse Source

Merge pull request #108 from JimBobSquarePants/fast2d-convolution

Use Fast2DArray for all convolution algorithms.
af/merge-core
James Jackson-South 9 years ago
committed by GitHub
parent
commit
7044e15e24
  1. 3
      src/ImageSharp.Processing/Convolution/BoxBlur.cs
  2. 30
      src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs
  3. 31
      src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs
  4. 74
      src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs
  5. 29
      src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs
  6. 15
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
  7. 36
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  8. 23
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
  9. 38
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs
  10. 121
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs
  11. 22
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs
  12. 26
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs
  13. 26
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs
  14. 38
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs
  15. 34
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs
  16. 121
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs
  17. 38
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs
  18. 38
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs
  19. 31
      src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs
  20. 33
      src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs
  21. 29
      src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs
  22. 6
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  23. 4
      src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs
  24. 4
      src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs
  25. 4
      src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs
  26. 4
      src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs
  27. 4
      src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs
  28. 4
      src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs
  29. 4
      src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs
  30. 4
      src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs
  31. 4
      src/ImageSharp/Dithering/Ordered/Bayer.cs
  32. 4
      src/ImageSharp/Dithering/Ordered/Ordered.cs
  33. 73
      tests/ImageSharp.Benchmarks/General/Array2D.cs
  34. 28
      tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs

3
src/ImageSharp.Processing/Convolution/BoxBlur.cs

@ -7,7 +7,6 @@ namespace ImageSharp
{
using System;
using Processing;
using Processing.Processors;
/// <summary>
@ -41,7 +40,7 @@ namespace ImageSharp
public static Image<TColor> BoxBlur<TColor>(this Image<TColor> source, int radius, Rectangle rectangle)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
source.ApplyProcessor(new BoxBlurProcessor<TColor>(), rectangle);
source.ApplyProcessor(new BoxBlurProcessor<TColor>(radius), rectangle);
return source;
}
}

30
src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs

@ -35,12 +35,12 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public float[][] KernelX { get; }
public Fast2DArray<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public float[][] KernelY { get; }
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
@ -52,46 +52,42 @@ namespace ImageSharp.Processing.Processors
/// Create a 1 dimensional Box kernel.
/// </summary>
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
/// <returns>The <see cref="T:float[][]"/></returns>
private float[][] CreateBoxKernel(bool horizontal)
/// <returns>The <see cref="Fast2DArray{T}"/></returns>
private Fast2DArray<float> CreateBoxKernel(bool horizontal)
{
int size = this.kernelSize;
float[][] kernel = horizontal ? new float[1][] : new float[size][];
if (horizontal)
{
kernel[0] = new float[size];
}
float sum = 0.0f;
Fast2DArray<float> kernel = horizontal
? new Fast2DArray<float>(size, 1)
: new Fast2DArray<float>(1, size);
float sum = 0F;
for (int i = 0; i < size; i++)
{
float x = 1;
sum += x;
if (horizontal)
{
kernel[0][i] = x;
kernel[0, i] = x;
}
else
{
kernel[i] = new[] { x };
kernel[i, 0] = x;
}
}
// Normalise kernel so that the sum of all weights equals 1
// Normalize kernel so that the sum of all weights equals 1
if (horizontal)
{
for (int i = 0; i < size; i++)
{
kernel[0][i] = kernel[0][i] / sum;
kernel[0, i] = kernel[0, i] / sum;
}
}
else
{
for (int i = 0; i < size; i++)
{
kernel[i][0] = kernel[i][0] / sum;
kernel[i, 0] = kernel[i, 0] / sum;
}
}

31
src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs

@ -21,7 +21,7 @@ namespace ImageSharp.Processing.Processors
/// </summary>
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
public Convolution2DProcessor(float[][] kernelX, float[][] kernelY)
public Convolution2DProcessor(Fast2DArray<float> kernelX, Fast2DArray<float> kernelY)
{
this.KernelX = kernelX;
this.KernelY = kernelY;
@ -30,20 +30,20 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public float[][] KernelX { get; }
public Fast2DArray<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public float[][] KernelY { get; }
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
int kernelYHeight = this.KernelY.Length;
int kernelYWidth = this.KernelY[0].Length;
int kernelXHeight = this.KernelX.Length;
int kernelXWidth = this.KernelX[0].Length;
int kernelYHeight = this.KernelY.Height;
int kernelYWidth = this.KernelY.Width;
int kernelXHeight = this.KernelX.Height;
int kernelXWidth = this.KernelX.Width;
int radiusY = kernelYHeight >> 1;
int radiusX = kernelXWidth >> 1;
@ -89,22 +89,21 @@ namespace ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
if (fy < kernelXHeight)
{
rX += this.KernelX[fy][fx] * r;
gX += this.KernelX[fy][fx] * g;
bX += this.KernelX[fy][fx] * b;
Vector4 kx = this.KernelX[fy, fx] * currentColor;
rX += kx.X;
gX += kx.Y;
bX += kx.Z;
}
if (fx < kernelYWidth)
{
rY += this.KernelY[fy][fx] * r;
gY += this.KernelY[fy][fx] * g;
bY += this.KernelY[fy][fx] * b;
Vector4 ky = this.KernelY[fy, fx] * currentColor;
rY += ky.X;
gY += ky.Y;
bY += ky.Z;
}
}
}

74
src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -14,14 +14,14 @@ namespace ImageSharp.Processing.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public class Convolution2PassProcessor<TColor> : ImageProcessor<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// Initializes a new instance of the <see cref="Convolution2PassProcessor{TColor}"/> class.
/// </summary>
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
public Convolution2PassProcessor(float[][] kernelX, float[][] kernelY)
public Convolution2PassProcessor(Fast2DArray<float> kernelX, Fast2DArray<float> kernelY)
{
this.KernelX = kernelX;
this.KernelY = kernelY;
@ -30,18 +30,16 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public float[][] KernelX { get; }
public Fast2DArray<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public float[][] KernelY { get; }
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
float[][] kernelX = this.KernelX;
float[][] kernelY = this.KernelY;
int width = source.Width;
int height = source.Height;
@ -50,8 +48,8 @@ namespace ImageSharp.Processing.Processors
using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX);
this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY);
this.ApplyConvolution(firstPassPixels, sourcePixels, sourceRectangle, this.KernelX);
this.ApplyConvolution(targetPixels, firstPassPixels, sourceRectangle, this.KernelY);
}
source.SwapPixelsBuffers(targetPixels);
@ -62,18 +60,16 @@ namespace ImageSharp.Processing.Processors
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="targetPixels">The target pixels to apply the process to.</param>
/// <param name="sourcePixels">The source pixels. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="kernel">The kernel operator.</param>
private void ApplyConvolution(int width, int height, PixelAccessor<TColor> targetPixels, PixelAccessor<TColor> sourcePixels, Rectangle sourceRectangle, float[][] kernel)
private void ApplyConvolution(PixelAccessor<TColor> targetPixels, PixelAccessor<TColor> sourcePixels, Rectangle sourceRectangle, Fast2DArray<float> kernel)
{
int kernelHeight = kernel.Length;
int kernelWidth = kernel[0].Length;
int kernelHeight = kernel.Height;
int kernelWidth = kernel.Width;
int radiusY = kernelHeight >> 1;
int radiusX = kernelWidth >> 1;
@ -85,40 +81,40 @@ namespace ImageSharp.Processing.Processors
int maxX = endX - 1;
Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
{
for (int x = startX; x < endX; x++)
startY,
endY,
this.ParallelOptions,
y =>
{
Vector4 destination = default(Vector4);
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++)
for (int x = startX; x < endX; x++)
{
int fyr = fy - radiusY;
int offsetY = y + fyr;
Vector4 destination = default(Vector4);
offsetY = offsetY.Clamp(0, maxY);
for (int fx = 0; fx < kernelWidth; fx++)
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++)
{
int fxr = fx - radiusX;
int offsetX = x + fxr;
int fyr = fy - radiusY;
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
offsetX = offsetX.Clamp(0, maxX);
for (int fx = 0; fx < kernelWidth; fx++)
{
int fxr = fx - radiusX;
int offsetX = x + fxr;
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
destination += kernel[fy][fx] * currentColor;
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
destination += kernel[fy, fx] * currentColor;
}
}
}
TColor packed = default(TColor);
packed.PackFromVector4(destination);
targetPixels[x, y] = packed;
}
});
TColor packed = default(TColor);
packed.PackFromVector4(destination);
targetPixels[x, y] = packed;
}
});
}
}
}

29
src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -14,13 +14,13 @@ namespace ImageSharp.Processing.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public class ConvolutionProcessor<TColor> : ImageProcessor<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// Initializes a new instance of the <see cref="ConvolutionProcessor{TColor}"/> class.
/// </summary>
/// <param name="kernelXY">The 2d gradient operator.</param>
public ConvolutionProcessor(float[][] kernelXY)
public ConvolutionProcessor(Fast2DArray<float> kernelXY)
{
this.KernelXY = kernelXY;
}
@ -28,13 +28,12 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets the 2d gradient operator.
/// </summary>
public virtual float[][] KernelXY { get; }
public Fast2DArray<float> KernelXY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
float[][] kernelX = this.KernelXY;
int kernelLength = kernelX.GetLength(0);
int kernelLength = this.KernelXY.Height;
int radius = kernelLength >> 1;
int startY = sourceRectangle.Y;
@ -56,9 +55,9 @@ namespace ImageSharp.Processing.Processors
{
for (int x = startX; x < endX; x++)
{
float rX = 0;
float gX = 0;
float bX = 0;
float red = 0;
float green = 0;
float blue = 0;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelLength; fy++)
@ -76,20 +75,14 @@ namespace ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
currentColor *= this.KernelXY[fy, fx];
rX += kernelX[fy][fx] * r;
gX += kernelX[fy][fx] * g;
bX += kernelX[fy][fx] * b;
red += currentColor.X;
green += currentColor.Y;
blue += currentColor.Z;
}
}
float red = rX;
float green = gX;
float blue = bX;
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;

15
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs

@ -14,15 +14,26 @@ namespace ImageSharp.Processing.Processors
public abstract class EdgeDetector2DProcessor<TColor> : ImageProcessor<TColor>, IEdgeDetectorProcessor<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// Initializes a new instance of the <see cref="EdgeDetector2DProcessor{TColor}"/> class.
/// </summary>
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
protected EdgeDetector2DProcessor(Fast2DArray<float> kernelX, Fast2DArray<float> kernelY)
{
this.KernelX = kernelX;
this.KernelY = kernelY;
}
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public abstract float[][] KernelX { get; }
public Fast2DArray<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public abstract float[][] KernelY { get; }
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
public bool Grayscale { get; set; }

36
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs

@ -19,50 +19,59 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets the North gradient operator
/// </summary>
public abstract float[][] North { get; }
public abstract Fast2DArray<float> North { get; }
/// <summary>
/// Gets the NorthWest gradient operator
/// </summary>
public abstract float[][] NorthWest { get; }
public abstract Fast2DArray<float> NorthWest { get; }
/// <summary>
/// Gets the West gradient operator
/// </summary>
public abstract float[][] West { get; }
public abstract Fast2DArray<float> West { get; }
/// <summary>
/// Gets the SouthWest gradient operator
/// </summary>
public abstract float[][] SouthWest { get; }
public abstract Fast2DArray<float> SouthWest { get; }
/// <summary>
/// Gets the South gradient operator
/// </summary>
public abstract float[][] South { get; }
public abstract Fast2DArray<float> South { get; }
/// <summary>
/// Gets the SouthEast gradient operator
/// </summary>
public abstract float[][] SouthEast { get; }
public abstract Fast2DArray<float> SouthEast { get; }
/// <summary>
/// Gets the East gradient operator
/// </summary>
public abstract float[][] East { get; }
public abstract Fast2DArray<float> East { get; }
/// <summary>
/// Gets the NorthEast gradient operator
/// </summary>
public abstract float[][] NorthEast { get; }
public abstract Fast2DArray<float> NorthEast { get; }
/// <inheritdoc/>
public bool Grayscale { get; set; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
if (this.Grayscale)
{
new GrayscaleBt709Processor<TColor>().Apply(source, sourceRectangle);
}
}
/// <inheritdoc />
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
float[][][] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast };
Fast2DArray<float>[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast };
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -132,14 +141,5 @@ namespace ImageSharp.Processing.Processors
}
}
}
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
if (this.Grayscale)
{
new GrayscaleBt709Processor<TColor>().Apply(source, sourceRectangle);
}
}
}
}

23
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs

@ -14,19 +14,22 @@ namespace ImageSharp.Processing.Processors
public abstract class EdgeDetectorProcessor<TColor> : ImageProcessor<TColor>, IEdgeDetectorProcessor<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// Initializes a new instance of the <see cref="EdgeDetectorProcessor{TColor}"/> class.
/// </summary>
/// <param name="kernelXY">The 2d gradient operator.</param>
protected EdgeDetectorProcessor(Fast2DArray<float> kernelXY)
{
this.KernelXY = kernelXY;
}
/// <inheritdoc/>
public bool Grayscale { get; set; }
/// <summary>
/// Gets the 2d gradient operator.
/// </summary>
public abstract float[][] KernelXY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
new ConvolutionProcessor<TColor>(this.KernelXY).Apply(source, sourceRectangle);
}
public Fast2DArray<float> KernelXY { get; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TColor> source, Rectangle sourceRectangle)
@ -36,5 +39,11 @@ namespace ImageSharp.Processing.Processors
new GrayscaleBt709Processor<TColor>().Apply(source, sourceRectangle);
}
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
new ConvolutionProcessor<TColor>(this.KernelXY).Apply(source, sourceRectangle);
}
}
}

38
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs

@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The horizontal gradient operator.
/// </summary>
private static readonly float[][] KayyaliX =
{
new float[] { 6, 0, -6 },
new float[] { 0, 0, 0 },
new float[] { -6, 0, 6 }
};
private static readonly Fast2DArray<float> KayyaliX =
new float[,]
{
{ 6, 0, -6 },
{ 0, 0, 0 },
{ -6, 0, 6 }
};
/// <summary>
/// The vertical gradient operator.
/// </summary>
private static readonly float[][] KayyaliY =
{
new float[] { -6, 0, 6 },
new float[] { 0, 0, 0 },
new float[] { 6, 0, -6 }
};
/// <inheritdoc/>
public override float[][] KernelX => KayyaliX;
private static readonly Fast2DArray<float> KayyaliY =
new float[,]
{
{ -6, 0, 6 },
{ 0, 0, 0 },
{ 6, 0, -6 }
};
/// <inheritdoc/>
public override float[][] KernelY => KayyaliY;
/// <summary>
/// Initializes a new instance of the <see cref="KayyaliProcessor{TColor}"/> class.
/// </summary>
public KayyaliProcessor()
: base(KayyaliX, KayyaliY)
{
}
}
}

121
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs

@ -2,6 +2,7 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Processing.Processors
{
using System;
@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The North gradient operator
/// </summary>
private static readonly float[][] KirschNorth =
{
new float[] { 5, 5, 5 },
new float[] { -3, 0, -3 },
new float[] { -3, -3, -3 }
};
private static readonly Fast2DArray<float> KirschNorth =
new float[,]
{
{ 5, 5, 5 },
{ -3, 0, -3 },
{ -3, -3, -3 }
};
/// <summary>
/// The NorthWest gradient operator
/// </summary>
private static readonly float[][] KirschNorthWest =
{
new float[] { 5, 5, -3 },
new float[] { 5, 0, -3 },
new float[] { -3, -3, -3 }
};
private static readonly Fast2DArray<float> KirschNorthWest =
new float[,]
{
{ 5, 5, -3 },
{ 5, 0, -3 },
{ -3, -3, -3 }
};
/// <summary>
/// The West gradient operator
/// </summary>
private static readonly float[][] KirschWest =
{
new float[] { 5, -3, -3 },
new float[] { 5, 0, -3 },
new float[] { 5, -3, -3 }
};
private static readonly Fast2DArray<float> KirschWest =
new float[,]
{
{ 5, -3, -3 },
{ 5, 0, -3 },
{ 5, -3, -3 }
};
/// <summary>
/// The SouthWest gradient operator
/// </summary>
private static readonly float[][] KirschSouthWest =
{
new float[] { -3, -3, -3 },
new float[] { 5, 0, -3 },
new float[] { 5, 5, -3 }
};
private static readonly Fast2DArray<float> KirschSouthWest =
new float[,]
{
{ -3, -3, -3 },
{ 5, 0, -3 },
{ 5, 5, -3 }
};
/// <summary>
/// The South gradient operator
/// </summary>
private static readonly float[][] KirschSouth =
{
new float[] { -3, -3, -3 },
new float[] { -3, 0, -3 },
new float[] { 5, 5, 5 }
};
private static readonly Fast2DArray<float> KirschSouth =
new float[,]
{
{ -3, -3, -3 },
{ -3, 0, -3 },
{ 5, 5, 5 }
};
/// <summary>
/// The SouthEast gradient operator
/// </summary>
private static readonly float[][] KirschSouthEast =
{
new float[] { -3, -3, -3 },
new float[] { -3, 0, 5 },
new float[] { -3, 5, 5 }
};
private static readonly Fast2DArray<float> KirschSouthEast =
new float[,]
{
{ -3, -3, -3 },
{ -3, 0, 5 },
{ -3, 5, 5 }
};
/// <summary>
/// The East gradient operator
/// </summary>
private static readonly float[][] KirschEast =
{
new float[] { -3, -3, 5 },
new float[] { -3, 0, 5 },
new float[] { -3, -3, 5 }
};
private static readonly Fast2DArray<float> KirschEast =
new float[,]
{
{ -3, -3, 5 },
{ -3, 0, 5 },
{ -3, -3, 5 }
};
/// <summary>
/// The NorthEast gradient operator
/// </summary>
private static readonly float[][] KirschNorthEast =
{
new float[] { -3, 5, 5 },
new float[] { -3, 0, 5 },
new float[] { -3, -3, -3 }
};
private static readonly Fast2DArray<float> KirschNorthEast =
new float[,]
{
{ -3, 5, 5 },
{ -3, 0, 5 },
{ -3, -3, -3 }
};
/// <inheritdoc/>
public override float[][] North => KirschNorth;
public override Fast2DArray<float> North => KirschNorth;
/// <inheritdoc/>
public override float[][] NorthWest => KirschNorthWest;
public override Fast2DArray<float> NorthWest => KirschNorthWest;
/// <inheritdoc/>
public override float[][] West => KirschWest;
public override Fast2DArray<float> West => KirschWest;
/// <inheritdoc/>
public override float[][] SouthWest => KirschSouthWest;
public override Fast2DArray<float> SouthWest => KirschSouthWest;
/// <inheritdoc/>
public override float[][] South => KirschSouth;
public override Fast2DArray<float> South => KirschSouth;
/// <inheritdoc/>
public override float[][] SouthEast => KirschSouthEast;
public override Fast2DArray<float> SouthEast => KirschSouthEast;
/// <inheritdoc/>
public override float[][] East => KirschEast;
public override Fast2DArray<float> East => KirschEast;
/// <inheritdoc/>
public override float[][] NorthEast => KirschNorthEast;
public override Fast2DArray<float> NorthEast => KirschNorthEast;
}
}

22
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs

@ -20,14 +20,20 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The 2d gradient operator.
/// </summary>
private static readonly float[][] Laplacian3X3XY = new float[][]
{
new float[] { -1, -1, -1 },
new float[] { -1, 8, -1 },
new float[] { -1, -1, -1 }
};
private static readonly Fast2DArray<float> Laplacian3X3XY =
new float[,]
{
{ -1, -1, -1 },
{ -1, 8, -1 },
{ -1, -1, -1 }
};
/// <inheritdoc/>
public override float[][] KernelXY => Laplacian3X3XY;
/// <summary>
/// Initializes a new instance of the <see cref="Laplacian3X3Processor{TColor}"/> class.
/// </summary>
public Laplacian3X3Processor()
: base(Laplacian3X3XY)
{
}
}
}

26
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs

@ -20,16 +20,22 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The 2d gradient operator.
/// </summary>
private static readonly float[][] Laplacian5X5XY =
{
new float[] { -1, -1, -1, -1, -1 },
new float[] { -1, -1, -1, -1, -1 },
new float[] { -1, -1, 24, -1, -1 },
new float[] { -1, -1, -1, -1, -1 },
new float[] { -1, -1, -1, -1, -1 }
};
private static readonly Fast2DArray<float> Laplacian5X5XY =
new float[,]
{
{ -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1 },
{ -1, -1, 24, -1, -1 },
{ -1, -1, -1, -1, -1 },
{ -1, -1, -1, -1, -1 }
};
/// <inheritdoc/>
public override float[][] KernelXY => Laplacian5X5XY;
/// <summary>
/// Initializes a new instance of the <see cref="Laplacian5X5Processor{TColor}"/> class.
/// </summary>
public Laplacian5X5Processor()
: base(Laplacian5X5XY)
{
}
}
}

26
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs

@ -20,16 +20,22 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The 2d gradient operator.
/// </summary>
private static readonly float[][] LaplacianOfGaussianXY =
{
new float[] { 0, 0, -1, 0, 0 },
new float[] { 0, -1, -2, -1, 0 },
new float[] { -1, -2, 16, -2, -1 },
new float[] { 0, -1, -2, -1, 0 },
new float[] { 0, 0, -1, 0, 0 }
};
private static readonly Fast2DArray<float> LaplacianOfGaussianXY =
new float[,]
{
{ 0, 0, -1, 0, 0 },
{ 0, -1, -2, -1, 0 },
{ -1, -2, 16, -2, -1 },
{ 0, -1, -2, -1, 0 },
{ 0, 0, -1, 0, 0 }
};
/// <inheritdoc/>
public override float[][] KernelXY => LaplacianOfGaussianXY;
/// <summary>
/// Initializes a new instance of the <see cref="LaplacianOfGaussianProcessor{TColor}"/> class.
/// </summary>
public LaplacianOfGaussianProcessor()
: base(LaplacianOfGaussianXY)
{
}
}
}

38
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs

@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The horizontal gradient operator.
/// </summary>
private static readonly float[][] PrewittX =
{
new float[] { -1, 0, 1 },
new float[] { -1, 0, 1 },
new float[] { -1, 0, 1 }
};
private static readonly Fast2DArray<float> PrewittX =
new float[,]
{
{ -1, 0, 1 },
{ -1, 0, 1 },
{ -1, 0, 1 }
};
/// <summary>
/// The vertical gradient operator.
/// </summary>
private static readonly float[][] PrewittY =
{
new float[] { 1, 1, 1 },
new float[] { 0, 0, 0 },
new float[] { -1, -1, -1 }
};
/// <inheritdoc/>
public override float[][] KernelX => PrewittX;
private static readonly Fast2DArray<float> PrewittY =
new float[,]
{
{ 1, 1, 1 },
{ 0, 0, 0 },
{ -1, -1, -1 }
};
/// <inheritdoc/>
public override float[][] KernelY => PrewittY;
/// <summary>
/// Initializes a new instance of the <see cref="PrewittProcessor{TColor}"/> class.
/// </summary>
public PrewittProcessor()
: base(PrewittX, PrewittY)
{
}
}
}

34
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs

@ -20,25 +20,29 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The horizontal gradient operator.
/// </summary>
private static readonly float[][] RobertsCrossX =
{
new float[] { 1, 0 },
new float[] { 0, -1 }
};
private static readonly Fast2DArray<float> RobertsCrossX =
new float[,]
{
{ 1, 0 },
{ 0, -1 }
};
/// <summary>
/// The vertical gradient operator.
/// </summary>
private static readonly float[][] RobertsCrossY =
{
new float[] { 0, 1 },
new float[] { -1, 0 }
};
/// <inheritdoc/>
public override float[][] KernelX => RobertsCrossX;
private static readonly Fast2DArray<float> RobertsCrossY =
new float[,]
{
{ 0, 1 },
{ -1, 0 }
};
/// <inheritdoc/>
public override float[][] KernelY => RobertsCrossY;
/// <summary>
/// Initializes a new instance of the <see cref="RobertsCrossProcessor{TColor}"/> class.
/// </summary>
public RobertsCrossProcessor()
: base(RobertsCrossX, RobertsCrossY)
{
}
}
}

121
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs

@ -2,6 +2,7 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Processing.Processors
{
using System;
@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The North gradient operator
/// </summary>
private static readonly float[][] RobinsonNorth =
{
new float[] { 1, 2, 1 },
new float[] { 0, 0, 0 },
new float[] { -1, -2, -1 }
};
private static readonly Fast2DArray<float> RobinsonNorth =
new float[,]
{
{ 1, 2, 1 },
{ 0, 0, 0 },
{ -1, -2, -1 }
};
/// <summary>
/// The NorthWest gradient operator
/// </summary>
private static readonly float[][] RobinsonNorthWest =
{
new float[] { 2, 1, 0 },
new float[] { 1, 0, -1 },
new float[] { 0, -1, -2 }
};
private static readonly Fast2DArray<float> RobinsonNorthWest =
new float[,]
{
{ 2, 1, 0 },
{ 1, 0, -1 },
{ 0, -1, -2 }
};
/// <summary>
/// The West gradient operator
/// </summary>
private static readonly float[][] RobinsonWest =
{
new float[] { 1, 0, -1 },
new float[] { 2, 0, -2 },
new float[] { 1, 0, -1 }
};
private static readonly Fast2DArray<float> RobinsonWest =
new float[,]
{
{ 1, 0, -1 },
{ 2, 0, -2 },
{ 1, 0, -1 }
};
/// <summary>
/// The SouthWest gradient operator
/// </summary>
private static readonly float[][] RobinsonSouthWest =
{
new float[] { 0, -1, -2 },
new float[] { 1, 0, -1 },
new float[] { 2, 1, 0 }
};
private static readonly Fast2DArray<float> RobinsonSouthWest =
new float[,]
{
{ 0, -1, -2 },
{ 1, 0, -1 },
{ 2, 1, 0 }
};
/// <summary>
/// The South gradient operator
/// </summary>
private static readonly float[][] RobinsonSouth =
{
new float[] { -1, -2, -1 },
new float[] { 0, 0, 0 },
new float[] { 1, 2, 1 }
};
private static readonly Fast2DArray<float> RobinsonSouth =
new float[,]
{
{ -1, -2, -1 },
{ 0, 0, 0 },
{ 1, 2, 1 }
};
/// <summary>
/// The SouthEast gradient operator
/// </summary>
private static readonly float[][] RobinsonSouthEast =
{
new float[] { -2, -1, 0 },
new float[] { -1, 0, 1 },
new float[] { 0, 1, 2 }
};
private static readonly Fast2DArray<float> RobinsonSouthEast =
new float[,]
{
{ -2, -1, 0 },
{ -1, 0, 1 },
{ 0, 1, 2 }
};
/// <summary>
/// The East gradient operator
/// </summary>
private static readonly float[][] RobinsonEast =
{
new float[] { -1, 0, 1 },
new float[] { -2, 0, 2 },
new float[] { -1, 0, 1 }
};
private static readonly Fast2DArray<float> RobinsonEast =
new float[,]
{
{ -1, 0, 1 },
{ -2, 0, 2 },
{ -1, 0, 1 }
};
/// <summary>
/// The NorthEast gradient operator
/// </summary>
private static readonly float[][] RobinsonNorthEast =
{
new float[] { 0, 1, 2 },
new float[] { -1, 0, 1 },
new float[] { -2, -1, 0 }
};
private static readonly Fast2DArray<float> RobinsonNorthEast =
new float[,]
{
{ 0, 1, 2 },
{ -1, 0, 1 },
{ -2, -1, 0 }
};
/// <inheritdoc/>
public override float[][] North => RobinsonNorth;
public override Fast2DArray<float> North => RobinsonNorth;
/// <inheritdoc/>
public override float[][] NorthWest => RobinsonNorthWest;
public override Fast2DArray<float> NorthWest => RobinsonNorthWest;
/// <inheritdoc/>
public override float[][] West => RobinsonWest;
public override Fast2DArray<float> West => RobinsonWest;
/// <inheritdoc/>
public override float[][] SouthWest => RobinsonSouthWest;
public override Fast2DArray<float> SouthWest => RobinsonSouthWest;
/// <inheritdoc/>
public override float[][] South => RobinsonSouth;
public override Fast2DArray<float> South => RobinsonSouth;
/// <inheritdoc/>
public override float[][] SouthEast => RobinsonSouthEast;
public override Fast2DArray<float> SouthEast => RobinsonSouthEast;
/// <inheritdoc/>
public override float[][] East => RobinsonEast;
public override Fast2DArray<float> East => RobinsonEast;
/// <inheritdoc/>
public override float[][] NorthEast => RobinsonNorthEast;
public override Fast2DArray<float> NorthEast => RobinsonNorthEast;
}
}

38
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs

@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The horizontal gradient operator.
/// </summary>
private static readonly float[][] ScharrX = new float[3][]
{
new float[] { -3, 0, 3 },
new float[] { -10, 0, 10 },
new float[] { -3, 0, 3 }
};
private static readonly Fast2DArray<float> ScharrX =
new float[,]
{
{ -3, 0, 3 },
{ -10, 0, 10 },
{ -3, 0, 3 }
};
/// <summary>
/// The vertical gradient operator.
/// </summary>
private static readonly float[][] ScharrY = new float[3][]
{
new float[] { 3, 10, 3 },
new float[] { 0, 0, 0 },
new float[] { -3, -10, -3 }
};
/// <inheritdoc/>
public override float[][] KernelX => ScharrX;
private static readonly Fast2DArray<float> ScharrY =
new float[,]
{
{ 3, 10, 3 },
{ 0, 0, 0 },
{ -3, -10, -3 }
};
/// <inheritdoc/>
public override float[][] KernelY => ScharrY;
/// <summary>
/// Initializes a new instance of the <see cref="ScharrProcessor{TColor}"/> class.
/// </summary>
public ScharrProcessor()
: base(ScharrX, ScharrY)
{
}
}
}

38
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs

@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// The horizontal gradient operator.
/// </summary>
private static readonly float[][] SobelX =
{
new float[] { -1, 0, 1 },
new float[] { -2, 0, 2 },
new float[] { -1, 0, 1 }
};
private static readonly Fast2DArray<float> SobelX =
new float[,]
{
{ -1, 0, 1 },
{ -2, 0, 2 },
{ -1, 0, 1 }
};
/// <summary>
/// The vertical gradient operator.
/// </summary>
private static readonly float[][] SobelY =
{
new float[] { -1, -2, -1 },
new float[] { 0, 0, 0 },
new float[] { 1, 2, 1 }
};
/// <inheritdoc/>
public override float[][] KernelX => SobelX;
private static readonly Fast2DArray<float> SobelY =
new float[,]
{
{ -1, -2, -1 },
{ 0, 0, 0 },
{ 1, 2, 1 }
};
/// <inheritdoc/>
public override float[][] KernelY => SobelY;
/// <summary>
/// Initializes a new instance of the <see cref="SobelProcessor{TColor}"/> class.
/// </summary>
public SobelProcessor()
: base(SobelX, SobelY)
{
}
}
}

31
src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs

@ -71,12 +71,12 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public float[][] KernelX { get; }
public Fast2DArray<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public float[][] KernelY { get; }
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
@ -88,21 +88,18 @@ namespace ImageSharp.Processing.Processors
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
/// <returns>The <see cref="T:float[][]"/></returns>
private float[][] CreateGaussianKernel(bool horizontal)
/// <returns>The <see cref="Fast2DArray{T}"/></returns>
private Fast2DArray<float> CreateGaussianKernel(bool horizontal)
{
int size = this.kernelSize;
float weight = this.sigma;
float[][] kernel = horizontal ? new float[1][] : new float[size][];
Fast2DArray<float> kernel = horizontal
? new Fast2DArray<float>(size, 1)
: new Fast2DArray<float>(1, size);
if (horizontal)
{
kernel[0] = new float[size];
}
float sum = 0.0f;
float sum = 0F;
float midpoint = (size - 1) / 2F;
float midpoint = (size - 1) / 2f;
for (int i = 0; i < size; i++)
{
float x = i - midpoint;
@ -110,27 +107,27 @@ namespace ImageSharp.Processing.Processors
sum += gx;
if (horizontal)
{
kernel[0][i] = gx;
kernel[0, i] = gx;
}
else
{
kernel[i] = new[] { gx };
kernel[i, 0] = gx;
}
}
// Normalise kernel so that the sum of all weights equals 1
// Normalize kernel so that the sum of all weights equals 1
if (horizontal)
{
for (int i = 0; i < size; i++)
{
kernel[0][i] = kernel[0][i] / sum;
kernel[0, i] = kernel[0, i] / sum;
}
}
else
{
for (int i = 0; i < size; i++)
{
kernel[i][0] = kernel[i][0] / sum;
kernel[i, 0] = kernel[i, 0] / sum;
}
}

33
src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs

@ -73,12 +73,12 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public float[][] KernelX { get; }
public Fast2DArray<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public float[][] KernelY { get; }
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
@ -90,17 +90,14 @@ namespace ImageSharp.Processing.Processors
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
/// <returns>The <see cref="T:float[][]"/></returns>
private float[][] CreateGaussianKernel(bool horizontal)
/// <returns>The <see cref="Fast2DArray{T}"/></returns>
private Fast2DArray<float> CreateGaussianKernel(bool horizontal)
{
int size = this.kernelSize;
float weight = this.sigma;
float[][] kernel = horizontal ? new float[1][] : new float[size][];
if (horizontal)
{
kernel[0] = new float[size];
}
Fast2DArray<float> kernel = horizontal
? new Fast2DArray<float>(size, 1)
: new Fast2DArray<float>(1, size);
float sum = 0;
@ -112,11 +109,11 @@ namespace ImageSharp.Processing.Processors
sum += gx;
if (horizontal)
{
kernel[0][i] = gx;
kernel[0, i] = gx;
}
else
{
kernel[i] = new[] { gx };
kernel[i, 0] = gx;
}
}
@ -130,12 +127,12 @@ namespace ImageSharp.Processing.Processors
if (i == midpointRounded)
{
// Calculate central value
kernel[0][i] = (2f * sum) - kernel[0][i];
kernel[0, i] = (2F * sum) - kernel[0, i];
}
else
{
// invert value
kernel[0][i] = -kernel[0][i];
kernel[0, i] = -kernel[0, i];
}
}
}
@ -146,12 +143,12 @@ namespace ImageSharp.Processing.Processors
if (i == midpointRounded)
{
// Calculate central value
kernel[i][0] = (2 * sum) - kernel[i][0];
kernel[i, 0] = (2 * sum) - kernel[i, 0];
}
else
{
// invert value
kernel[i][0] = -kernel[i][0];
kernel[i, 0] = -kernel[i, 0];
}
}
}
@ -161,14 +158,14 @@ namespace ImageSharp.Processing.Processors
{
for (int i = 0; i < size; i++)
{
kernel[0][i] = kernel[0][i] / sum;
kernel[0, i] = kernel[0, i] / sum;
}
}
else
{
for (int i = 0; i < size; i++)
{
kernel[i][0] = kernel[i][0] / sum;
kernel[i, 0] = kernel[i, 0] / sum;
}
}

29
src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs

@ -30,6 +30,22 @@ namespace ImageSharp
/// </summary>
public int Height;
/// <summary>
/// Initializes a new instance of the <see cref="Fast2DArray{T}" /> struct.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public Fast2DArray(int width, int height)
{
this.Height = height;
this.Width = width;
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Data = new T[this.Width * this.Height];
}
/// <summary>
/// Initializes a new instance of the <see cref="Fast2DArray{T}"/> struct.
/// </summary>
@ -77,6 +93,19 @@ namespace ImageSharp
}
}
/// <summary>
/// Performs an implicit conversion from a 2D array to a <see cref="ImageSharp.Fast2DArray{T}" />.
/// </summary>
/// <param name="data">The source array.</param>
/// <returns>
/// The <see cref="ImageSharp.Fast2DArray{T}"/> represenation on the source data.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Fast2DArray<T>(T[,] data)
{
return new Fast2DArray<T>(data);
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>

6
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -37,6 +37,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="int"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
@ -48,6 +49,7 @@ namespace ImageSharp
/// <param name="x">The x provided to G(x).</param>
/// <param name="sigma">The spread of the blur.</param>
/// <returns>The Gaussian G(x)</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Gaussian(float x, float sigma)
{
const float Numerator = 1.0f;
@ -72,6 +74,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float GetBcValue(float x, float b, float c)
{
float temp;
@ -104,6 +107,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float SinC(float x)
{
if (Math.Abs(x) > Constants.Epsilon)
@ -122,6 +126,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="float"/> representing the degree as radians.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DegreesToRadians(float degrees)
{
return degrees * (float)(Math.PI / 180);
@ -139,6 +144,7 @@ namespace ImageSharp
/// <returns>
/// The bounding <see cref="Rectangle"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight)
{
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);

4
src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs

@ -15,12 +15,12 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> AtkinsonMatrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 1, 1 },
{ 1, 1, 1, 0 },
{ 0, 1, 0, 0 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="Atkinson"/> class.

4
src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs

@ -15,11 +15,11 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> BurksMatrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 0, 8, 4 },
{ 2, 4, 8, 4, 2 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="Burks"/> class.

4
src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs

@ -15,11 +15,11 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> FloydSteinbergMatrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 7 },
{ 3, 5, 1 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="FloydSteinberg"/> class.

4
src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs

@ -15,12 +15,12 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> JarvisJudiceNinkeMatrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 0, 7, 5 },
{ 3, 5, 7, 5, 3 },
{ 1, 3, 5, 3, 1 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="JarvisJudiceNinke"/> class.

4
src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs

@ -15,11 +15,11 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> Sierra2Matrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 0, 4, 3 },
{ 1, 2, 3, 2, 1 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="Sierra2"/> class.

4
src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs

@ -15,12 +15,12 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> Sierra3Matrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 0, 5, 3 },
{ 2, 4, 5, 4, 2 },
{ 0, 2, 3, 2, 0 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="Sierra3"/> class.

4
src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs

@ -15,11 +15,11 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> SierraLiteMatrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 2 },
{ 1, 1, 0 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="SierraLite"/> class.

4
src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs

@ -15,12 +15,12 @@ namespace ImageSharp.Dithering
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> StuckiMatrix =
new Fast2DArray<float>(new float[,]
new float[,]
{
{ 0, 0, 0, 8, 4 },
{ 2, 4, 8, 4, 2 },
{ 1, 2, 4, 2, 1 }
});
};
/// <summary>
/// Initializes a new instance of the <see cref="Stucki"/> class.

4
src/ImageSharp/Dithering/Ordered/Bayer.cs

@ -18,13 +18,13 @@ namespace ImageSharp.Dithering.Ordered
/// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1
/// </summary>
private static readonly Fast2DArray<byte> ThresholdMatrix =
new Fast2DArray<byte>(new byte[,]
new byte[,]
{
{ 15, 143, 47, 175 },
{ 207, 79, 239, 111 },
{ 63, 191, 31, 159 },
{ 255, 127, 223, 95 }
});
};
/// <inheritdoc />
public Fast2DArray<byte> Matrix { get; } = ThresholdMatrix;

4
src/ImageSharp/Dithering/Ordered/Ordered.cs

@ -18,13 +18,13 @@ namespace ImageSharp.Dithering.Ordered
/// This is calculated by multiplying each value in the original matrix by 16
/// </summary>
private static readonly Fast2DArray<byte> ThresholdMatrix =
new Fast2DArray<byte>(new byte[,]
new byte[,]
{
{ 0, 128, 32, 160 },
{ 192, 64, 224, 96 },
{ 48, 176, 16, 144 },
{ 240, 112, 208, 80 }
});
};
/// <inheritdoc />
public Fast2DArray<byte> Matrix { get; } = ThresholdMatrix;

73
tests/ImageSharp.Benchmarks/General/Array2D.cs

@ -5,25 +5,31 @@
namespace ImageSharp.Benchmarks.General
{
using System;
using BenchmarkDotNet.Attributes;
public class Array2D
{
private float[,] data;
private float[] flatArray;
private float[,] array2D;
private float[][] jaggedData;
private Fast2DArray<float> fastData;
[Params(10, 100, 1000, 10000)]
[Params(4, 16, 128)]
public int Count { get; set; }
public int Index { get; set; }
public int Min { get; private set; }
public int Max { get; private set; }
[Setup]
public void SetUp()
{
this.data = new float[this.Count, this.Count];
this.flatArray = new float[this.Count * this.Count];
this.array2D = new float[this.Count, this.Count];
this.jaggedData = new float[this.Count][];
for (int i = 0; i < this.Count; i++)
@ -31,27 +37,72 @@ namespace ImageSharp.Benchmarks.General
this.jaggedData[i] = new float[this.Count];
}
this.fastData = new Fast2DArray<float>(this.data);
this.fastData = new Fast2DArray<float>(this.array2D);
this.Index = this.Count / 2;
this.Min = this.Count / 2 - 10;
this.Min = Math.Max(0, this.Min);
this.Max = this.Min + Math.Min(10, this.Count);
}
[Benchmark(Description = "Emulated 2D array access using flat array")]
public float FlatArrayIndex()
{
float[] a = this.flatArray;
float s = 0;
int count = this.Count;
for (int i = this.Min; i < this.Max; i++)
{
for (int j = this.Min; j < this.Max; j++)
{
s += a[count * i + j];
}
}
return s;
}
[Benchmark(Baseline = true, Description = "Array access using 2D array")]
public float ArrayIndex()
public float Array2DIndex()
{
return this.data[this.Index, this.Index];
float s = 0;
float[,] a = this.array2D;
for (int i = this.Min; i < this.Max; i++)
{
for (int j = this.Min; j < this.Max; j++)
{
s += a[i, j];
}
}
return s;
}
[Benchmark(Description = "Array access using a jagged array")]
public float ArrayJaggedIndex()
{
return this.jaggedData[this.Index][this.Index];
float s = 0;
float[][] a = this.jaggedData;
for (int i = this.Min; i < this.Max; i++)
{
for (int j = this.Min; j < this.Max; j++)
{
s += a[i][j];
}
}
return s;
}
[Benchmark(Description = "Array access using Fast2DArray")]
public float ArrayFastIndex()
{
return this.fastData[this.Index, this.Index];
float s = 0;
Fast2DArray<float> a = this.fastData;
for (int i = this.Min; i < this.Max; i++)
{
for (int j = this.Min; j < this.Max; j++)
{
s += a[i, j];
}
}
return s;
}
}
}

28
tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs

@ -21,9 +21,27 @@ namespace ImageSharp.Tests.Common
public void Fast2DArrayThrowsOnNullInitializer()
{
Assert.Throws<ArgumentNullException>(() =>
{
Fast2DArray<float> fast = new Fast2DArray<float>(null);
});
{
Fast2DArray<float> fast = new Fast2DArray<float>(null);
});
}
[Fact]
public void Fast2DArrayThrowsOnEmptyZeroWidth()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Fast2DArray<float> fast = new Fast2DArray<float>(0, 10);
});
}
[Fact]
public void Fast2DArrayThrowsOnEmptyZeroHeight()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Fast2DArray<float> fast = new Fast2DArray<float>(10, 0);
});
}
[Fact]
@ -46,7 +64,7 @@ namespace ImageSharp.Tests.Common
[Fact]
public void Fast2DArrayGetReturnsCorrectResults()
{
Fast2DArray<float> fast = new Fast2DArray<float>(FloydSteinbergMatrix);
Fast2DArray<float> fast = FloydSteinbergMatrix;
for (int row = 0; row < fast.Height; row++)
{
@ -60,7 +78,7 @@ namespace ImageSharp.Tests.Common
[Fact]
public void Fast2DArrayGetSetReturnsCorrectResults()
{
Fast2DArray<float> fast = new Fast2DArray<float>(new float[4, 4]);
Fast2DArray<float> fast = new Fast2DArray<float>(4, 4);
const float Val = 5F;
fast[3, 3] = Val;

Loading…
Cancel
Save