Jump to content

Colorblindness simulation plugin [v5 2022-06-28]


Recommended Posts

[June 28th 2022] Version 5 available!

 

This plugin can be used to simulate certain colorblindness types such as: protanopia, deuteranopia, tritanopia, achromatopsia and blue-cone monochromacy.

 

Download:

colorblindness.zip

Can be found in Effects > Color > Colorblindness

Note: if you have an older version installed, remove it manually from the Paint.NET Effects folder. The old version is called colorblindness4.dll.

The new version does not include the version number in the name.

 

Preview:

demo.thumb.jpg.b1d66071ceef7d13ec23c6849b35cc1e.jpg

 

Code:

Spoiler
#region UICode
ListBoxControl<Deficiency> type = Deficiency.Default; //Color blindness type|Protanopia|Deuteranopia|Tritanopia|Achromatopsia|Blue-Cone Monochromacy|Normal Vision
#endregion

enum Deficiency
{
    [LmsMatrix(
        0,  1.05118294,  -0.05116099,
        0,  1,            0,
        0,  0,            1)]
    Protanopia,

    [LmsMatrix(
        1,         0,  0,
        0.9513092, 0,  0.04866992,
        0,         0,  1)]
    Deuteranopia,

    [LmsMatrix(
        1,           0,          0,
        0,           1,          0,
       -0.86744736, 1.86727089,  0)]
    Tritanopia,

    [LmsMatrix(
        0.4,  0.6,  0,
        0.4,  0.6,  0,
        0.4,  0.6,  0)]
    Achromatopsia,

    [LmsMatrix(
        0, 0, 1,
        0, 0, 1,
        0, 0, 1
    )]
    Blue_Cone_Monochromacy,
    
    [LmsMatrix(
        1,  0,  0,
        0,  1,  0,
        0,  0,  1)]
    Default
}

static double[,] toLmsMatrix = toMatrix(3, 3,
    0.31399022, 0.63951294, 0.04649755,
    0.15537241, 0.75789446, 0.08670142,
    0.01775239, 0.10944209, 0.87256922);

static double[,] toLinearRgbMatrix = toMatrix(3, 3,
    5.47221206, -4.6419601, 0.16963708,
    -1.1252419, 2.29317094, -0.1678952,
    0.02980165, -0.19318073, 1.16364789);

class LmsMatrix: Attribute
{
    public double[,] matrix {get; private set;}

    internal LmsMatrix(params double[] matrix)
    {
        this.matrix = toMatrix(3, 3, matrix);
    }

    public static LmsMatrix getMatrix(Deficiency d)
    {
        return (LmsMatrix) Attribute.GetCustomAttribute(deficiencyOf(d), typeof(LmsMatrix));
    }

    static MemberInfo deficiencyOf(Deficiency d)
    {
        return typeof(Deficiency).GetField(Enum.GetName(typeof(Deficiency), d));
    }
}

static double[,] toLinear(params byte[] rgb)
{
    double[,] linear = new double[rgb.Length, 1];
    for (int i = 0; i < linear.Length; i++)
    {
        linear[i, 0] = Math.Clamp(rgb[i] <= 0.04045 * 255.0
                ? (rgb[i] / 255.0) / 12.92
                : Math.Pow((rgb[i] / 255.0 + 0.055) / 1.055, 2.4),
            0.0, 1.0);
    }
    return linear;
}

static byte[] fromLinear(double[,] linear)
{
    byte[] normal = new byte[linear.GetLength(0)];
    for (int i = 0; i < normal.Length; i++)
    {
        double l = Math.Clamp(linear[i, 0], 0.0, 1.0);
        normal[i] = Math.Clamp((byte) (l <= 0.0031308
                ? l * 255.0 * 12.92
                : 255.0 * (1.055 * Math.Pow(l, 0.41666) - 0.055)),
            (byte) 0, (byte) 255);
    }
    return normal;
}

static double[,] multiplyMatrix(double[,] a, double[,] b)
{
    int rA = a.GetLength(0);
    int cA = a.GetLength(1);
    int cB = b.GetLength(1);
    double[,] result = new double[rA, cB];

    for (int i = 0; i < rA; i++)
    {
        for (int j = 0; j < cB; j++)
        {
            double temp = 0;
            for (int k = 0; k < cA; k++)
            {
                temp += a[i, k] * b[k, j];
            }
            result[i, j] = temp;
        }
    }
    return result;
}

static double[,] toMatrix(int rows, int columns, params double[] values)
{
    double[,] result = new double[rows, columns];
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            result[i, j] = values[j + i * columns];
        }
    }
    return result;
}

static double[,] getColorBlindnessMatrix(double[,] lmsMatrix)
{
    return multiplyMatrix(
        multiplyMatrix(
            toLinearRgbMatrix,
            lmsMatrix),
        toLmsMatrix
    );
}

static ColorBgra applyColorBlindnessMatrix(double[,] matrix, ColorBgra pixel)
{
    byte[] rgb = new byte[]{pixel.R, pixel.G, pixel.B};
    byte[] newRgb = fromLinear(
        multiplyMatrix(
            matrix,
            toLinear(rgb)
        )
    );
    return ColorBgra.FromBgra(newRgb[2], newRgb[1], newRgb[0], pixel.A);
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    double[,] matrix = getColorBlindnessMatrix(LmsMatrix.getMatrix(type).matrix);

    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            dst[x,y] = applyColorBlindnessMatrix(matrix, src[x,y]);
        }
    }
}

 

Edited by pascal
Removed redundant code
  • Like 2
  • Upvote 4
Link to comment
Share on other sites

Hi Pascal & welcome to the forum :)

 

Good job on this plugin. There is this older plugin - but it doesn't do the tritanopia effect....https://forums.getpaint.net/topic/19780-colourblind-simulation/

 

If you want help cleaning up your code post it over here: https://forums.getpaint.net/forum/17-plugin-developers-central/

 

Link to comment
Share on other sites

  • 4 weeks later...
  • 2 weeks later...
On 6/7/2020 at 9:44 PM, pascal said:

New version available!

Thank you @pascal for the new version. This post will let everyone know there's been a change, so that they download the new version.

 

You should really make a new post when you update the plugin, otherwise users will have no way of knowing. I was only alerted to this because of your posts on the General Discussion forum.  

 

Also, it's better not to use the plugin version number in the name of the DLL file itself. If the file name of the newer version is not identical to the previous version, it will not replace the older one in the Effects folder. This will result with two (or more) copies of your plugin in the Effects folder and whatever problems that may cause. 

 

Naming the containing zip file with the version number is OK, even helpful. I like it and wish plugin authors would adopt it as a standard.

 

Edited by Djisves
version number

Xkds4Lh.png

Link to comment
Share on other sites

  • pascal changed the title to Colorblindness simulation plugin [v5 2022-06-28]

Version 5 available

- The new version is more accurate than before. It converts the RGB values to the LMS color space and takes gamma correction into account.

- Protanomaly, deuteranomaly, tritanomaly and achromatomaly are removed from the plugin because those were just blending between normal vision and the corresponding deficiency.

- The deficiency 'Blue-Cone Monochromacy' was added as a second type of monochromacy.

  • Like 2
  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...