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 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>(new float[1, size])
: new Fast2DArray<float>(new float[size, 1]);
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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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 Fast2DArray<float>(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>(new float[1, size])
: new Fast2DArray<float>(new float[size, 1]);
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>(new float[1, size])
: new Fast2DArray<float>(new float[size, 1]);
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;
}
}

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);

Loading…
Cancel
Save