Browse Source

Don't create a copy if you dont need to.

ImageFilter = NoCopy
ImageSampler = Copy


Former-commit-id: 530cc95d3b655175999dc78d95fea988f25416e6
Former-commit-id: 63650e982a0a2bb116a473816692187b186a4d0f
Former-commit-id: fe8e7020aa116899a2ae1620b4c78b446a3510e7
af/merge-core
James Jackson-South 10 years ago
parent
commit
8c84d41b26
  1. 2
      src/ImageProcessorCore/Filters/Blend.cs
  2. 2
      src/ImageProcessorCore/Filters/ColorBlindness.cs
  3. 4
      src/ImageProcessorCore/Filters/Grayscale.cs
  4. 27
      src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs
  5. 47
      src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs
  6. 31
      src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs
  7. 7
      src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
  8. 11
      src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs
  9. 9
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
  10. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs
  11. 4
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs
  12. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
  13. 13
      src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs
  14. 12
      src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs
  15. 31
      src/ImageProcessorCore/Filters/Processors/IImageFilter.cs
  16. 68
      src/ImageProcessorCore/Filters/Processors/ImageFilter.cs
  17. 12
      src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs
  18. 9
      src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs
  19. 47
      src/ImageProcessorCore/Image/IImageProcessor.cs
  20. 74
      src/ImageProcessorCore/Image/ImageIOExtensions.cs
  21. 104
      src/ImageProcessorCore/Image/ImageProcessingExtensions.cs
  22. 125
      src/ImageProcessorCore/ImageProcessor.cs
  23. 0
      src/ImageProcessorCore/Samplers/BoxBlur.cs
  24. 0
      src/ImageProcessorCore/Samplers/DetectEdges.cs
  25. 0
      src/ImageProcessorCore/Samplers/GuassianBlur.cs
  26. 0
      src/ImageProcessorCore/Samplers/GuassianSharpen.cs
  27. 0
      src/ImageProcessorCore/Samplers/Pixelate.cs
  28. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/BoxBlurProcessor.cs
  29. 2
      src/ImageProcessorCore/Samplers/Processors/Convolution/Convolution2DFilter.cs
  30. 2
      src/ImageProcessorCore/Samplers/Processors/Convolution/Convolution2PassFilter.cs
  31. 2
      src/ImageProcessorCore/Samplers/Processors/Convolution/ConvolutionFilter.cs
  32. 2
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs
  33. 2
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs
  34. 2
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs
  35. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs
  36. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/KirschProcessor.cs
  37. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs
  38. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs
  39. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs
  40. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/PrewittProcessor.cs
  41. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs
  42. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/ScharrProcessor.cs
  43. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/SobelProcessor.cs
  44. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/GuassianBlurProcessor.cs
  45. 0
      src/ImageProcessorCore/Samplers/Processors/Convolution/GuassianSharpenProcessor.cs
  46. 2
      src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs
  47. 46
      src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs
  48. 111
      src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs
  49. 4
      src/ImageProcessorCore/Samplers/Processors/PixelateProcessor.cs
  50. 1
      src/ImageProcessorCore/Samplers/Processors/ResamplingWeightedProcessor.cs
  51. 1
      src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs

2
src/ImageProcessorCore/Filters/Blend.cs

@ -59,4 +59,4 @@ namespace ImageProcessorCore
}
}
}
}
}

2
src/ImageProcessorCore/Filters/ColorBlindness.cs

