Browse Source

Merge branch 'Core' of https://github.com/JimBobSquarePants/ImageProcessor into Core

Former-commit-id: 203574b85fa329af3810a1b75bddb7d77afaacd6
Former-commit-id: eae274b5fcab74785bd43d46a6c84df3f575e411
Former-commit-id: ce5682b4502e13a10385e0169f932c92a8b2eb8c
pull/1/head
James Jackson-South 10 years ago
parent
commit
cd3217c38c
  1. 11
      README.md
  2. 60
      src/ImageProcessorCore/Colors/Color.cs
  3. 8
      src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs
  4. 8
      src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs
  5. 10
      src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs
  6. 8
      src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs
  7. 8
      src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs
  8. 8
      src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs
  9. 2
      src/ImageProcessorCore/Filters/Alpha.cs
  10. 2
      src/ImageProcessorCore/Filters/Binarization/Threshold.cs
  11. 2
      src/ImageProcessorCore/Filters/Brightness.cs
  12. 2
      src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs
  13. 2
      src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs
  14. 2
      src/ImageProcessorCore/Filters/Contrast.cs
  15. 2
      src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs
  16. 4
      src/ImageProcessorCore/Filters/ImageFilterExtensions.cs
  17. 2
      src/ImageProcessorCore/Filters/Invert.cs
  18. 2
      src/ImageProcessorCore/Filters/Pixelate.cs
  19. 2
      src/ImageProcessorCore/IImageProcessor.cs
  20. 63
      src/ImageProcessorCore/Image.cs
  21. 12
      src/ImageProcessorCore/ImageExtensions.cs
  22. 2
      src/ImageProcessorCore/ParallelImageProcessor.cs
  23. 20
      tests/ImageProcessorCore.Tests/Colors/ColorTests.cs
  24. 4
      tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs
  25. 36
      tests/ImageProcessorCore.Tests/Processors/Formats/EncoderDecoderTests.cs

11
README.md

