Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Color Gradient Algorithm in Lab color space

Let's say I have two colors in Lab color space-

Color 1: L=81, a=-8, b=74
Color 2: L=64, a=-14, b=3

I want generate n colors between them. For say n=100 or as many colors possible between them.

I know the RGB and HSV color gradient algorithm. But I want to generate gradient in LAB color spaces. I don't want to convert the colors to HSV or RGB as different color models generate different gradients.

This is a link i found which generates gradient in Lab and other color models: http://davidjohnstone.net/pages/lch-lab-colour-gradient-picker

I'm aiming to do something similar in Java but language doesn't matter, I just need to understand the logic and algorithm behind it.

I'm doing this basically for matching a scanned color value with a chart of 5 colors I have. So, I have to first generate all colors in between those 5 colors (using gradient) and compare the other color to find the one which is closest to it. (For comparing I'm using CIEDE2000 Delta-e method). But that's secondary i guess.


Adding further to the last part of my question,

I suppose I'll have to generate a gradient because I want to find the exact location of the color from my sample in the sequence of chart I have.

For eg- I have 6 colors of green shades (light to dark) in my chart each corresponding to particular numeric data between 0 to 450 mg like below (with their LAB values)

Color 1: 78, -10, -71 [0 mg]
Color 2: 73,-14,44 [30 mg]
Color 3: 71, -19, 53 [80 mg]
Color 4: 67, -18, 31 [160 mg]
Color 5: 69, -2, 29  [300 mg]
Color 6: 61, -14, 3 [450 mg]

Now I want to generate all colors between them and find the location of my scanned color and return the mg value. Say my color is exactly between Color 1 and Color 2, then it'll return 15 mg, otherwise if it is closer to Color 2 it'll return 28.5 mg and so on.

Hope this clears up what I'm trying to achieve.

like image 891
Sarthak Singhal Avatar asked Oct 31 '22 13:10

Sarthak Singhal


1 Answers

To generate a gradient between two color values, you can just linearly interpolate between them. This will basically work in any color space, although if the transformation between two color spaces is not linear, the gradient obtained by linearly interpolating in one space will not be linear in the other. Thus, generating gradients in Lab color space is exactly like generating them in, say, RGB space.

(For color spaces like HSV or HSL which have a hue coordinate that "loops around", some extra care may be needed to choose the right direction to interpolate in; fortunately, you're not asking about those color spaces here, so I don't need to go into such details.)


Just as a demonstration, here's how you'd generate an n-sample gradient between the colors c1 and c2 (each given as a LabColor object with properties L, a and b):

public static LabColor[] makeGradient(LabColor c1, LabColor c2, int n) {
    LabColor gradient = new LabColor[n];
    for (int i = 0; i < n; i++) {
        float alpha = (float)i / (n-1);  // 0.0 <= alpha <= 1.0 
        float L = (1-alpha) * c1.L + alpha * c2.L;
        float a = (1-alpha) * c1.a + alpha * c2.a;
        float b = (1-alpha) * c1.b + alpha * c2.b;
        gradient[i] = new LabColor(L, a, b);
    }
    return gradient;
}

This will return a gradient with n color samples, where the first color is equal to c1, the last color is equal to c2, and the rest are interpolated between them.


However, based on the remark at the end of your question, I suspect you don't actually need to generate any gradients. Rather, to find the color in your chart that most closely matches the one in your sample, you just need to calculate the perceptual distance of each chart color from the sample (which, in the Lab color space, can be well approximated simply by their Euclidian distance) and select the closest one:

public static LabColor findNearest(LabColor sample, LabColor[] chart) {
    LabColor nearest = null;
    float minDistanceSquared = Float.POSITIVE_INFINITY;
    for (int i = 0; i < chart.length; i++) {
        float dL = sample.L - chart[i].L;
        float da = sample.a - chart[i].a;
        float db = sample.b - chart[i].b;
        float distanceSquared = dL*dL + da*da + db*db;
        if (distanceSquared < minDistanceSquared) {
            nearest = chart[i];
            minDistanceSquared = distanceSquared;
        }
    }
    return nearest;
}
like image 61
Ilmari Karonen Avatar answered Nov 13 '22 07:11

Ilmari Karonen