@ -44,7 +44,7 @@ namespace ImageProcessorCore
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
IImageProcessor<TColor, TPacked> processor;
IImageFilter<TColor, TPacked> processor;
switch (colorBlindness)
{

4
src/ImageProcessorCore/Filters/Grayscale.cs

@ -44,8 +44,8 @@ namespace ImageProcessorCore
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
IImageProcessor<TColor, TPacked> processor = mode == GrayscaleMode.Bt709
? (IImageProcessor<TColor, TPacked>)new GrayscaleBt709Processor<TColor, TPacked>()
IImageFilter<TColor, TPacked> processor = mode == GrayscaleMode.Bt709
? (IImageFilter<TColor, TPacked>)new GrayscaleBt709Processor<TColor, TPacked>()
: new GrayscaleBt601Processor<TColor, TPacked>();
processor.OnProgress += progressHandler;

27
src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs

@ -10,11 +10,11 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the alpha component of an <see cref="Image{TColor, TPacked}"/>.
/// An <see cref="IImageFilter{TColor, TPacked}"/> to change the alpha component of an <see cref="Image{TColor, TPacked}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class AlphaProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class AlphaProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -37,7 +37,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
float alpha = this.Value / 100F;
int startX = sourceRectangle.X;
@ -63,25 +63,24 @@ namespace ImageProcessorCore.Processors
Vector4 alphaVector = new Vector4(1, 1, 1, alpha);
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
TColor packed = default(TColor);
packed.PackFromVector4(sourcePixels[offsetX, offsetY].ToVector4() * alphaVector);
targetPixels[offsetX, offsetY] = packed;
}
int offsetX = x - startX;
TColor packed = default(TColor);
packed.PackFromVector4(sourcePixels[offsetX, offsetY].ToVector4() * alphaVector);
sourcePixels[offsetX, offsetY] = packed;
}
this.OnRowProcessed();
});
this.OnRowProcessed();
});
}
}
}

47
src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Sets the background color of the image.
/// </summary>
public class BackgroundColorProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class BackgroundColorProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -36,7 +36,7 @@ namespace ImageProcessorCore.Processors
public TColor Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -61,39 +61,38 @@ namespace ImageProcessorCore.Processors
Vector4 backgroundColor = this.Value.ToVector4();
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
float a = color.W;
if (a < 1 && a > 0)
{
color = Vector4.Lerp(color, backgroundColor, .5F);
}
int offsetX = x - startX;
Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
float a = color.W;
if (Math.Abs(a) < Epsilon)
{
color = backgroundColor;
}
if (a < 1 && a > 0)
{
color = Vector4.Lerp(color, backgroundColor, .5F);
}
TColor packed = default(TColor);
packed.PackFromVector4(color);
targetPixels[offsetX, offsetY] = packed;
if (Math.Abs(a) < Epsilon)
{
color = backgroundColor;
}
this.OnRowProcessed();
});
TColor packed = default(TColor);
packed.PackFromVector4(color);
sourcePixels[offsetX, offsetY] = packed;
}
this.OnRowProcessed();
});
}
}
}
}
}

31
src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs

@ -9,12 +9,12 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to perform binary threshold filtering against an
/// An <see cref="IImageFilter{TColor,TPacked}"/> to perform binary threshold filtering against an
/// <see cref="Image"/>. The image will be converted to grayscale before thresholding occurs.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class BinaryThresholdProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class BinaryThresholdProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -56,13 +56,13 @@ namespace ImageProcessorCore.Processors
public TColor LowerColor { get; set; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
protected override void OnApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
new GrayscaleBt709Processor<TColor, TPacked>().Apply(source, source, sourceRectangle);
new GrayscaleBt709Processor<TColor, TPacked>().Apply(source, sourceRectangle);
}
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
float threshold = this.Value;
TColor upper = this.UpperColor;
@ -88,26 +88,25 @@ namespace ImageProcessorCore.Processors
}
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
TColor color = sourcePixels[offsetX, offsetY];
int offsetX = x - startX;
TColor color = sourcePixels[offsetX, offsetY];
// Any channel will do since it's Grayscale.
targetPixels[offsetX, offsetY] = color.ToVector4().X >= threshold ? upper : lower;
}
// Any channel will do since it's Grayscale.
sourcePixels[offsetX, offsetY] = color.ToVector4().X >= threshold ? upper : lower;
}
this.OnRowProcessed();
});
this.OnRowProcessed();
});
}
}
}

7
src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs

@ -14,7 +14,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class BlendProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class BlendProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -44,7 +44,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -71,7 +71,6 @@ namespace ImageProcessorCore.Processors
using (PixelAccessor<TColor, TPacked> toBlendPixels = this.blend.Lock())
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
@ -98,7 +97,7 @@ namespace ImageProcessorCore.Processors
TColor packed = default(TColor);
packed.PackFromVector4(color);
targetPixels[offsetX, offsetY] = packed;
sourcePixels[offsetX, offsetY] = packed;
}
this.OnRowProcessed();

