Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions greys/Matrices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ public static class BuiltinMatrices
public static float[,] Tritanopia { get; private set; }
public static float[,] Tritanomaly { get; private set; }

/// <summary>
/// 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.
/// </summary>
public static float[,] ProtanopiaCorrection { get; private set; }
public static float[,] DeuteranopiaCorrection { get; private set; }
public static float[,] TritanopiaCorrection { get; private set; }

/// <summary>
/// simple colors transformations
/// </summary>
Expand Down Expand Up @@ -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 },
Expand Down
14 changes: 13 additions & 1 deletion greys/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ static void Main(string[] args)
"Tritanopia",
"Tritanomaly",

"ProtanopiaCorrection",
"DeuteranopiaCorrection",
"TritanopiaCorrection",

"Negative",
"GrayScale",
"NegativeGrayScale",
Expand All @@ -66,6 +70,10 @@ static void Main(string[] args)
float[] Tritanopia = BuiltinMatrices.Tritanopia.Cast<float>().ToArray();
float[] Tritanomaly = BuiltinMatrices.Tritanomaly.Cast<float>().ToArray();

float[] ProtanopiaCorrection = BuiltinMatrices.ProtanopiaCorrection.Cast<float>().ToArray();
float[] DeuteranopiaCorrection = BuiltinMatrices.DeuteranopiaCorrection.Cast<float>().ToArray();
float[] TritanopiaCorrection = BuiltinMatrices.TritanopiaCorrection.Cast<float>().ToArray();

float[] Negative = BuiltinMatrices.Negative.Cast<float>().ToArray();
float[] GrayScale = BuiltinMatrices.GrayScale.Cast<float>().ToArray();
float[] NegativeGrayScale = BuiltinMatrices.NegativeGrayScale.Cast<float>().ToArray();
Expand All @@ -88,6 +96,10 @@ static void Main(string[] args)
Tritanopia,
Tritanomaly,

ProtanopiaCorrection,
DeuteranopiaCorrection,
TritanopiaCorrection,

Negative,
GrayScale,
NegativeGrayScale,
Expand All @@ -110,7 +122,7 @@ static void Main(string[] args)
int optionsCount = colorFiltersStr.Count;
int selected = 0;
bool done = false;
List<string> alphabet = new List<string> { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" };
List<string> alphabet = new List<string> { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v" };


while (!done)
Expand Down
56 changes: 56 additions & 0 deletions greys/Transform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/// <summary>
/// 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.
/// </summary>
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<float[,]> Interpolate(float[,] A, float[,] B)
{
const int STEPS = 10;
Expand Down
2 changes: 1 addition & 1 deletion greys/greys.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup Label="Build">
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
Loading