diff --git a/greys/Matrices.cs b/greys/Matrices.cs
index c7e47f1..904acaa 100644
--- a/greys/Matrices.cs
+++ b/greys/Matrices.cs
@@ -37,6 +37,17 @@ public static class BuiltinMatrices
public static float[,] Tritanopia { get; private set; }
public static float[,] Tritanomaly { get; private set; }
+ ///
+ /// Daltonization (correction) filters. Unlike the simulation matrices
+ /// above, these are assistive: they redistribute the colour information
+ /// a deficient observer loses onto channels they can still perceive,
+ /// making confusable colours easier to tell apart. This is the same kind
+ /// of filter as the Windows "colour blindness" accessibility filters.
+ ///
+ public static float[,] ProtanopiaCorrection { get; private set; }
+ public static float[,] DeuteranopiaCorrection { get; private set; }
+ public static float[,] TritanopiaCorrection { get; private set; }
+
///
/// simple colors transformations
///
@@ -127,6 +138,36 @@ static BuiltinMatrices()
{ 0, 0, 0, 0, 1 }
};
+ // Error-shift matrices used to daltonize, in the GDI/Magnification
+ // row-vector convention (v_out = v_in * M), i.e. the transpose of
+ // the classic column-vector error matrices.
+ //
+ // Red-green deficiencies (protan/deutan): the lost red-green signal
+ // is pushed onto the green and blue channels.
+ // column-vector form -> row-vector (stored) form below
+ // [0 0 0] [0 .7 .7]
+ // [.7 1 0] [0 1 0]
+ // [.7 0 1] [0 0 1]
+ float[,] redGreenErrorShift = new float[,] {
+ { 0f, 0.7f, 0.7f, 0f, 0f },
+ { 0f, 1.0f, 0.0f, 0f, 0f },
+ { 0f, 0.0f, 1.0f, 0f, 0f },
+ { 0f, 0.0f, 0.0f, 1f, 0f },
+ { 0f, 0.0f, 0.0f, 0f, 1f }
+ };
+ // Blue-yellow deficiency (tritan): the lost blue-yellow signal is
+ // pushed onto the red and green channels.
+ float[,] blueYellowErrorShift = new float[,] {
+ { 1.0f, 0.0f, 0f, 0f, 0f },
+ { 0.0f, 1.0f, 0f, 0f, 0f },
+ { 0.7f, 0.7f, 0f, 0f, 0f },
+ { 0.0f, 0.0f, 0f, 1f, 0f },
+ { 0.0f, 0.0f, 0f, 0f, 1f }
+ };
+ ProtanopiaCorrection = Transform.Daltonize(Protanopia, redGreenErrorShift);
+ DeuteranopiaCorrection = Transform.Daltonize(Deuteranopia, redGreenErrorShift);
+ TritanopiaCorrection = Transform.Daltonize(Tritanopia, blueYellowErrorShift);
+
Negative = new float[,] {
{ 0.75f, 0.25f, 0.25f, 0, 0 },
{ 0.25f, 1.25f, 0.25f, 0, 0 },
diff --git a/greys/Program.cs b/greys/Program.cs
index 4116db9..5238533 100644
--- a/greys/Program.cs
+++ b/greys/Program.cs
@@ -44,6 +44,10 @@ static void Main(string[] args)
"Tritanopia",
"Tritanomaly",
+ "ProtanopiaCorrection",
+ "DeuteranopiaCorrection",
+ "TritanopiaCorrection",
+
"Negative",
"GrayScale",
"NegativeGrayScale",
@@ -66,6 +70,10 @@ static void Main(string[] args)
float[] Tritanopia = BuiltinMatrices.Tritanopia.Cast().ToArray();
float[] Tritanomaly = BuiltinMatrices.Tritanomaly.Cast().ToArray();
+ float[] ProtanopiaCorrection = BuiltinMatrices.ProtanopiaCorrection.Cast().ToArray();
+ float[] DeuteranopiaCorrection = BuiltinMatrices.DeuteranopiaCorrection.Cast().ToArray();
+ float[] TritanopiaCorrection = BuiltinMatrices.TritanopiaCorrection.Cast().ToArray();
+
float[] Negative = BuiltinMatrices.Negative.Cast().ToArray();
float[] GrayScale = BuiltinMatrices.GrayScale.Cast().ToArray();
float[] NegativeGrayScale = BuiltinMatrices.NegativeGrayScale.Cast().ToArray();
@@ -88,6 +96,10 @@ static void Main(string[] args)
Tritanopia,
Tritanomaly,
+ ProtanopiaCorrection,
+ DeuteranopiaCorrection,
+ TritanopiaCorrection,
+
Negative,
GrayScale,
NegativeGrayScale,
@@ -110,7 +122,7 @@ static void Main(string[] args)
int optionsCount = colorFiltersStr.Count;
int selected = 0;
bool done = false;
- List alphabet = new List { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" };
+ List alphabet = new List { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v" };
while (!done)
diff --git a/greys/Transform.cs b/greys/Transform.cs
index 73a2387..53947f6 100644
--- a/greys/Transform.cs
+++ b/greys/Transform.cs
@@ -43,6 +43,62 @@ public static class Transform
return c;
}
+ public static float[,] Add(float[,] a, float[,] b)
+ {
+ if (a.GetLength(0) != b.GetLength(0) || a.GetLength(1) != b.GetLength(1))
+ {
+ throw new Exception("Add: dimensions mismatch");
+ }
+ float[,] c = new float[a.GetLength(0), a.GetLength(1)];
+ for (int i = 0; i < c.GetLength(0); i++)
+ for (int j = 0; j < c.GetLength(1); j++)
+ c[i, j] = a[i, j] + b[i, j];
+ return c;
+ }
+
+ public static float[,] Subtract(float[,] a, float[,] b)
+ {
+ if (a.GetLength(0) != b.GetLength(0) || a.GetLength(1) != b.GetLength(1))
+ {
+ throw new Exception("Subtract: dimensions mismatch");
+ }
+ float[,] c = new float[a.GetLength(0), a.GetLength(1)];
+ for (int i = 0; i < c.GetLength(0); i++)
+ for (int j = 0; j < c.GetLength(1); j++)
+ c[i, j] = a[i, j] - b[i, j];
+ return c;
+ }
+
+ public static float[,] Identity(int size)
+ {
+ float[,] m = new float[size, size];
+ for (int i = 0; i < size; i++)
+ m[i, i] = 1f;
+ return m;
+ }
+
+ ///
+ /// Builds a daltonization (correction) matrix from a colour-blindness
+ /// *simulation* matrix. Where a simulation matrix collapses colours onto
+ /// the confusion axis (showing what the user cannot distinguish), the
+ /// correction matrix takes the information lost by the deficiency and
+ /// redistributes it onto channels the user *can* still perceive, making
+ /// confusable colours easier to tell apart.
+ ///
+ /// Using the GDI/Magnification row-vector convention (v_out = v_in * M),
+ /// the daltonized correction is:
+ /// Correction = I + (I - S) * E
+ /// where S is the simulation matrix and E is the error-shift matrix.
+ ///
+ public static float[,] Daltonize(float[,] simulation, float[,] errorShift)
+ {
+ int size = simulation.GetLength(0);
+ float[,] identity = Identity(size);
+ float[,] lost = Subtract(identity, simulation); // information the deficiency removes
+ float[,] redistributed = Multiply(lost, errorShift);
+ return Add(identity, redistributed);
+ }
+
public static List Interpolate(float[,] A, float[,] B)
{
const int STEPS = 10;
diff --git a/greys/greys.csproj b/greys/greys.csproj
index e48bed8..c8ca362 100644
--- a/greys/greys.csproj
+++ b/greys/greys.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net8.0-windows
enable
enable