11
src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs

@ -10,11 +10,11 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the brightness of an <see cref="Image{TColor, TPacked}"/>.
/// An <see cref="IImageFilter{TColor,TPacked}"/> to change the brightness of an <see cref="Image{TColor, TPacked}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class BrightnessProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class BrightnessProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -37,7 +37,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
float brightness = this.Value / 100F;
int startX = sourceRectangle.X;
@ -61,7 +61,6 @@ namespace ImageProcessorCore.Processors
}
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
@ -82,7 +81,7 @@ namespace ImageProcessorCore.Processors
TColor packed = default(TColor);
packed.PackFromVector4(vector.Compress());
targetPixels[offsetX, offsetY] = packed;
sourcePixels[offsetX, offsetY] = packed;
}
this.OnRowProcessed();
@ -90,4 +89,4 @@ namespace ImageProcessorCore.Processors
}
}
}
}
}

9
src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs

@ -14,7 +14,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class ColorMatrixFilter<TColor, TPacked> : ImageProcessor<TColor, TPacked>, IColorMatrixFilter<TColor, TPacked>
public abstract class ColorMatrixFilter<TColor, TPacked> : ImageFilter<TColor, TPacked>, IColorMatrixFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -25,7 +25,7 @@ namespace ImageProcessorCore.Processors
public override bool Compand { get; set; } = true;
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -51,7 +51,6 @@ namespace ImageProcessorCore.Processors
bool compand = this.Compand;
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
@ -63,7 +62,7 @@ namespace ImageProcessorCore.Processors
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
targetPixels[offsetX, offsetY] = this.ApplyMatrix(sourcePixels[offsetX, offsetY], matrix, compand);
sourcePixels[offsetX, offsetY] = this.ApplyMatrix(sourcePixels[offsetX, offsetY], matrix, compand);
}
this.OnRowProcessed();
@ -96,4 +95,4 @@ namespace ImageProcessorCore.Processors
return packed;
}
}
}
}

2
src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs

@ -13,7 +13,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IColorMatrixFilter<TColor, TPacked> : IImageProcessor<TColor, TPacked>
public interface IColorMatrixFilter<TColor, TPacked> : IImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{

4
src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs

@ -28,11 +28,11 @@ namespace ImageProcessorCore.Processors
};
/// <inheritdoc/>
protected override void AfterApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
protected override void AfterApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
TColor packed = default(TColor);
packed.PackFromBytes(0, 10, 0, 255); // Very dark (mostly black) lime green.
new VignetteProcessor<TColor, TPacked> { VignetteColor = packed }.Apply(target, target, sourceRectangle);
new VignetteProcessor<TColor, TPacked> { VignetteColor = packed }.Apply(source, sourceRectangle);
}
}
}

6
src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs

@ -34,15 +34,15 @@ namespace ImageProcessorCore.Processors
};
/// <inheritdoc/>
protected override void AfterApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
protected override void AfterApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
TColor packedV = default(TColor);
packedV.PackFromBytes(102, 34, 0, 255); // Very dark orange [Brown tone]
new VignetteProcessor<TColor, TPacked> { VignetteColor = packedV }.Apply(target, target, sourceRectangle);
new VignetteProcessor<TColor, TPacked> { VignetteColor = packedV }.Apply(source, sourceRectangle);
TColor packedG = default(TColor);
packedG.PackFromBytes(255, 153, 102, 178); // Light orange
new GlowProcessor<TColor, TPacked> { GlowColor = packedG, Radius = target.Width / 4F }.Apply(target, target, sourceRectangle);
new GlowProcessor<TColor, TPacked> { GlowColor = packedG, Radius = source.Width / 4F }.Apply(source, sourceRectangle);
}
}
}

13
src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs

@ -10,11 +10,11 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the contrast of an <see cref="Image{TColor, TPacked}"/>.
/// An <see cref="IImageFilter{TColor,TPacked}"/> to change the contrast of an <see cref="Image{TColor, TPacked}"/>.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
public class ContrastProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class ContrastProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -37,7 +37,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
float contrast = (100F + this.Value) / 100F;
int startX = sourceRectangle.X;
@ -63,7 +63,6 @@ namespace ImageProcessorCore.Processors
}
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
@ -82,7 +81,7 @@ namespace ImageProcessorCore.Processors
vector += shiftVector;
TColor packed = default(TColor);
packed.PackFromVector4(vector.Compress());
targetPixels[offsetX, offsetY] = packed;
sourcePixels[offsetX, offsetY] = packed;
}
this.OnRowProcessed();
@ -90,4 +89,4 @@ namespace ImageProcessorCore.Processors
}
}
}
}
}

