ndesmic / cvd-sim

A color vision deficiency simulator in SVG, JS, GLSL and WGSL
MIT License
1 stars 1 forks source link

Partial color blindness #3

Open kachkaev opened 3 years ago

kachkaev commented 3 years ago

Given the existing filters for four conditions, how hard would it be to simulate partial color blindness? I mean protanomaly, deuteranomaly, tritanomaly and achromatomaly.

This is what I get via https://github.com/hail2u/color-blindness-emulation β†’ filters.svg, which give a bit less accurate results:

Screenshot 2021-07-13 at 23 49 05
ndesmic commented 3 years ago

If you have the the LMS space transforms it should be easy. From what I understand partial values have different severities, it depends on the person so there's no perfectly accurate version.

This source has several matrices for different amount of severity in terms of RGB space transforms: https://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/CVD_Simulation.html

kachkaev commented 3 years ago

Yeah you're right. There's no single deuteranomaly, it would be deuteranomaly 10%, deuteranomaly 90% and so on. So to get a matrix, we need the condition and the percentage.

Can the percentage converted to another matrix? So to get the final matrix, we combine four matrices instead of three πŸ€”

kachkaev commented 3 years ago

If you are curious, I've taken your fixed matrices and made it possible to generate a CSS filter on the file: https://github.com/kachkaev/tooling-for-how-old-is-this-house/blob/3e73ce0b02973b2d0677c1b4219f09242c43ca02/src/ui/Legend/helpersForColorBlindness.ts

That's similar to what Google folks were doing in their blog: https://developer.chrome.com/blog/cvd/

I'm thinking of renaming generateColorBlindnessCss to generateColorBlindnessFilter and only return url(...) to make the logic a bit more reusable. It'd be cool to have an API like:

generateColorBlindnessFilter("deuteranopia", { severity: 0.5, method: "machado2009" })
ndesmic commented 3 years ago

It's been a while since I looked at this. But the percentages should be easy.

function lerp(start, end, t) {
    const result = [];
    for (let row = 0; row < start.length; row++) {
        const newRow = [];
        for (let col = 0; col < start[0].length; col++) {
            newRow.push(start[row][col] + (end[row][col] - start[row][col]) * t)
        }
        result.push(newRow);
    } return result;
}

const normalVision = [
               [1, 0, 0, 0],
               [0, 1, 0, 0],
               [0, 0, 1, 0],
               [0, 0, 0, 1]
];

lerp(normalVision, protanopia, 0.5);

This would linearly interpolate the matrix between normal and protanopia where t is between 0 and 1 to get partial values. Or something like that.

kachkaev commented 3 years ago

So if I understand you correctly, you suggest to linearly interpolate two matrices, one with the condition and another one with normal vision (i.e. identity matrix). That makes sense.

Do you think we could do this in the RGB space or would it be more reasonable to use LMS when interpolating? Something like:

- multiply(multiply(multiply(color, rgbToLms), pronaopia), lmsToRgb);
+ multiply(multiply(multiply(color, rgbToLms), lerp(normalVision, pronaopia, 0.5)), lmsToRgb);

I suspect that this may make an important difference, but that’s just intuition rather than scientific reasoning 😁 My understanding of color spaces is quite modest, to be frank.

These questions about partial transforms are mainly driven by curiosity, at least for now πŸ˜… I have already achieved my practical goal with your matrices, which are great! Don’t feel oblidged to help me with this if you are busy, but if you are curious too, happy to continue this discussion πŸ™‚

ndesmic commented 3 years ago

It shouldn't matter. If you've already have the RGB space matrix you'll save yourself a few repeated ops by using it.