Browse Source

Use Fast2DArray for all convolution algorithms.

af/merge-core
James Jackson-South 9 years ago
parent
commit
ef09d48c11
  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. 6
      src/ImageSharp/Common/Helpers/ImageMaths.cs

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

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

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

@ -35,12 +35,12 @@ namespace ImageSharp.Processing.Processors
/// <summary> /// <summary>
/// Gets the horizontal gradient operator. /// Gets the horizontal gradient operator.
/// </summary> /// </summary>
public float[][] KernelX { get; } public Fast2DArray<float> KernelX { get; }
/// <summary> /// <summary>
/// Gets the vertical gradient operator. /// Gets the vertical gradient operator.
/// </summary> /// </summary>
public float[][] KernelY { get; } public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
@ -52,46 +52,42 @@ namespace ImageSharp.Processing.Processors
/// Create a 1 dimensional Box kernel. /// Create a 1 dimensional Box kernel.
/// </summary> /// </summary>
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param> /// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
/// <returns>The <see cref="T:float[][]"/></returns> /// <returns>The <see cref="Fast2DArray{T}"/></returns>
private float[][] CreateBoxKernel(bool horizontal) private Fast2DArray<float> CreateBoxKernel(bool horizontal)
{ {
int size = this.kernelSize; int size = this.kernelSize;
float[][] kernel = horizontal ? new float[1][] : new float[size][]; Fast2DArray<float> kernel = horizontal
? new Fast2DArray<float>(new float[1, size])
if (horizontal) : new Fast2DArray<float>(new float[size, 1]);
{
kernel[0] = new float[size];
}
float sum = 0.0f;
float sum = 0F;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
float x = 1; float x = 1;
sum += x; sum += x;
if (horizontal) if (horizontal)
{ {
kernel[0][i] = x; kernel[0, i] = x;
} }
else 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) if (horizontal)
{ {
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
kernel[0][i] = kernel[0][i] / sum; kernel[0, i] = kernel[0, i] / sum;
} }
} }
else else
{ {
for (int i = 0; i < size; i++) 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> /// </summary>
/// <param name="kernelX">The horizontal gradient operator.</param> /// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical 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.KernelX = kernelX;
this.KernelY = kernelY; this.KernelY = kernelY;
@ -30,20 +30,20 @@ namespace ImageSharp.Processing.Processors
/// <summary> /// <summary>
/// Gets the horizontal gradient operator. /// Gets the horizontal gradient operator.
/// </summary> /// </summary>
public float[][] KernelX { get; } public Fast2DArray<float> KernelX { get; }
/// <summary> /// <summary>
/// Gets the vertical gradient operator. /// Gets the vertical gradient operator.
/// </summary> /// </summary>
public float[][] KernelY { get; } public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{ {
int kernelYHeight = this.KernelY.Length; int kernelYHeight = this.KernelY.Height;
int kernelYWidth = this.KernelY[0].Length; int kernelYWidth = this.KernelY.Width;
int kernelXHeight = this.KernelX.Length; int kernelXHeight = this.KernelX.Height;
int kernelXWidth = this.KernelX[0].Length; int kernelXWidth = this.KernelX.Width;
int radiusY = kernelYHeight >> 1; int radiusY = kernelYHeight >> 1;
int radiusX = kernelXWidth >> 1; int radiusX = kernelXWidth >> 1;
@ -89,22 +89,21 @@ namespace ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
if (fy < kernelXHeight) if (fy < kernelXHeight)
{ {
rX += this.KernelX[fy][fx] * r; Vector4 kx = this.KernelX[fy, fx] * currentColor;
gX += this.KernelX[fy][fx] * g; rX += kx.X;
bX += this.KernelX[fy][fx] * b; gX += kx.Y;
bX += kx.Z;
} }
if (fx < kernelYWidth) if (fx < kernelYWidth)
{ {
rY += this.KernelY[fy][fx] * r; Vector4 ky = this.KernelY[fy, fx] * currentColor;
gY += this.KernelY[fy][fx] * g; rY += ky.X;
bY += this.KernelY[fy][fx] * b; gY += ky.Y;
bY += ky.Z;
} }
} }
} }

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

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

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

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

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