12
src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs

@ -10,11 +10,11 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> that applies a radial glow effect an <see cref="Image{TColor, TPacked}"/>.
/// An <see cref="IImageFilter{TColor,TPacked}"/> that applies a radial glow effect an <see cref="Image{TColor, TPacked}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class GlowProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class GlowProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -39,7 +39,7 @@ namespace ImageProcessorCore.Processors
public float Radius { get; set; }
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -66,7 +66,6 @@ namespace ImageProcessorCore.Processors
}
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
@ -85,7 +84,7 @@ namespace ImageProcessorCore.Processors
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TColor packed = default(TColor);
packed.PackFromVector4(Vector4.Lerp(glowColor.ToVector4(), sourceColor, distance / maxDistance));
targetPixels[offsetX, offsetY] = packed;
sourcePixels[offsetX, offsetY] = packed;
}
}
@ -94,5 +93,4 @@ namespace ImageProcessorCore.Processors
}
}
}
}
}

31
src/ImageProcessorCore/Filters/Processors/IImageFilter.cs

@ -0,0 +1,31 @@
namespace ImageProcessorCore.Processors
{
/// <summary>
/// Encapsulates methods to alter the pixels of an image. The processor operates on the original source pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IImageFilter<TColor, TPacked> : IImageProcessor
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T, TP}"/>.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image processing filter as new image.
/// </remarks>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="source"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="sourceRectangle"/> doesnt fit the dimension of the image.
/// </exception>
void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle);
}
}

68
src/ImageProcessorCore/Filters/Processors/ImageFilter.cs

@ -0,0 +1,68 @@
namespace ImageProcessorCore.Processors
{
using System;
/// <summary>
/// Encapsulates methods to alter the pixels of an image. The processor operates on the original source pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class ImageFilter<TColor, TPacked> : ImageProcessor<TColor, TPacked>, IImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
/// <inheritdoc/>
public void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
try
{
this.OnApply(source, sourceRectangle);
this.NumRowsProcessed = 0;
this.TotalRows = sourceRectangle.Height;
this.Apply(source, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom);
this.AfterApply(source, sourceRectangle);
}
catch (Exception ex)
{
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
}
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor, TPacked}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="source">The source image. 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="startY">The index of the row within the source image to start processing.</param>
/// <param name="endY">The index of the row within the source image to end processing.</param>
protected abstract void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY);
/// <summary>
/// This method is called before the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void OnApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
}
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
}
}
}

12
src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs

@ -10,16 +10,16 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to invert the colors of an <see cref="Image{TColor, TPacked}"/>.
/// An <see cref="IImageFilter{TColor,TPacked}"/> to invert the colors of an <see cref="Image{TColor, TPacked}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class InvertProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class InvertProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -43,7 +43,6 @@ namespace ImageProcessorCore.Processors
}
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
@ -60,7 +59,7 @@ namespace ImageProcessorCore.Processors
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(vector, color.W));
targetPixels[offsetX, offsetY] = packed;
sourcePixels[offsetX, offsetY] = packed;
}
this.OnRowProcessed();
@ -68,5 +67,4 @@ namespace ImageProcessorCore.Processors
}
}
}
}
}

9
src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs

