Followup: Finding an accurate "distance" between colors
Asked Answered
Z

7

53

Original Question

I am looking for a function that attempts to quantify how "distant" (or distinct) two colors are. This question is really in two parts:

  1. What color space best represents human vision?
  2. What distance metric in that space best represents human vision (euclidean?)
Zia answered 4/8, 2008 at 15:8 Comment(1)
I ended up plotting some of the solutions below when dealing with a similar situation. Others may find the plots interesting https://mcmap.net/q/21771/-calculate-how-humans-perceive-similarity-between-different-colours/156755Tell
C
48

Convert to La*b* (aka just plain "Lab", and you'll also see reference to "CIELAB"). A good quick measaure of color difference is

(L1-L2)^2 + (a1-a2)^2 + (b1-b2)^2

Color scientists have other more refined measures, which may not be worth the bother, depending on accuracy needed for what you're doing.

The a and b values represent opposing colors in a way similar to how cones work, and may be negative or positive. Neutral colors - white, grays are a=0,b=0. The L is brightness defined in a particular way, from zero (pure darkness) up to whatever.

Crude explanation :>> Given a color, our eyes distinguish between two broad ranges of wavelength - blue vs longer wavelengths. and then, thanks to a more recent genetic mutation, the longer wavelength cones bifurcated into two, distinguishing for us red vs. green.

By the way, it'll be great for your career to rise above your color caveman collegues who know of only "RGB" or "CMYK" which are great for devices but suck for serious perception work. I've worked for imaging scientists who didn't know a thing about this stuff!

For more fun reading on color difference theory, try:

More detail on Lab at http://en.kioskea.net/video/cie-lab.php3 I can't at this time find a non-ugly page that actually had the conversion formulas but I'm sure someone will edit this answer to include one.

Condensed answered 16/9, 2008 at 16:8 Comment(3)
Regarding the ugliness of the conversion formulas: they're ugly for a reason, since getting from RGB to XYZ to LAB depends on viewing conditions. cf: (warning, ugliness) easyrgb.com/index.php?X=MATHLonghand
Possibly it'd be worth adding a description of standard color distance metrics and pseudometrics defined by CIE: en.wikipedia.org/wiki/Color_differencePlain
For anyone wondering by the way this is half of the Pythagoras theorem which gives the (incorrect mind you, this is a non-linear distance calculation in a space that is supposed to be uniform) distance between the two colours (points) in the 3d space the LAB model occupies. There's much more information here on this subject en.wikipedia.org/wiki/Color_differenceTurncoat
F
9

as cmetric.htm link above failed for me, as well as many other implementations for color distance I found (after a very long jurney..) how to calculate the best color distance, and .. most scientifically accurate one: deltaE and from 2 RGB (!) values using OpenCV:

This required 3 color space conversions + some code conversion from javascript (http://svn.int64.org/viewvc/int64/colors/colors.js) to C++

And finally the code (seems to work right out of the box, hope no one finds a serious bug there ... but it seems fine after a number of tests)

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/photo/photo.hpp>
#include <math.h>

using namespace cv;
using namespace std;

#define REF_X 95.047; // Observer= 2°, Illuminant= D65
#define REF_Y 100.000;
#define REF_Z 108.883;

void bgr2xyz( const Vec3b& BGR, Vec3d& XYZ );
void xyz2lab( const Vec3d& XYZ, Vec3d& Lab );
void lab2lch( const Vec3d& Lab, Vec3d& LCH );
double deltaE2000( const Vec3b& bgr1, const Vec3b& bgr2 );
double deltaE2000( const Vec3d& lch1, const Vec3d& lch2 );


void bgr2xyz( const Vec3b& BGR, Vec3d& XYZ )
{
    double r = (double)BGR[2] / 255.0;
    double g = (double)BGR[1] / 255.0;
    double b = (double)BGR[0] / 255.0;
    if( r > 0.04045 )
        r = pow( ( r + 0.055 ) / 1.055, 2.4 );
    else
        r = r / 12.92;
    if( g > 0.04045 )
        g = pow( ( g + 0.055 ) / 1.055, 2.4 );
    else
        g = g / 12.92;
    if( b > 0.04045 )
        b = pow( ( b + 0.055 ) / 1.055, 2.4 );
    else
        b = b / 12.92;
    r *= 100.0;
    g *= 100.0;
    b *= 100.0;
    XYZ[0] = r * 0.4124 + g * 0.3576 + b * 0.1805;
    XYZ[1] = r * 0.2126 + g * 0.7152 + b * 0.0722;
    XYZ[2] = r * 0.0193 + g * 0.1192 + b * 0.9505;
}

void xyz2lab( const Vec3d& XYZ, Vec3d& Lab )
{
    double x = XYZ[0] / REF_X;
    double y = XYZ[1] / REF_X;
    double z = XYZ[2] / REF_X;
    if( x > 0.008856 )
        x = pow( x , .3333333333 );
    else
        x = ( 7.787 * x ) + ( 16.0 / 116.0 );
    if( y > 0.008856 )
        y = pow( y , .3333333333 );
    else
        y = ( 7.787 * y ) + ( 16.0 / 116.0 );
    if( z > 0.008856 )
        z = pow( z , .3333333333 );
    else
        z = ( 7.787 * z ) + ( 16.0 / 116.0 );
    Lab[0] = ( 116.0 * y ) - 16.0;
    Lab[1] = 500.0 * ( x - y );
    Lab[2] = 200.0 * ( y - z );
}

void lab2lch( const Vec3d& Lab, Vec3d& LCH )
{
    LCH[0] = Lab[0];
    LCH[1] = sqrt( ( Lab[1] * Lab[1] ) + ( Lab[2] * Lab[2] ) );
    LCH[2] = atan2( Lab[2], Lab[1] );
}

double deltaE2000( const Vec3b& bgr1, const Vec3b& bgr2 )
{
    Vec3d xyz1, xyz2, lab1, lab2, lch1, lch2;
    bgr2xyz( bgr1, xyz1 );
    bgr2xyz( bgr2, xyz2 );
    xyz2lab( xyz1, lab1 );
    xyz2lab( xyz2, lab2 );
    lab2lch( lab1, lch1 );
    lab2lch( lab2, lch2 );
    return deltaE2000( lch1, lch2 );
}

double deltaE2000( const Vec3d& lch1, const Vec3d& lch2 )
{
    double avg_L = ( lch1[0] + lch2[0] ) * 0.5;
    double delta_L = lch2[0] - lch1[0];
    double avg_C = ( lch1[1] + lch2[1] ) * 0.5;
    double delta_C = lch1[1] - lch2[1];
    double avg_H = ( lch1[2] + lch2[2] ) * 0.5;
    if( fabs( lch1[2] - lch2[2] ) > CV_PI )
        avg_H += CV_PI;
    double delta_H = lch2[2] - lch1[2];
    if( fabs( delta_H ) > CV_PI )
    {
        if( lch2[2] <= lch1[2] )
            delta_H += CV_PI * 2.0;
        else
            delta_H -= CV_PI * 2.0;
    }

    delta_H = sqrt( lch1[1] * lch2[1] ) * sin( delta_H ) * 2.0;
    double T = 1.0 -
            0.17 * cos( avg_H - CV_PI / 6.0 ) +
            0.24 * cos( avg_H * 2.0 ) +
            0.32 * cos( avg_H * 3.0 + CV_PI / 30.0 ) -
            0.20 * cos( avg_H * 4.0 - CV_PI * 7.0 / 20.0 );
    double SL = avg_L - 50.0;
    SL *= SL;
    SL = SL * 0.015 / sqrt( SL + 20.0 ) + 1.0;
    double SC = avg_C * 0.045 + 1.0;
    double SH = avg_C * T * 0.015 + 1.0;
    double delta_Theta = avg_H / 25.0 - CV_PI * 11.0 / 180.0;
    delta_Theta = exp( delta_Theta * -delta_Theta ) * ( CV_PI / 6.0 );
    double RT = pow( avg_C, 7.0 );
    RT = sqrt( RT / ( RT + 6103515625.0 ) ) * sin( delta_Theta ) * -2.0; // 6103515625 = 25^7
    delta_L /= SL;
    delta_C /= SC;
    delta_H /= SH;
    return sqrt( delta_L * delta_L + delta_C * delta_C + delta_H * delta_H + RT * delta_C * delta_H );
}

Hope it helps someone :)

Fingernail answered 17/10, 2013 at 1:39 Comment(0)
D
6

HSL and HSV are better for human color perception. According to Wikipedia:

It is sometimes preferable in working with art materials, digitized images, or other media, to use the HSV or HSL color model over alternative models such as RGB or CMYK, because of differences in the ways the models emulate how humans perceive color. RGB and CMYK are additive and subtractive models, respectively, modelling the way that primary color lights or pigments (respectively) combine to form new colors when mixed.

Graphical depiction of HSV

Decorative answered 4/8, 2008 at 15:16 Comment(4)
Watch out: red is at 0°, so yellowish red is at +10° and blueish red is at -10° or 350°. Calculating the distance isn't just as easy as subtracting the two values now.Actinology
Not really helpful in the context of the other answer. Metrics defined by CIE are better for human color perception not HLS and HSV.Plain
In this paper, there is a formula for color distance in HSV space citeseerx.ist.psu.edu/viewdoc/…Briareus
People love to repeat after each other that thing about CIE that it's better than HSV. but they never have a proof. The bicone HSV distance works well for me so far. github.com/Nakilon/colorutils/blob/…Antilogy
F
4

The easiest distance would of course be to just consider the colors as 3d vectors originating from the same origin, and taking the distance between their end points.

If you need to consider such factors that green is more prominent in judging intensity, you can weigh the values.

ImageMagic provides the following scales:

  • red: 0.3
  • green: 0.6
  • blue: 0.1

Of course, values like this would only be meaningful in relation to other values for other colors, not as something that would be meaningful to humans, so all you could use the values for would be similiarity ordering.

Flavescent answered 4/8, 2008 at 15:14 Comment(0)
B
4

Well, as a first point of call, I'd say of the common metrics HSV (Hue, Saturation and Value) or HSL are better representative of how humans perceive colour than say RGB or CYMK. See HSL, HSV on Wikipedia.

I suppose naively I would plot the points in the HSL space for the two colours and calculate the magnitude of the difference vector. However this would mean that bright yellow and bright green would be considered just as different as green to dark green. But then many consider red and pink two different colours.

Moreover, difference vectors in the same direction in this parameter space are not equal. For instance, the human eye picks up green much better than other colours. A shift in hue from green by the same amount as a shift from red may seem greater. Also a shift in saturation from a small amount to zero is the difference between grey and pink, elsewhere the shift would be the difference between two shades of red.

From a programmers point of view, you would need to plot the difference vectors but modified by a proportionality matrix that would adjust the lengths accordingly in various regions of the HSL space - this would be fairly arbitrary and would be based on various colour theory ideas but be tweaked fairly arbitrarily depending on what you wanted to apply this to.

Even better, you could see if anyone has already done such a thing online...

Balladeer answered 4/8, 2008 at 15:37 Comment(0)
C
4

The Wikipedia article on color differences lists a number of color spaces and distance metrics designed to agree with human perception of color distances.

Caucus answered 24/8, 2008 at 15:38 Comment(0)
S
2

As someone who is color blind I believe it is good to try to add more separation then normal vision. The most common form of color blindness is red/green deficiency. It doesn't mean that you can't see red or green, it means that it is more difficult to see and more difficult to see the differences. So it takes a larger separation before a color blind person can tell the difference.

Sardou answered 16/9, 2008 at 16:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.