diff --git a/src/ImageProcessorCore/Filters/ColorBlindness.cs b/src/ImageProcessorCore/Filters/ColorBlindness.cs
new file mode 100644
index 000000000..b5848a514
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/ColorBlindness.cs
@@ -0,0 +1,96 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+// -------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessorCore
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Applies the given colorblindness simulator to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image this method extends.
+ /// The type of color blindness simulator to apply.
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ return ColorBlindness(source, colorBlindness, source.Bounds, progressHandler);
+ }
+
+ ///
+ /// Applies the given colorblindness simulator to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image this method extends.
+ /// The type of color blindness simulator to apply.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ IImageProcessor processor;
+
+ switch (colorBlindness)
+ {
+ case ImageProcessorCore.ColorBlindness.Achromatomaly:
+ processor = new AchromatomalyProcessor();
+ break;
+
+ case ImageProcessorCore.ColorBlindness.Achromatopsia:
+ processor = new AchromatopsiaProcessor();
+ break;
+
+ case ImageProcessorCore.ColorBlindness.Deuteranomaly:
+ processor = new DeuteranomalyProcessor();
+ break;
+
+ case ImageProcessorCore.ColorBlindness.Deuteranopia:
+ processor = new DeuteranopiaProcessor();
+ break;
+
+ case ImageProcessorCore.ColorBlindness.Protanomaly:
+ processor = new ProtanomalyProcessor();
+ break;
+
+ case ImageProcessorCore.ColorBlindness.Protanopia:
+ processor = new ProtanopiaProcessor();
+ break;
+
+ case ImageProcessorCore.ColorBlindness.Tritanomaly:
+ processor = new TritanomalyProcessor();
+ break;
+
+ default:
+ processor = new TritanopiaProcessor();
+ break;
+ }
+
+ processor.OnProgress += progressHandler;
+
+ try
+ {
+ return source.Process(rectangle, processor);
+ }
+ finally
+ {
+ processor.OnProgress -= progressHandler;
+ }
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs b/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs
new file mode 100644
index 000000000..6d7fe849b
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs
@@ -0,0 +1,53 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ ///
+ /// Enumerates the various types of color blindness.
+ ///
+ public enum ColorBlindness
+ {
+ ///
+ /// Partial color desensitivity.
+ ///
+ Achromatomaly,
+
+ ///
+ /// Complete color desensitivity (Monochrome)
+ ///
+ Achromatopsia,
+
+ ///
+ /// Green weak
+ ///
+ Deuteranomaly,
+
+ ///
+ /// Green blind
+ ///
+ Deuteranopia,
+
+ ///
+ /// Red weak
+ ///
+ Protanomaly,
+
+ ///
+ /// Red blind
+ ///
+ Protanopia,
+
+ ///
+ /// Blue weak
+ ///
+ Tritanomaly,
+
+ ///
+ /// Blue blind
+ ///
+ Tritanopia
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
new file mode 100644
index 000000000..e8dd077eb
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
@@ -0,0 +1,36 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class AchromatomalyProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = .618f,
+ M12 = .163f,
+ M13 = .163f,
+ M21 = .320f,
+ M22 = .775f,
+ M23 = .320f,
+ M31 = .062f,
+ M32 = .062f,
+ M33 = .516f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
new file mode 100644
index 000000000..0e7f69a13
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
@@ -0,0 +1,36 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class AchromatopsiaProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = .299f,
+ M12 = .299f,
+ M13 = .299f,
+ M21 = .587f,
+ M22 = .587f,
+ M23 = .587f,
+ M31 = .114f,
+ M32 = .114f,
+ M33 = .114f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
new file mode 100644
index 000000000..b7f48ea1d
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class DeuteranomalyProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.8f,
+ M12 = 0.258f,
+ M21 = 0.2f,
+ M22 = 0.742f,
+ M23 = 0.142f,
+ M33 = 0.858f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
new file mode 100644
index 000000000..82f50d07f
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class DeuteranopiaProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.625f,
+ M12 = 0.7f,
+ M21 = 0.375f,
+ M22 = 0.3f,
+ M23 = 0.3f,
+ M33 = 0.7f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
new file mode 100644
index 000000000..32380cba1
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class ProtanomalyProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.817f,
+ M12 = 0.333f,
+ M21 = 0.183f,
+ M22 = 0.667f,
+ M23 = 0.125f,
+ M33 = 0.875f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs
new file mode 100644
index 000000000..891b6e06a
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class ProtanopiaProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.567f,
+ M12 = 0.558f,
+ M21 = 0.433f,
+ M22 = 0.442f,
+ M23 = 0.242f,
+ M33 = 0.758f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/README.md b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/README.md
new file mode 100644
index 000000000..209f3b67b
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/README.md
@@ -0,0 +1,4 @@
+Color blindness matrices adapted from and tested against:
+
+http://web.archive.org/web/20090413045433/http://nofunc.org/Color_Matrix_Library
+http://www.color-blindness.com/coblis-color-blindness-simulator/
\ No newline at end of file
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs
new file mode 100644
index 000000000..88e5fd5dc
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class TritanomalyProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.967f,
+ M21 = 0.33f,
+ M22 = 0.733f,
+ M23 = 0.183f,
+ M32 = 0.267f,
+ M33 = 0.817f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs
new file mode 100644
index 000000000..b708927a2
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class TritanopiaProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.95f,
+ M21 = 0.05f,
+ M22 = 0.433f,
+ M23 = 0.475f,
+ M32 = 0.567f,
+ M33 = 0.525f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
index 0b7600fbc..cbab86a1c 100644
--- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
@@ -9,8 +9,10 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
///
- /// The color matrix filter.
+ /// The color matrix filter. Inherit from this class to perform operation involving color matrices.
///
+ /// The pixel format.
+ /// The packed format. long, float.
public abstract class ColorMatrixFilter : ImageProcessor, IColorMatrixFilter
where T : IPackedVector
where TP : struct
@@ -63,11 +65,11 @@ namespace ImageProcessorCore.Processors
// color = Color.Expand(color);
//}
- Vector4 transformed = Vector4.Transform(color.ToVector4(), matrix);
- //Vector3 transformed = Vector3.Transform(color.ToVector3(), matrix);
+ Vector4 vector = color.ToVector4();
+ Vector3 transformed = Vector3.Transform(new Vector3(vector.X, vector.Y, vector.Z), matrix);
//return compand ? Color.Compress(new Color(transformed, color.A)) : new Color(transformed, color.A);
T packed = default(T);
- packed.PackVector(transformed);
+ packed.PackVector(new Vector4(transformed.X, transformed.Y, transformed.Z, vector.W));
return packed;
}
}
diff --git a/src/ImageProcessorCore/Image.cs b/src/ImageProcessorCore/Image.cs
index 9ab3e73ba..e4e2186b9 100644
--- a/src/ImageProcessorCore/Image.cs
+++ b/src/ImageProcessorCore/Image.cs
@@ -5,12 +5,14 @@
namespace ImageProcessorCore
{
- using System;
+ using System.Diagnostics;
using System.IO;
///
- /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha.
+ /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
+ /// packed into a single unsigned integer value.
///
+ [DebuggerDisplay("Image: {Width}x{Height}")]
public class Image : Image
{
///
diff --git a/src/ImageProcessorCore/Image/Image.cs b/src/ImageProcessorCore/Image/Image.cs
index 0b716f0dd..a53c87c5f 100644
--- a/src/ImageProcessorCore/Image/Image.cs
+++ b/src/ImageProcessorCore/Image/Image.cs
@@ -13,12 +13,14 @@ namespace ImageProcessorCore
using System.Linq;
using Formats;
+ using System.Diagnostics;
///
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
///
/// The pixel format.
/// The packed format. long, float.
+ [DebuggerDisplay("Image: {Width}x{Height}")]
public class Image : ImageBase
where T : IPackedVector
where TP : struct
@@ -75,6 +77,7 @@ namespace ImageProcessorCore
/// The other image, where the clone should be made from.
/// is null.
public Image(Image other)
+ : base(other)
{
foreach (ImageFrame frame in other.Frames)
{
@@ -84,7 +87,6 @@ namespace ImageProcessorCore
}
}
- this.Quality = other.Quality;
this.RepeatCount = other.RepeatCount;
this.HorizontalResolution = other.HorizontalResolution;
this.VerticalResolution = other.VerticalResolution;
diff --git a/src/ImageProcessorCore/Image/ImageBase.cs b/src/ImageProcessorCore/Image/ImageBase.cs
index 3a7a8f99a..adbe70d1b 100644
--- a/src/ImageProcessorCore/Image/ImageBase.cs
+++ b/src/ImageProcessorCore/Image/ImageBase.cs
@@ -6,6 +6,7 @@
namespace ImageProcessorCore
{
using System;
+ using System.Diagnostics;
///
/// The base class of all images. Encapsulates the basic properties and methods required to manipulate
@@ -13,6 +14,7 @@ namespace ImageProcessorCore
///
/// The pixel format.
/// The packed format. long, float.
+ [DebuggerDisplay("Image: {Width}x{Height}")]
public abstract class ImageBase : IImageBase
where T : IPackedVector
where TP : struct
diff --git a/src/ImageProcessorCore/Image/ImageExtensions.cs b/src/ImageProcessorCore/Image/ImageExtensions.cs
index c97b6dfa6..0b1bac55e 100644
--- a/src/ImageProcessorCore/Image/ImageExtensions.cs
+++ b/src/ImageProcessorCore/Image/ImageExtensions.cs
@@ -174,7 +174,7 @@ namespace ImageProcessorCore
: new Image
{
// Several properties require copying
- // TODO: Check why we need to set these?
+ FrameDelay = source.FrameDelay,
Quality = source.Quality,
HorizontalResolution = source.HorizontalResolution,
VerticalResolution = source.VerticalResolution,
diff --git a/src/ImageProcessorCore/Samplers/Pad.cs b/src/ImageProcessorCore/Samplers/Pad.cs
index 2bb6acc33..0b1b959b0 100644
--- a/src/ImageProcessorCore/Samplers/Pad.cs
+++ b/src/ImageProcessorCore/Samplers/Pad.cs
@@ -21,7 +21,7 @@ namespace ImageProcessorCore
/// The new width.
/// The new height.
/// A delegate which is called as progress is made processing the image.
- /// The .
+ /// The .
public static Image Pad(this Image source, int width, int height, ProgressEventHandler progressHandler = null)
where T : IPackedVector
where TP : struct
diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/ColorBlindnessTest.cs
new file mode 100644
index 000000000..5b2300885
--- /dev/null
+++ b/tests/ImageProcessorCore.Tests/Processors/Filters/ColorBlindnessTest.cs
@@ -0,0 +1,52 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Tests
+{
+ using System.IO;
+
+ using Xunit;
+
+ public class ColorBlindnessTest : FileTestBase
+ {
+ public static readonly TheoryData ColorBlindnessFilters
+ = new TheoryData
+ {
+ ColorBlindness.Achromatomaly,
+ ColorBlindness.Achromatopsia,
+ ColorBlindness.Deuteranomaly,
+ ColorBlindness.Deuteranopia,
+ ColorBlindness.Protanomaly,
+ ColorBlindness.Protanopia,
+ ColorBlindness.Tritanomaly,
+ ColorBlindness.Tritanopia
+ };
+
+ [Theory]
+ [MemberData("ColorBlindnessFilters")]
+ public void ImageShouldApplyColorBlindnessFilter(ColorBlindness colorBlindness)
+ {
+ const string path = "TestOutput/ColorBlindness";
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ foreach (string file in Files)
+ {
+ using (FileStream stream = File.OpenRead(file))
+ {
+ string filename = Path.GetFileNameWithoutExtension(file) + "-" + colorBlindness + Path.GetExtension(file);
+ Image image = new Image(stream);
+ using (FileStream output = File.OpenWrite($"{path}/{filename}"))
+ {
+ image.ColorBlindness(colorBlindness)
+ .Save(output);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file