@ -10,11 +10,11 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> that applies a radial vignette effect to an <see cref="Image{TColor, TPacked}"/>.
/// An <see cref="IImageFilter{TColor,TPacked}"/> that applies a radial vignette effect to an <see cref="Image{TColor, TPacked}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class VignetteProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class VignetteProcessor<TColor, TPacked> : ImageFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -44,7 +44,7 @@ namespace ImageProcessorCore.Processors
public float RadiusY { get; set; }
/// <inheritdoc/>
protected override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -72,7 +72,6 @@ namespace ImageProcessorCore.Processors
}
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{
Parallel.For(
minY,
@ -88,7 +87,7 @@ namespace ImageProcessorCore.Processors
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TColor packed = default(TColor);
packed.PackFromVector4(Vector4.Lerp(vignetteColor.ToVector4(), sourceColor, 1 - (.9F * (distance / maxDistance))));
targetPixels[offsetX, offsetY] = packed;
sourcePixels[offsetX, offsetY] = packed;
}
this.OnRowProcessed();

47
src/ImageProcessorCore/Image/IImageProcessor.cs

@ -17,11 +17,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Encapsulates methods to alter the pixels of an image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IImageProcessor<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
public interface IImageProcessor
{
/// <summary>
/// Event fires when each row of the source image has been processed.
@ -42,46 +38,5 @@ namespace ImageProcessorCore.Processors
/// or expand individual pixel colors the value on processing.
/// </summary>
bool Compand { get; set; }
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T, TP}"/>.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image processing filter as new image.
/// </remarks>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="target"/> is null or <paramref name="source"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="sourceRectangle"/> doesnt fit the dimension of the image.
/// </exception>
void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle sourceRectangle);
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T, TP}"/> at the specified
/// location and with the specified size.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target width.</param>
/// <param name="height">The target height.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image process as new image.
/// </remarks>
void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle);
}
}

74
src/ImageProcessorCore/Image/ImageIOExtensions.cs

@ -0,0 +1,74 @@
// <copyright file="ImageExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using System.IO;
using Formats;
/// <summary>
/// Extension methods for the <see cref="Image{TColor, TPacked}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Saves the image to the given stream with the bmp format.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new BmpEncoder().Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the png format.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors.
/// Anything equal to 256 and below will cause the encoder to save the image in an indexed format.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPng<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream, int quality = int.MaxValue)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new PngEncoder { Quality = quality }.Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the jpeg format.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to. Between 1 and 100.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsJpeg<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream, int quality = 75)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new JpegEncoder { Quality = quality }.Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the gif format.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors. Between 1 and 256.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
internal static void SaveAsGif<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream, int quality = 256)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new GifEncoder { Quality = quality }.Encode(source, stream);
}
}

104
src/ImageProcessorCore/Image/ImageExtensions.cs → src/ImageProcessorCore/Image/ImageProcessingExtensions.cs