@ -19,50 +19,59 @@ namespace ImageSharp.Processing.Processors
/// <summary> /// <summary>
/// Gets the North gradient operator /// Gets the North gradient operator
/// </summary> /// </summary>
public abstract float[][] North { get; } public abstract Fast2DArray<float> North { get; }
/// <summary> /// <summary>
/// Gets the NorthWest gradient operator /// Gets the NorthWest gradient operator
/// </summary> /// </summary>
public abstract float[][] NorthWest { get; } public abstract Fast2DArray<float> NorthWest { get; }
/// <summary> /// <summary>
/// Gets the West gradient operator /// Gets the West gradient operator
/// </summary> /// </summary>
public abstract float[][] West { get; } public abstract Fast2DArray<float> West { get; }
/// <summary> /// <summary>
/// Gets the SouthWest gradient operator /// Gets the SouthWest gradient operator
/// </summary> /// </summary>
public abstract float[][] SouthWest { get; } public abstract Fast2DArray<float> SouthWest { get; }
/// <summary> /// <summary>
/// Gets the South gradient operator /// Gets the South gradient operator
/// </summary> /// </summary>
public abstract float[][] South { get; } public abstract Fast2DArray<float> South { get; }
/// <summary> /// <summary>
/// Gets the SouthEast gradient operator /// Gets the SouthEast gradient operator
/// </summary> /// </summary>
public abstract float[][] SouthEast { get; } public abstract Fast2DArray<float> SouthEast { get; }
/// <summary> /// <summary>
/// Gets the East gradient operator /// Gets the East gradient operator
/// </summary> /// </summary>
public abstract float[][] East { get; } public abstract Fast2DArray<float> East { get; }
/// <summary> /// <summary>
/// Gets the NorthEast gradient operator /// Gets the NorthEast gradient operator
/// </summary> /// </summary>
public abstract float[][] NorthEast { get; } public abstract Fast2DArray<float> NorthEast { get; }
/// <inheritdoc/> /// <inheritdoc/>
public bool Grayscale { get; set; } 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 /> /// <inheritdoc />
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) 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 startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom; 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> public abstract class EdgeDetectorProcessor<TColor> : ImageProcessor<TColor>, IEdgeDetectorProcessor<TColor>
where TColor : struct, IPackedPixel, IEquatable<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/> /// <inheritdoc/>
public bool Grayscale { get; set; } public bool Grayscale { get; set; }
/// <summary> /// <summary>
/// Gets the 2d gradient operator. /// Gets the 2d gradient operator.
/// </summary> /// </summary>
public abstract float[][] KernelXY { get; } public Fast2DArray<float> KernelXY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
new ConvolutionProcessor<TColor>(this.KernelXY).Apply(source, sourceRectangle);
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeApply(ImageBase<TColor> source, Rectangle sourceRectangle) protected override void BeforeApply(ImageBase<TColor> source, Rectangle sourceRectangle)
@ -36,5 +39,11 @@ namespace ImageSharp.Processing.Processors
new GrayscaleBt709Processor<TColor>().Apply(source, sourceRectangle); 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> /// <summary>
/// The horizontal gradient operator. /// The horizontal gradient operator.
/// </summary> /// </summary>
private static readonly float[][] KayyaliX = private static readonly Fast2DArray<float> KayyaliX =
{ new Fast2DArray<float>(new float[,]
new float[] { 6, 0, -6 }, {
new float[] { 0, 0, 0 }, { 6, 0, -6 },
new float[] { -6, 0, 6 } { 0, 0, 0 },
}; { -6, 0, 6 }
});
/// <summary> /// <summary>
/// The vertical gradient operator. /// The vertical gradient operator.
/// </summary> /// </summary>
private static readonly float[][] KayyaliY = private static readonly Fast2DArray<float> KayyaliY =
{ new Fast2DArray<float>(new float[,]
new float[] { -6, 0, 6 }, {
new float[] { 0, 0, 0 }, { -6, 0, 6 },
new float[] { 6, 0, -6 } { 0, 0, 0 },
}; { 6, 0, -6 }
});
/// <inheritdoc/>
public override float[][] KernelX => KayyaliX;
/// <inheritdoc/> /// <summary>
public override float[][] KernelY => KayyaliY; /// 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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Processing.Processors namespace ImageSharp.Processing.Processors
{ {
using System; using System;
@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors
/// <summary> /// <summary>
/// The North gradient operator /// The North gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschNorth = private static readonly Fast2DArray<float> KirschNorth =
{ new Fast2DArray<float>(new float[,]
new float[] { 5, 5, 5 }, {
new float[] { -3, 0, -3 }, { 5, 5, 5 },
new float[] { -3, -3, -3 } { -3, 0, -3 },
}; { -3, -3, -3 }
});
/// <summary> /// <summary>
/// The NorthWest gradient operator /// The NorthWest gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschNorthWest = private static readonly Fast2DArray<float> KirschNorthWest =
{ new Fast2DArray<float>(new float[,]
new float[] { 5, 5, -3 }, {
new float[] { 5, 0, -3 }, { 5, 5, -3 },
new float[] { -3, -3, -3 } { 5, 0, -3 },
}; { -3, -3, -3 }
});
/// <summary> /// <summary>
/// The West gradient operator /// The West gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschWest = private static readonly Fast2DArray<float> KirschWest =
{ new Fast2DArray<float>(new float[,]
new float[] { 5, -3, -3 }, {
new float[] { 5, 0, -3 }, { 5, -3, -3 },
new float[] { 5, -3, -3 } { 5, 0, -3 },
}; { 5, -3, -3 }
});
/// <summary> /// <summary>
/// The SouthWest gradient operator /// The SouthWest gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschSouthWest = private static readonly Fast2DArray<float> KirschSouthWest =
{ new Fast2DArray<float>(new float[,]
new float[] { -3, -3, -3 }, {
new float[] { 5, 0, -3 }, { -3, -3, -3 },
new float[] { 5, 5, -3 } { 5, 0, -3 },
}; { 5, 5, -3 }
});
/// <summary> /// <summary>
/// The South gradient operator /// The South gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschSouth = private static readonly Fast2DArray<float> KirschSouth =
{ new Fast2DArray<float>(new float[,]
new float[] { -3, -3, -3 }, {
new float[] { -3, 0, -3 }, { -3, -3, -3 },
new float[] { 5, 5, 5 } { -3, 0, -3 },
}; { 5, 5, 5 }
});
/// <summary> /// <summary>
/// The SouthEast gradient operator /// The SouthEast gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschSouthEast = private static readonly Fast2DArray<float> KirschSouthEast =
{ new Fast2DArray<float>(new float[,]
new float[] { -3, -3, -3 }, {
new float[] { -3, 0, 5 }, { -3, -3, -3 },
new float[] { -3, 5, 5 } { -3, 0, 5 },
}; { -3, 5, 5 }
});
/// <summary> /// <summary>
/// The East gradient operator /// The East gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschEast = private static readonly Fast2DArray<float> KirschEast =
{ new Fast2DArray<float>(new float[,]
new float[] { -3, -3, 5 }, {
new float[] { -3, 0, 5 }, { -3, -3, 5 },
new float[] { -3, -3, 5 } { -3, 0, 5 },
}; { -3, -3, 5 }
});
/// <summary> /// <summary>
/// The NorthEast gradient operator /// The NorthEast gradient operator
/// </summary> /// </summary>
private static readonly float[][] KirschNorthEast = private static readonly Fast2DArray<float> KirschNorthEast =
{ new Fast2DArray<float>(new float[,]
new float[] { -3, 5, 5 }, {
new float[] { -3, 0, 5 }, { -3, 5, 5 },
new float[] { -3, -3, -3 } { -3, 0, 5 },
}; { -3, -3, -3 }
});
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] North => KirschNorth; public override Fast2DArray<float> North => KirschNorth;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] NorthWest => KirschNorthWest; public override Fast2DArray<float> NorthWest => KirschNorthWest;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] West => KirschWest; public override Fast2DArray<float> West => KirschWest;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] SouthWest => KirschSouthWest; public override Fast2DArray<float> SouthWest => KirschSouthWest;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] South => KirschSouth; public override Fast2DArray<float> South => KirschSouth;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] SouthEast => KirschSouthEast; public override Fast2DArray<float> SouthEast => KirschSouthEast;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] East => KirschEast; public override Fast2DArray<float> East => KirschEast;
/// <inheritdoc/> /// <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> /// <summary>
/// The 2d gradient operator. /// The 2d gradient operator.
/// </summary> /// </summary>
private static readonly float[][] Laplacian3X3XY = new float[][] private static readonly Fast2DArray<float> Laplacian3X3XY =
{ new Fast2DArray<float>(new float[,]
new float[] { -1, -1, -1 }, {
new float[] { -1, 8, -1 }, { -1, -1, -1 },
new float[] { -1, -1, -1 } { -1, 8, -1 },
}; { -1, -1, -1 }
});
/// <inheritdoc/> /// <summary>
public override float[][] KernelXY => Laplacian3X3XY; /// 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> /// <summary>
/// The 2d gradient operator. /// The 2d gradient operator.
/// </summary> /// </summary>
private static readonly float[][] Laplacian5X5XY = private static readonly Fast2DArray<float> Laplacian5X5XY =
{ new Fast2DArray<float>(new float[,]
new float[] { -1, -1, -1, -1, -1 }, {
new float[] { -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1 },
new float[] { -1, -1, 24, -1, -1 }, { -1, -1, -1, -1, -1 },
new float[] { -1, -1, -1, -1, -1 }, { -1, -1, 24, -1, -1 },
new float[] { -1, -1, -1, -1, -1 } { -1, -1, -1, -1, -1 },
}; { -1, -1, -1, -1, -1 }
});
/// <inheritdoc/> /// <summary>
public override float[][] KernelXY => Laplacian5X5XY; /// 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> /// <summary>
/// The 2d gradient operator. /// The 2d gradient operator.
/// </summary> /// </summary>
private static readonly float[][] LaplacianOfGaussianXY = private static readonly Fast2DArray<float> LaplacianOfGaussianXY =
{ new Fast2DArray<float>(new float[,]
new float[] { 0, 0, -1, 0, 0 }, {
new float[] { 0, -1, -2, -1, 0 }, { 0, 0, -1, 0, 0 },
new float[] { -1, -2, 16, -2, -1 }, { 0, -1, -2, -1, 0 },
new float[] { 0, -1, -2, -1, 0 }, { -1, -2, 16, -2, -1 },
new float[] { 0, 0, -1, 0, 0 } { 0, -1, -2, -1, 0 },
}; { 0, 0, -1, 0, 0 }
});
/// <inheritdoc/> /// <summary>
public override float[][] KernelXY => LaplacianOfGaussianXY; /// 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> /// <summary>
/// The horizontal gradient operator. /// The horizontal gradient operator.
/// </summary> /// </summary>
private static readonly float[][] PrewittX = private static readonly Fast2DArray<float> PrewittX =
{ new Fast2DArray<float>(new float[,]
new float[] { -1, 0, 1 }, {
new float[] { -1, 0, 1 }, { -1, 0, 1 },
new float[] { -1, 0, 1 } { -1, 0, 1 },
}; { -1, 0, 1 }
});
/// <summary> /// <summary>
/// The vertical gradient operator. /// The vertical gradient operator.
/// </summary> /// </summary>
private static readonly float[][] PrewittY = private static readonly Fast2DArray<float> PrewittY =
{ new Fast2DArray<float>(new float[,]
new float[] { 1, 1, 1 }, {
new float[] { 0, 0, 0 }, { 1, 1, 1 },
new float[] { -1, -1, -1 } { 0, 0, 0 },
}; { -1, -1, -1 }
});
/// <inheritdoc/>
public override float[][] KernelX => PrewittX;
/// <inheritdoc/> /// <summary>
public override float[][] KernelY => PrewittY; /// 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> /// <summary>
/// The horizontal gradient operator. /// The horizontal gradient operator.
/// </summary> /// </summary>
private static readonly float[][] RobertsCrossX = private static readonly Fast2DArray<float> RobertsCrossX =
{ new Fast2DArray<float>(new float[,]
new float[] { 1, 0 }, {
new float[] { 0, -1 } { 1, 0 },
}; { 0, -1 }
});
/// <summary> /// <summary>
/// The vertical gradient operator. /// The vertical gradient operator.
/// </summary> /// </summary>
private static readonly float[][] RobertsCrossY = private static readonly Fast2DArray<float> RobertsCrossY =
{ new Fast2DArray<float>(new float[,]
new float[] { 0, 1 }, {
new float[] { -1, 0 } { 0, 1 },
}; { -1, 0 }
});
/// <inheritdoc/>
public override float[][] KernelX => RobertsCrossX;
/// <inheritdoc/> /// <summary>
public override float[][] KernelY => RobertsCrossY; /// 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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Processing.Processors namespace ImageSharp.Processing.Processors
{ {
using System; using System;
@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors
/// <summary> /// <summary>
/// The North gradient operator /// The North gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonNorth = private static readonly Fast2DArray<float> RobinsonNorth =
{ new Fast2DArray<float>(new float[,]
new float[] { 1, 2, 1 }, {
new float[] { 0, 0, 0 }, { 1, 2, 1 },
new float[] { -1, -2, -1 } { 0, 0, 0 },
}; { -1, -2, -1 }
});
/// <summary> /// <summary>
/// The NorthWest gradient operator /// The NorthWest gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonNorthWest = private static readonly Fast2DArray<float> RobinsonNorthWest =
{ new Fast2DArray<float>(new float[,]
new float[] { 2, 1, 0 }, {
new float[] { 1, 0, -1 }, { 2, 1, 0 },
new float[] { 0, -1, -2 } { 1, 0, -1 },
}; { 0, -1, -2 }
});
/// <summary> /// <summary>
/// The West gradient operator /// The West gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonWest = private static readonly Fast2DArray<float> RobinsonWest =
{ new Fast2DArray<float>(new float[,]
new float[] { 1, 0, -1 }, {
new float[] { 2, 0, -2 }, { 1, 0, -1 },
new float[] { 1, 0, -1 } { 2, 0, -2 },
}; { 1, 0, -1 }
});
/// <summary> /// <summary>
/// The SouthWest gradient operator /// The SouthWest gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonSouthWest = private static readonly Fast2DArray<float> RobinsonSouthWest =
{ new Fast2DArray<float>(new float[,]
new float[] { 0, -1, -2 }, {
new float[] { 1, 0, -1 }, { 0, -1, -2 },
new float[] { 2, 1, 0 } { 1, 0, -1 },
}; { 2, 1, 0 }
});
/// <summary> /// <summary>
/// The South gradient operator /// The South gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonSouth = private static readonly Fast2DArray<float> RobinsonSouth =
{ new Fast2DArray<float>(new float[,]
new float[] { -1, -2, -1 }, {
new float[] { 0, 0, 0 }, { -1, -2, -1 },
new float[] { 1, 2, 1 } { 0, 0, 0 },
}; { 1, 2, 1 }
});
/// <summary> /// <summary>
/// The SouthEast gradient operator /// The SouthEast gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonSouthEast = private static readonly Fast2DArray<float> RobinsonSouthEast =
{ new Fast2DArray<float>(new float[,]
new float[] { -2, -1, 0 }, {
new float[] { -1, 0, 1 }, { -2, -1, 0 },
new float[] { 0, 1, 2 } { -1, 0, 1 },
}; { 0, 1, 2 }
});
/// <summary> /// <summary>
/// The East gradient operator /// The East gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonEast = private static readonly Fast2DArray<float> RobinsonEast =
{ new Fast2DArray<float>(new float[,]
new float[] { -1, 0, 1 }, {
new float[] { -2, 0, 2 }, { -1, 0, 1 },
new float[] { -1, 0, 1 } { -2, 0, 2 },
}; { -1, 0, 1 }
});
/// <summary> /// <summary>
/// The NorthEast gradient operator /// The NorthEast gradient operator
/// </summary> /// </summary>
private static readonly float[][] RobinsonNorthEast = private static readonly Fast2DArray<float> RobinsonNorthEast =
{ new Fast2DArray<float>(new float[,]
new float[] { 0, 1, 2 }, {
new float[] { -1, 0, 1 }, { 0, 1, 2 },
new float[] { -2, -1, 0 } { -1, 0, 1 },
}; { -2, -1, 0 }
});
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] North => RobinsonNorth; public override Fast2DArray<float> North => RobinsonNorth;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] NorthWest => RobinsonNorthWest; public override Fast2DArray<float> NorthWest => RobinsonNorthWest;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] West => RobinsonWest; public override Fast2DArray<float> West => RobinsonWest;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] SouthWest => RobinsonSouthWest; public override Fast2DArray<float> SouthWest => RobinsonSouthWest;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] South => RobinsonSouth; public override Fast2DArray<float> South => RobinsonSouth;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] SouthEast => RobinsonSouthEast; public override Fast2DArray<float> SouthEast => RobinsonSouthEast;
/// <inheritdoc/> /// <inheritdoc/>
public override float[][] East => RobinsonEast; public override Fast2DArray<float> East => RobinsonEast;
/// <inheritdoc/> /// <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> /// <summary>
/// The horizontal gradient operator. /// The horizontal gradient operator.
/// </summary> /// </summary>
private static readonly float[][] ScharrX = new float[3][] private static readonly Fast2DArray<float> ScharrX =
{ new Fast2DArray<float>(new float[,]
new float[] { -3, 0, 3 }, {
new float[] { -10, 0, 10 }, { -3, 0, 3 },
new float[] { -3, 0, 3 } { -10, 0, 10 },
}; { -3, 0, 3 }
});
/// <summary> /// <summary>
/// The vertical gradient operator. /// The vertical gradient operator.
/// </summary> /// </summary>
private static readonly float[][] ScharrY = new float[3][] private static readonly Fast2DArray<float> ScharrY =
{ new Fast2DArray<float>(new float[,]
new float[] { 3, 10, 3 }, {
new float[] { 0, 0, 0 }, { 3, 10, 3 },
new float[] { -3, -10, -3 } { 0, 0, 0 },
}; { -3, -10, -3 }
});
/// <inheritdoc/>
public override float[][] KernelX => ScharrX;
/// <inheritdoc/> /// <summary>
public override float[][] KernelY => ScharrY; /// 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> /// <summary>
/// The horizontal gradient operator. /// The horizontal gradient operator.
/// </summary> /// </summary>
private static readonly float[][] SobelX = private static readonly Fast2DArray<float> SobelX =
{ new Fast2DArray<float>(new float[,]
new float[] { -1, 0, 1 }, {
new float[] { -2, 0, 2 }, { -1, 0, 1 },
new float[] { -1, 0, 1 } { -2, 0, 2 },
}; { -1, 0, 1 }
});
/// <summary> /// <summary>
/// The vertical gradient operator. /// The vertical gradient operator.
/// </summary> /// </summary>
private static readonly float[][] SobelY = private static readonly Fast2DArray<float> SobelY =
{ new Fast2DArray<float>(new float[,]
new float[] { -1, -2, -1 }, {
new float[] { 0, 0, 0 }, { -1, -2, -1 },
new float[] { 1, 2, 1 } { 0, 0, 0 },
}; { 1, 2, 1 }
});
/// <inheritdoc/>
public override float[][] KernelX => SobelX;
/// <inheritdoc/> /// <summary>
public override float[][] KernelY => SobelY; /// 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> /// <summary>
/// Gets the horizontal gradient operator. /// Gets the horizontal gradient operator.
/// </summary> /// </summary>
public float[][] KernelX { get; } public Fast2DArray<float> KernelX { get; }
/// <summary> /// <summary>
/// Gets the vertical gradient operator. /// Gets the vertical gradient operator.
/// </summary> /// </summary>
public float[][] KernelY { get; } public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) 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 /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary> /// </summary>
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param> /// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
/// <returns>The <see cref="T:float[][]"/></returns> /// <returns>The <see cref="Fast2DArray{T}"/></returns>
private float[][] CreateGaussianKernel(bool horizontal) private Fast2DArray<float> CreateGaussianKernel(bool horizontal)
{ {
int size = this.kernelSize; int size = this.kernelSize;
float weight = this.sigma; float weight = this.sigma;
float[][] kernel = horizontal ? new float[1][] : new float[size][]; Fast2DArray<float> kernel = horizontal
? new Fast2DArray<float>(new float[1, size])
: new Fast2DArray<float>(new float[size, 1]);
if (horizontal) float sum = 0F;
{ float midpoint = (size - 1) / 2F;
kernel[0] = new float[size];
}
float sum = 0.0f;
float midpoint = (size - 1) / 2f;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
float x = i - midpoint; float x = i - midpoint;
@ -110,27 +107,27 @@ namespace ImageSharp.Processing.Processors
sum += gx; sum += gx;
if (horizontal) if (horizontal)
{ {
kernel[0][i] = gx; kernel[0, i] = gx;
} }
else 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) if (horizontal)
{ {
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
kernel[0][i] = kernel[0][i] / sum; kernel[0, i] = kernel[0, i] / sum;
} }
} }
else else
{ {
for (int i = 0; i < size; i++) 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> /// <summary>
/// Gets the horizontal gradient operator. /// Gets the horizontal gradient operator.
/// </summary> /// </summary>
public float[][] KernelX { get; } public Fast2DArray<float> KernelX { get; }
/// <summary> /// <summary>
/// Gets the vertical gradient operator. /// Gets the vertical gradient operator.
/// </summary> /// </summary>
public float[][] KernelY { get; } public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) 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 /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary> /// </summary>
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param> /// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
/// <returns>The <see cref="T:float[][]"/></returns> /// <returns>The <see cref="Fast2DArray{T}"/></returns>
private float[][] CreateGaussianKernel(bool horizontal) private Fast2DArray<float> CreateGaussianKernel(bool horizontal)
{ {
int size = this.kernelSize; int size = this.kernelSize;
float weight = this.sigma; float weight = this.sigma;
float[][] kernel = horizontal ? new float[1][] : new float[size][]; Fast2DArray<float> kernel = horizontal
? new Fast2DArray<float>(new float[1, size])
if (horizontal) : new Fast2DArray<float>(new float[size, 1]);
{
kernel[0] = new float[size];
}
float sum = 0; float sum = 0;
@ -112,11 +109,11 @@ namespace ImageSharp.Processing.Processors
sum += gx; sum += gx;
if (horizontal) if (horizontal)
{ {
kernel[0][i] = gx; kernel[0, i] = gx;
} }
else else
{ {
kernel[i] = new[] { gx }; kernel[i, 0] = gx;
} }
} }
@ -130,12 +127,12 @@ namespace ImageSharp.Processing.Processors
if (i == midpointRounded) if (i == midpointRounded)
{ {
// Calculate central value // Calculate central value
kernel[0][i] = (2f * sum) - kernel[0][i]; kernel[0, i] = (2F * sum) - kernel[0, i];
} }
else else
{ {
// invert value // 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) if (i == midpointRounded)
{ {
// Calculate central value // Calculate central value
kernel[i][0] = (2 * sum) - kernel[i][0]; kernel[i, 0] = (2 * sum) - kernel[i, 0];
} }
else else
{ {
// invert value // 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++) for (int i = 0; i < size; i++)
{ {
kernel[0][i] = kernel[0][i] / sum; kernel[0, i] = kernel[0, i] / sum;
} }
} }
else else
{ {
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
kernel[i][0] = kernel[i][0] / sum; kernel[i, 0] = kernel[i, 0] / sum;
} }
} }

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

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

Loading…
Cancel
Save