@ -177,3 +177,14 @@ Please... Spread the word, contribute algorithms, submit performance improvement
Performance is a biggie, if you know anything about the new vector types and can apply some fancy new stuff with that it would be awesome.
There's a lot of developers out there who could write this stuff a lot better and faster than I and I would love to see what we collectively can come up with so please, if you can help in any way it would be most welcome and benificial for all.
###The ImageProcessor Team
Grand High Eternal Dictator
- [James Jackson-South](https://github.com/jimbobsquarepants)
Core Team
- [Yufeih Huang](https://github.com/yufeih)
- [Thomas Broust](https://github.com/cosmo0)
- [Christopher Bauer](https://github.com/christopherbauer)
- [Jeavon Leopold](https://github.com/jeavon)

60
src/ImageProcessorCore/Colors/Color.cs

@ -78,20 +78,24 @@ namespace ImageProcessorCore
if (hex.Length == 8)
{
float r = Convert.ToByte(hex.Substring(2, 2), 16) / 255f;
float g = Convert.ToByte(hex.Substring(4, 2), 16) / 255f;
float b = Convert.ToByte(hex.Substring(6, 2), 16) / 255f;
float a = Convert.ToByte(hex.Substring(0, 2), 16) / 255f;
float r = Convert.ToByte(hex.Substring(2, 2), 16);
float g = Convert.ToByte(hex.Substring(4, 2), 16);
float b = Convert.ToByte(hex.Substring(6, 2), 16);
float a = Convert.ToByte(hex.Substring(0, 2), 16);
this.backingVector = FromNonPremultiplied(new Color(r, g, b, a)).ToVector4();
// Do division of Vector4 instead of each component to utilize SIMD optimizations
this.backingVector = FromNonPremultiplied(new Vector4(r, g, b, a) / 255f, this.A);
}
else if (hex.Length == 6)
{
this.R = Convert.ToByte(hex.Substring(0, 2), 16) / 255f;
this.G = Convert.ToByte(hex.Substring(2, 2), 16) / 255f;
this.B = Convert.ToByte(hex.Substring(4, 2), 16) / 255f;
this.A = 1;
float r = Convert.ToByte(hex.Substring(0, 2), 16);
float g = Convert.ToByte(hex.Substring(2, 2), 16);
float b = Convert.ToByte(hex.Substring(4, 2), 16);
float a = 255f;
// Do division of Vector4 instead of each component to utilize SIMD optimizations
this.backingVector = new Vector4(r, g, b, a) / 255f;
}
else
{
@ -99,10 +103,12 @@ namespace ImageProcessorCore
string gh = char.ToString(hex[1]);
string bh = char.ToString(hex[2]);
this.B = Convert.ToByte(bh + bh, 16) / 255f;
this.G = Convert.ToByte(gh + gh, 16) / 255f;
this.R = Convert.ToByte(rh + rh, 16) / 255f;
this.A = 1;
float r = Convert.ToByte(rh + rh, 16);
float g = Convert.ToByte(gh + gh, 16);
float b = Convert.ToByte(bh + bh, 16);
float a = 255f;
this.backingVector = new Vector4(r, g, b, a) / 255f;
}
}
@ -123,7 +129,7 @@ namespace ImageProcessorCore
/// </param>
public Color(Vector3 vector)
{
this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, 1);
this.backingVector = new Vector4(vector, 1);
}
/// <summary>
@ -135,7 +141,7 @@ namespace ImageProcessorCore
/// <param name="alpha">The alpha component.</param>
public Color(Vector3 vector, float alpha)
{
this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, alpha);
this.backingVector = new Vector4(vector, alpha);
}
/// <summary>
@ -367,8 +373,18 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Color"/>.</returns>
public static Color FromNonPremultiplied(Color color)
{
float a = color.A;
return new Color(color.backingVector * new Vector4(a, a, a, 1));
return new Color(FromNonPremultiplied(color.backingVector, color.A));
}
/// <summary>
/// Converts a non-premultiplied alpha Vector4 to a Vector4 that contains premultiplied alpha.
/// </summary>
/// <param name="vector">The vector to convert.</param>
/// <param name="alpha">The alpha to use in conversion.</param>
/// <returns>The Vector4 with premultiplied alpha.</returns>
private static Vector4 FromNonPremultiplied(Vector4 vector, float alpha)
{
return vector * new Vector4(alpha, alpha, alpha, 1);
}
/// <summary>
@ -443,10 +459,12 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public bool AlmostEquals(Color other, float precision)
{
return Math.Abs(this.R - other.R) < precision
&& Math.Abs(this.G - other.G) < precision
&& Math.Abs(this.B - other.B) < precision
&& Math.Abs(this.A - other.A) < precision;
Vector4 result = Vector4.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision
&& result.W < precision;
}
/// <summary>

8
src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs

@ -172,9 +172,11 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public bool AlmostEquals(CieLab other, float precision)
{
return Math.Abs(this.L - other.L) < precision
&& Math.Abs(this.B - other.B) < precision
&& Math.Abs(this.B - other.B) < precision;
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>

8
src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs

@ -163,9 +163,11 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public bool AlmostEquals(CieXyz other, float precision)
{
return Math.Abs(this.X - other.X) < precision
&& Math.Abs(this.Y - other.Y) < precision
&& Math.Abs(this.Z - other.Z) < precision;
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>

10
src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs

@ -175,10 +175,12 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public bool AlmostEquals(Cmyk other, float precision)
{
return Math.Abs(this.C - other.C) < precision
&& Math.Abs(this.M - other.M) < precision
&& Math.Abs(this.Y - other.Y) < precision
&& Math.Abs(this.K - other.K) < precision;
Vector4 result = Vector4.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision
&& result.W < precision;
}
/// <summary>

8
src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs

@ -192,9 +192,11 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public bool AlmostEquals(Hsl other, float precision)
{
return Math.Abs(this.H - other.H) < precision
&& Math.Abs(this.S - other.S) < precision
&& Math.Abs(this.L - other.L) < precision;
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>

8
src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs

@ -186,9 +186,11 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public bool AlmostEquals(Hsv other, float precision)
{
return Math.Abs(this.H - other.H) < precision
&& Math.Abs(this.S - other.S) < precision
&& Math.Abs(this.V - other.V) < precision;
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>

8
src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs

@ -162,9 +162,11 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public bool AlmostEquals(YCbCr other, float precision)
{
return Math.Abs(this.Y - other.Y) < precision
&& Math.Abs(this.Cb - other.Cb) < precision
&& Math.Abs(this.Cr - other.Cr) < precision;
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>

2
src/ImageProcessorCore/Filters/Alpha.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessorCore"/> to change the Alpha of an <see cref="Image"/>.
/// An <see cref="IImageProcessor"/> to change the Alpha of an <see cref="Image"/>.
/// </summary>
public class Alpha : ParallelImageProcessorCore
{

2
src/ImageProcessorCore/Filters/Binarization/Threshold.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessorCore"/> to perform binary threshold filtering against an
/// An <see cref="IImageProcessor"/> to perform binary threshold filtering against an
/// <see cref="Image"/>. The image will be converted to greyscale before thresholding
/// occurs.
/// </summary>

2
src/ImageProcessorCore/Filters/Brightness.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessorCore"/> to change the brightness of an <see cref="Image"/>.
/// An <see cref="IImageProcessor"/> to change the brightness of an <see cref="Image"/>.
/// </summary>
public class Brightness : ParallelImageProcessorCore
{

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

@ -11,7 +11,7 @@ namespace ImageProcessorCore.Filters
/// Encapsulates properties and methods for creating processors that utilize a matrix to
/// alter the image pixels.
/// </summary>
public interface IColorMatrixFilter : IImageProcessorCore
public interface IColorMatrixFilter : IImageProcessor
{
/// <summary>
/// Gets the <see cref="Matrix4x4"/> used to alter the image.

2
src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs

@ -9,7 +9,7 @@ namespace ImageProcessorCore.Filters
using System.Numerics;
/// <summary>
/// An <see cref="IImageProcessorCore"/> to change the saturation of an <see cref="Image"/>.
/// An <see cref="IImageProcessor"/> to change the saturation of an <see cref="Image"/>.
/// </summary>
public class Saturation : ColorMatrixFilter
{

2
src/ImageProcessorCore/Filters/Contrast.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessorCore"/> to change the contrast of an <see cref="Image"/>.
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>.
/// </summary>
public class Contrast : ParallelImageProcessorCore
{

2
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs

@ -8,7 +8,7 @@ namespace ImageProcessorCore.Filters
/// <summary>
/// Provides properties and methods allowing the detection of edges within an image.
/// </summary>
public interface IEdgeDetectorFilter : IImageProcessorCore
public interface IEdgeDetectorFilter : IImageProcessor
{
/// <summary>
/// Gets or sets a value indicating whether to convert the

4
src/ImageProcessorCore/Filters/ImageFilterExtensions.cs

@ -326,8 +326,8 @@ namespace ImageProcessorCore.Filters
/// <returns>The <see cref="Image"/>.</returns>
public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null)
{
IImageProcessorCore processor = mode == GreyscaleMode.Bt709
? (IImageProcessorCore)new GreyscaleBt709()
IImageProcessor processor = mode == GreyscaleMode.Bt709
? (IImageProcessor)new GreyscaleBt709()
: new GreyscaleBt601();
processor.OnProgress += progressHandler;

2
src/ImageProcessorCore/Filters/Invert.cs

@ -9,7 +9,7 @@ namespace ImageProcessorCore.Filters
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessorCore"/> to invert the colors of an <see cref="Image"/>.
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// </summary>
public class Invert : ParallelImageProcessorCore
{

2
src/ImageProcessorCore/Filters/Pixelate.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessorCore"/> to invert the colors of an <see cref="Image"/>.
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// </summary>
public class Pixelate : ParallelImageProcessorCore
{

2
src/ImageProcessorCore/IImageProcessor.cs

@ -15,7 +15,7 @@ namespace ImageProcessorCore
/// <summary>
/// Encapsulates methods to alter the pixels of an image.
/// </summary>
public interface IImageProcessorCore
public interface IImageProcessor
{
/// <summary>
/// Event fires when each row of the source image has been processed.

63
src/ImageProcessorCore/Image.cs

@ -230,52 +230,45 @@ namespace ImageProcessorCore
/// </exception>
private void Load(Stream stream, IList<IImageFormat> formats)
{
try
if (!stream.CanRead)
{
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
}
throw new NotSupportedException("Cannot read from the stream.");
}
if (!stream.CanSeek)
{
throw new NotSupportedException("The stream does not support seeking.");
}
if (!stream.CanSeek)
{
throw new NotSupportedException("The stream does not support seeking.");
}
if (formats.Count > 0)
if (formats.Count > 0)
{
int maxHeaderSize = formats.Max(x => x.Decoder.HeaderSize);
if (maxHeaderSize > 0)
{
int maxHeaderSize = formats.Max(x => x.Decoder.HeaderSize);
if (maxHeaderSize > 0)
byte[] header = new byte[maxHeaderSize];
stream.Read(header, 0, maxHeaderSize);
stream.Position = 0;
IImageFormat format = formats.FirstOrDefault(x => x.Decoder.IsSupportedFileFormat(header));
if (format != null)
{
byte[] header = new byte[maxHeaderSize];
stream.Read(header, 0, maxHeaderSize);
stream.Position = 0;
IImageFormat format = formats.FirstOrDefault(x => x.Decoder.IsSupportedFileFormat(header));
if (format != null)
{
format.Decoder.Decode(this, stream);
this.CurrentImageFormat = format;
return;
}
format.Decoder.Decode(this, stream);
this.CurrentImageFormat = format;
return;
}
}
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
foreach (IImageFormat format in formats)
{
stringBuilder.AppendLine("-" + format);
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
throw new NotSupportedException(stringBuilder.ToString());
}
finally
foreach (IImageFormat format in formats)
{
stream.Dispose();
stringBuilder.AppendLine("-" + format);
}
throw new NotSupportedException(stringBuilder.ToString());
}
}
}

12
src/ImageProcessorCore/ImageExtensions.cs

@ -55,7 +55,7 @@ namespace ImageProcessorCore
/// <param name="source">The image this method extends.</param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, params IImageProcessorCore[] processors)
public static Image Process(this Image source, params IImageProcessor[] processors)
{
return Process(source, source.Bounds, processors);
}
@ -70,10 +70,10 @@ namespace ImageProcessorCore
/// </param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, Rectangle sourceRectangle, params IImageProcessorCore[] processors)
public static Image Process(this Image source, Rectangle sourceRectangle, params IImageProcessor[] processors)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IImageProcessorCore filter in processors)
foreach (IImageProcessor filter in processors)
{
source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, sourceRectangle));
}
@ -89,7 +89,7 @@ namespace ImageProcessorCore
/// <param name="height">The target image height.</param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, int width, int height, params IImageProcessorCore[] processors)
public static Image Process(this Image source, int width, int height, params IImageProcessor[] processors)
{
return Process(source, width, height, source.Bounds, default(Rectangle), processors);
}
@ -113,10 +113,10 @@ namespace ImageProcessorCore
/// </param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, params IImageProcessorCore[] processors)
public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, params IImageProcessor[] processors)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IImageProcessorCore filter in processors)
foreach (IImageProcessor filter in processors)
{
source = PerformAction(source, false, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle));
}

2
src/ImageProcessorCore/ParallelImageProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore
/// <summary>
/// Allows the application of processors using parallel processing.
/// </summary>
public abstract class ParallelImageProcessorCore : IImageProcessorCore
public abstract class ParallelImageProcessorCore : IImageProcessor
{
/// <inheritdoc/>
public event ProgressEventHandler OnProgress;

20
tests/ImageProcessorCore.Tests/Colors/ColorTests.cs

@ -8,6 +8,8 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
using System.Numerics;
namespace ImageProcessorCore.Tests
{
using Xunit;
@ -76,6 +78,24 @@ namespace ImageProcessorCore.Tests
Assert.Equal(0, color3.G, 1);
Assert.Equal(0, color3.B, 3);
Assert.Equal(1, color3.A, 1);
Color color4 = new Color(new Vector3(1, .1f, .133f));
Assert.Equal(1, color4.R, 1);
Assert.Equal(.1f, color4.G, 1);
Assert.Equal(.133f, color4.B, 3);
Assert.Equal(1, color4.A, 1);
Color color5 = new Color(new Vector3(1, .1f, .133f), .5f);
Assert.Equal(1, color5.R, 1);
Assert.Equal(.1f, color5.G, 1);
Assert.Equal(.133f, color5.B, 3);
Assert.Equal(.5f, color5.A, 1);
Color color6 = new Color(new Vector4(1, .1f, .133f, .5f));
Assert.Equal(1, color5.R, 1);
Assert.Equal(.1f, color6.G, 1);
Assert.Equal(.133f, color6.B, 3);
Assert.Equal(.5f, color6.A, 1);
}
/// <summary>

4
tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Tests
public class FilterTests : ProcessorTestBase
{
public static readonly TheoryData<string, IImageProcessorCore> Filters = new TheoryData<string, IImageProcessorCore>
public static readonly TheoryData<string, IImageProcessor> Filters = new TheoryData<string, IImageProcessor>
{
{ "Brightness-50", new Brightness(50) },
{ "Brightness--50", new Brightness(-50) },
@ -49,7 +49,7 @@ namespace ImageProcessorCore.Tests
[Theory]
[MemberData("Filters")]
public void FilterImage(string name, IImageProcessorCore processor)
public void FilterImage(string name, IImageProcessor processor)
{
if (!Directory.Exists("TestOutput/Filter"))
{

36
tests/ImageProcessorCore.Tests/Processors/Formats/EncoderDecoderTests.cs

@ -6,7 +6,7 @@
using Formats;
using Xunit;
using System.Linq;
public class EncoderDecoderTests : ProcessorTestBase
{
[Fact]
@ -101,5 +101,39 @@
}
}
}
[Fact]
public void ImageShouldPreservePixelByteOrderWhenSerialized()
{
if (!Directory.Exists("TestOutput/Serialized"))
{
Directory.CreateDirectory("TestOutput/Serialized");
}
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
{
Image image = new Image(stream);
byte[] serialized;
using (MemoryStream memoryStream = new MemoryStream())
{
image.Save(memoryStream);
memoryStream.Flush();
serialized = memoryStream.ToArray();
}
using (MemoryStream memoryStream = new MemoryStream(serialized))
{
Image image2 = new Image(memoryStream);
using (FileStream output = File.OpenWrite($"TestOutput/Serialized/{Path.GetFileName(file)}"))
{
image2.Save(output);
}
}
}
}
}
}
}
Loading…
Cancel
Save