@ -6,9 +6,6 @@
namespace ImageProcessorCore
{
using System;
using System.IO;
using Formats;
using Processors;
/// <summary>
@ -17,65 +14,42 @@ namespace ImageProcessorCore
public static partial class ImageExtensions
{
/// <summary>
/// Saves the image to the given stream with the bmp format.
/// Applies the processor to the image.
/// <remarks>This method does not resize the target image.</remarks>
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream)
/// <param name="processor">The processor to apply to the image.</param>
/// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>
internal static Image<TColor, TPacked> Process<TColor, TPacked>(this Image<TColor, TPacked> source, IImageFilter<TColor, TPacked> processor)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new BmpEncoder().Encode(source, stream);
{
return Process(source, source.Bounds, processor);
}
/// <summary>
/// Saves the image to the given stream with the png format.
/// Applies the processor to the image.
/// <remarks>This method does not resize the target image.</remarks>
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors.
/// Anything equal to 256 and below will cause the encoder to save the image in an indexed format.
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPng<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream, int quality = int.MaxValue)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new PngEncoder { Quality = quality }.Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the jpeg format.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to. Between 1 and 100.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsJpeg<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream, int quality = 75)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new JpegEncoder { Quality = quality }.Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the gif format.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors. Between 1 and 256.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
internal static void SaveAsGif<TColor, TPacked>(this Image<TColor, TPacked> source, Stream stream, int quality = 256)
/// <param name="processor">The processors to apply to the image.</param>
/// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>
internal static Image<TColor, TPacked> Process<TColor, TPacked>(this Image<TColor, TPacked> source, Rectangle sourceRectangle, IImageFilter<TColor, TPacked> processor)
where TColor : IPackedVector<TPacked>
where TPacked : struct
=> new GifEncoder { Quality = quality }.Encode(source, stream);
{
return PerformAction(source, (sourceImage) => processor.Apply(sourceImage, sourceRectangle));
}
/// <summary>
/// Applies the collection of processors to the image.
/// Applies the processor to the image.
/// <remarks>This method does not resize the target image.</remarks>
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
@ -83,7 +57,7 @@ namespace ImageProcessorCore
/// <param name="source">The image this method extends.</param>
/// <param name="processor">The processor to apply to the image.</param>
/// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>
internal static Image<TColor, TPacked> Process<TColor, TPacked>(this Image<TColor, TPacked> source, IImageProcessor<TColor, TPacked> processor)
internal static Image<TColor, TPacked> Process<TColor, TPacked>(this Image<TColor, TPacked> source, IImageSampler<TColor, TPacked> processor)
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -91,7 +65,7 @@ namespace ImageProcessorCore
}
/// <summary>
/// Applies the collection of processors to the image.
/// Applies the processor to the image.
/// <remarks>This method does not resize the target image.</remarks>
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
@ -102,7 +76,7 @@ namespace ImageProcessorCore
/// </param>
/// <param name="processor">The processors to apply to the image.</param>
/// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>
internal static Image<TColor, TPacked> Process<TColor, TPacked>(this Image<TColor, TPacked> source, Rectangle sourceRectangle, IImageProcessor<TColor, TPacked> processor)
internal static Image<TColor, TPacked> Process<TColor, TPacked>(this Image<TColor, TPacked> source, Rectangle sourceRectangle, IImageSampler<TColor, TPacked> processor)
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -110,12 +84,12 @@ namespace ImageProcessorCore
}
/// <summary>
/// Applies the collection of processors to the image.
/// Applies the processor to the image.
/// <remarks>
/// This method is not chainable.
/// This method resizes the image.
/// </remarks>
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target image width.</param>
@ -130,12 +104,12 @@ namespace ImageProcessorCore
}
/// <summary>
/// Applies the collection of processors to the image.
/// Applies the processor to the image.
/// <remarks>
/// This method does will resize the target image if the source and target rectangles are different.
/// </remarks>
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target image width.</param>
@ -159,7 +133,29 @@ namespace ImageProcessorCore
/// <summary>
/// Performs the given action on the source image.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image to perform the action against.</param>
/// <param name="action">The <see cref="Action"/> to perform against the image.</param>
/// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>
private static Image<TColor, TPacked> PerformAction<TColor, TPacked>(Image<TColor, TPacked> source, Action<ImageBase<TColor, TPacked>> action)
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
action(source);
foreach (ImageFrame<TColor, TPacked> sourceFrame in source.Frames)
{
action(sourceFrame);
}
return source;
}
/// <summary>
/// Performs the given action on the source image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image to perform the action against.</param>
/// <param name="clone">Whether to clone the image.</param>

125
src/ImageProcessorCore/ImageProcessor.cs

@ -5,7 +5,6 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Threading;
using System.Threading.Tasks;
@ -14,7 +13,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class ImageProcessor<TColor, TPacked> : IImageProcessor<TColor, TPacked>
public abstract class ImageProcessor<TColor, TPacked> : IImageProcessor
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
@ -24,12 +23,12 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// The number of rows processed by a derived class.
/// </summary>
private int numRowsProcessed;
protected int NumRowsProcessed;
/// <summary>
/// The total number of rows that will be processed by a derived class.
/// </summary>
private int totalRows;
protected int TotalRows;
/// <inheritdoc/>
public virtual ParallelOptions ParallelOptions { get; set; } = Bootstrapper.Instance.ParallelOptions;
@ -37,114 +36,6 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
public virtual bool Compand { get; set; } = false;
/// <inheritdoc/>
public void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
try
{
this.OnApply(target, source, target.Bounds, sourceRectangle);
this.numRowsProcessed = 0;
this.totalRows = sourceRectangle.Height;
this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom);
this.AfterApply(target, source, target.Bounds, sourceRectangle);
}
catch (Exception ex)
{
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
}
}
/// <inheritdoc/>
public void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle))
{
try
{
TColor[] pixels = new TColor[width * height];
target.SetPixels(width, height, pixels);
// Ensure we always have bounds.
if (sourceRectangle == Rectangle.Empty)
{
sourceRectangle = source.Bounds;
}
if (targetRectangle == Rectangle.Empty)
{
targetRectangle = target.Bounds;
}
this.OnApply(target, source, targetRectangle, sourceRectangle);
this.numRowsProcessed = 0;
this.totalRows = targetRectangle.Height;
this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom);
this.AfterApply(target, source, target.Bounds, sourceRectangle);
}
catch (Exception ex)
{
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
}
}
/// <summary>
/// This method is called before the process is applied to prepare the processor.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor, TPacked}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="startY">The index of the row within the source image to start processing.</param>
/// <param name="endY">The index of the row within the source image to end processing.</param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image process as new image.
/// </remarks>
protected abstract void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY);
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
}
/// <summary>
/// Must be called by derived classes after processing a single row.
/// </summary>
@ -152,17 +43,17 @@ namespace ImageProcessorCore.Processors
{
if (this.OnProgress != null)
{
int currThreadNumRows = Interlocked.Add(ref this.numRowsProcessed, 1);
int currThreadNumRows = Interlocked.Add(ref this.NumRowsProcessed, 1);
// Multi-pass filters process multiple times more rows than totalRows, so update totalRows on the fly
if (currThreadNumRows > this.totalRows)
if (currThreadNumRows > this.TotalRows)
{
this.totalRows = currThreadNumRows;
this.TotalRows = currThreadNumRows;
}
// Report progress. This may be on the client's thread, or on a Task library thread.
this.OnProgress(this, new ProgressEventArgs { RowsProcessed = currThreadNumRows, TotalRows = this.totalRows });
this.OnProgress(this, new ProgressEventArgs { RowsProcessed = currThreadNumRows, TotalRows = this.TotalRows });
}
}
}
}
}

0
src/ImageProcessorCore/Filters/BoxBlur.cs → src/ImageProcessorCore/Samplers/BoxBlur.cs

0
src/ImageProcessorCore/Filters/DetectEdges.cs → src/ImageProcessorCore/Samplers/DetectEdges.cs

0
src/ImageProcessorCore/Filters/GuassianBlur.cs → src/ImageProcessorCore/Samplers/GuassianBlur.cs

0
src/ImageProcessorCore/Filters/GuassianSharpen.cs → src/ImageProcessorCore/Samplers/GuassianSharpen.cs

0
src/ImageProcessorCore/Filters/Pixelate.cs → src/ImageProcessorCore/Samplers/Pixelate.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/BoxBlurProcessor.cs

2
src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/Convolution2DFilter.cs

@ -14,7 +14,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class Convolution2DFilter<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public abstract class Convolution2DFilter<TColor, TPacked> : ImageSampler<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{

2
src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/Convolution2PassFilter.cs

@ -13,7 +13,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class Convolution2PassFilter<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public abstract class Convolution2PassFilter<TColor, TPacked> : ImageSampler<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{

2
src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/ConvolutionFilter.cs

@ -13,7 +13,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class ConvolutionFilter<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public abstract class ConvolutionFilter<TColor, TPacked> : ImageSampler<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{

2
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs

@ -23,7 +23,7 @@ namespace ImageProcessorCore.Processors
{
if (this.Grayscale)
{
new GrayscaleBt709Processor<TColor, TPacked>().Apply(source, source, sourceRectangle);
new GrayscaleBt709Processor<TColor, TPacked>().Apply(source, sourceRectangle);
}
}
}

2
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs

@ -23,7 +23,7 @@ namespace ImageProcessorCore.Processors
{
if (this.Grayscale)
{
new GrayscaleBt709Processor<TColor, TPacked>().Apply(source, source, sourceRectangle);
new GrayscaleBt709Processor<TColor, TPacked>().Apply(source, sourceRectangle);
}
}
}

2
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IEdgeDetectorFilter<TColor, TPacked> : IImageProcessor<TColor, TPacked>, IEdgeDetectorFilter
public interface IEdgeDetectorFilter<TColor, TPacked> : IImageSampler<TColor, TPacked>, IEdgeDetectorFilter
where TColor : IPackedVector<TPacked>
where TPacked : struct
{

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/KirschProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/PrewittProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/ScharrProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/EdgeDetection/SobelProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/GuassianBlurProcessor.cs

0
src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs → src/ImageProcessorCore/Samplers/Processors/Convolution/GuassianSharpenProcessor.cs

2
src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs

@ -50,7 +50,7 @@ namespace ImageProcessorCore.Processors
new SobelProcessor<TColor, TPacked>().Apply(temp, source, sourceRectangle);
// Apply threshold binarization filter.
new BinaryThresholdProcessor<TColor, TPacked>(.5f).Apply(temp, temp, sourceRectangle);
new BinaryThresholdProcessor<TColor, TPacked>(.5f).Apply(temp, sourceRectangle);
// Search for the first white pixels
Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);

46
src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs

@ -6,11 +6,53 @@
namespace ImageProcessorCore.Processors
{
/// <summary>
/// Acts as a marker for generic parameters that require an image sampler.
/// Encapsulates methods to alter the pixels of an image. The processor creates a copy of the original image to operate on.
/// </summary>
public interface IImageSampler<TColor, TPacked> : IImageProcessor<TColor, TPacked>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IImageSampler<TColor, TPacked> : IImageProcessor
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T, TP}"/>.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image processing filter as new image.
/// </remarks>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="target"/> is null or <paramref name="source"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="sourceRectangle"/> doesnt fit the dimension of the image.
/// </exception>
void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle sourceRectangle);
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T, TP}"/> at the specified
/// location and with the specified size.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target width.</param>
/// <param name="height">The target height.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image process as new image.
/// </remarks>
void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle);
}
}

111
src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs

@ -5,9 +5,10 @@
namespace ImageProcessorCore.Processors
{
using System;
/// <summary>
/// Applies sampling methods to an image.
/// All processors requiring resampling or resizing should inherit from this.
/// Encapsulates methods to alter the pixels of an image. The processor creates a copy of the original image to operate on.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
@ -15,5 +16,111 @@ namespace ImageProcessorCore.Processors
where TColor : IPackedVector<TPacked>
where TPacked : struct
{
/// <inheritdoc/>
public void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{
try
{
this.OnApply(target, source, target.Bounds, sourceRectangle);
this.NumRowsProcessed = 0;
this.TotalRows = sourceRectangle.Height;
this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom);
this.AfterApply(target, source, target.Bounds, sourceRectangle);
}
catch (Exception ex)
{
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
}
}
/// <inheritdoc/>
public void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle))
{
try
{
TColor[] pixels = new TColor[width * height];
target.SetPixels(width, height, pixels);
// Ensure we always have bounds.
if (sourceRectangle == Rectangle.Empty)
{
sourceRectangle = source.Bounds;
}
if (targetRectangle == Rectangle.Empty)
{
targetRectangle = target.Bounds;
}
this.OnApply(target, source, targetRectangle, sourceRectangle);
this.NumRowsProcessed = 0;
this.TotalRows = targetRectangle.Height;
this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom);
this.AfterApply(target, source, target.Bounds, sourceRectangle);
}
catch (Exception ex)
{
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
}
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor, TPacked}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="startY">The index of the row within the source image to start processing.</param>
/// <param name="endY">The index of the row within the source image to end processing.</param>
/// <remarks>
/// The method keeps the source image unchanged and returns the the result of image process as new image.
/// </remarks>
protected abstract void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY);
/// <summary>
/// This method is called before the process is applied to prepare the processor.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
}
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
}
}
}

4
src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs → src/ImageProcessorCore/Samplers/Processors/PixelateProcessor.cs

@ -10,11 +10,11 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to pixelate the colors of an <see cref="Image{TColor, TPacked}"/>.
/// An <see cref="IImageSampler{TColor,TPacked}"/> to pixelate the colors of an <see cref="Image{TColor, TPacked}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class PixelateProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
public class PixelateProcessor<TColor, TPacked> : ImageSampler<TColor, TPacked>
where TColor : IPackedVector<TPacked>
where TPacked : struct
{

1
src/ImageProcessorCore/Samplers/Processors/ResamplingWeightedProcessor.cs

@ -65,7 +65,6 @@ namespace ImageProcessorCore.Processors
}
}
/// <summary>
/// Computes the weights to apply at each pixel when resizing.
/// </summary>

1
src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs

@ -5,6 +5,7 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;

Loading…
Cancel
Save