Domanda

Domanda originale

Sto cercando una funzione che tenti di quantificare quanto sono "distanti" (o distinti) due colori.Questa domanda è in realtà divisa in due parti:

  1. Quale spazio colore rappresenta meglio la visione umana?
  2. Quale metrica di distanza in quello spazio rappresenta meglio la visione umana (euclidea?)
È stato utile?

Soluzione

Converti in La*b* (ovvero semplicemente "Lab" e vedrai anche il riferimento a "CIELAB").Una buona misura rapida della differenza di colore è

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

Gli scienziati del colore hanno altre misure più raffinate, che potrebbero non valere la pena, a seconda della precisione necessaria per quello che stai facendo.

IL a E b i valori rappresentano i colori opposti in un modo simile a come funzionano i coni e possono essere negativi o positivi.Colori neutri: bianco, grigi a=0,b=0.IL L è la luminosità definita in un modo particolare, da zero (oscurità pura) fino a qualunque cosa.

Spiegazione grossolana:>> Dato un colore, i nostri occhi distinguono tra due ampie gamme di lunghezze d'onda: il blu e le lunghezze d'onda più lunghe.e poi, grazie ad una mutazione genetica più recente, i coni con lunghezza d'onda maggiore si sono biforcati in due, distinguendo per noi il rosso vs.verde.

A proposito, sarà fantastico per la tua carriera superare i colleghi cavernicoli del colore che conoscono solo "RGB" o "CMYK" che sono ottimi per i dispositivi ma fanno schifo per un serio lavoro di percezione.Ho lavorato per scienziati dell'imaging che non sapevano nulla di queste cose!

Per una lettura più divertente sulla teoria della differenza di colore, prova:

Maggiori dettagli su Lab a http://en.kioskea.net/video/cie-lab.php3 Al momento non riesco a trovare una pagina non brutta che contenga effettivamente le formule di conversione, ma sono sicuro che qualcuno modificherà questa risposta per includerne una.

Altri suggerimenti

poiché il collegamento cmetric.htm sopra non è riuscito per me, così come molte altre implementazioni per la distanza del colore ho trovato (dopo un viaggio molto lungo...) come calcolare la migliore distanza del colore e...quello scientificamente più accurato: deltaE e da 2 valori RGB (!) utilizzando OpenCV:

Ciò ha richiesto 3 conversioni dello spazio colore + alcune conversioni di codice da Javascript (http://svn.int64.org/viewvc/int64/colors/colors.js) al C++

E infine il codice (sembra funzionare subito, spero che nessuno trovi un bug serio lì...ma sembra a posto dopo una serie di test)

#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 );
}

Spero che aiuti qualcuno :)

HSL e HSV sono migliori per la percezione umana del colore.Secondo Wikipedia:

A volte è preferibile, quando si lavora con materiali artistici, immagini digitalizzate o altri media, utilizzare il modello di colore HSV o HSL rispetto a modelli alternativi come RGB o CMYK, a causa delle differenze nel modo in cui i modelli emulano il modo in cui gli esseri umani percepiscono il colore.RGB e CMYK sono rispettivamente modelli additivi e sottrattivi, che modellano il modo in cui le luci o i pigmenti dei colori primari (rispettivamente) si combinano per formare nuovi colori quando miscelati.

Graphical depiction of HSV

IL Articolo di Wikipedia sulle differenze di colore elenca una serie di spazi colore e parametri di distanza progettati per concordare con la percezione umana delle distanze colore.

Potrebbe sembrare spam ma no, questo collegamento è davvero interessante per gli spazi colore :)

http://www.compuphase.com/cmetric.htm

Il più facile distanza ovviamente bisognerebbe considerare i colori come vettori 3D che hanno la stessa origine e prendendo la distanza tra i loro punti finali.

Se è necessario considerare fattori tali che il verde sia più prominente nel giudicare l'intensità, è possibile pesare i valori.

ImageMagic prevede le seguenti scale:

  • rosso:0,3
  • verde:0,6
  • blu:0,1

Naturalmente, valori come questo sarebbero significativi solo in relazione ad altri valori per altri colori, non come qualcosa che sarebbe significativo per gli esseri umani, quindi tutto ciò per cui potresti usare i valori sarebbe l'ordinamento per similarità.

Bene, come primo punto di riferimento, direi che le metriche comuni HSV (tonalità, saturazione e valore) o HSL sono meglio rappresentative del modo in cui gli esseri umani percepiscono il colore rispetto a RGB o CYMK.Vedere HSL, HSV su Wikipedia.

Suppongo ingenuamente di tracciare i punti nello spazio HSL per i due colori e calcolare la grandezza del vettore differenza.Tuttavia ciò significherebbe che il giallo brillante e il verde brillante sarebbero considerati altrettanto diversi quanto il verde dal verde scuro.Ma del resto molti considerano il rosso e il rosa due colori diversi.

Inoltre, i vettori differenza nella stessa direzione in questo spazio dei parametri non sono uguali.Ad esempio, l’occhio umano percepisce il verde molto meglio degli altri colori.Uno spostamento di tonalità dal verde della stessa entità di uno spostamento dal rosso può sembrare maggiore.Anche uno spostamento della saturazione da una piccola quantità a zero è la differenza tra grigio e rosa, altrove lo spostamento sarebbe la differenza tra due tonalità di rosso.

Dal punto di vista del programmatore, dovresti tracciare i vettori delle differenze ma modificati da una matrice di proporzionalità che regolerebbe le lunghezze di conseguenza in varie regioni dello spazio HSL - questo sarebbe abbastanza arbitrario e sarebbe basato su varie idee di teoria dei colori ma essere modificato in modo abbastanza arbitrario a seconda di cosa si desidera applicare.

Ancora meglio, potresti vedere se qualcuno ha già fatto una cosa del genere online...

Essendo daltonico, credo che sia positivo provare ad aggiungere più separazione rispetto alla visione normale.La forma più comune di daltonismo è la carenza di rosso/verde.Ciò non significa che non puoi vedere il rosso o il verde, significa che è più difficile vedere e più difficile vedere le differenze.Quindi è necessaria una separazione più ampia prima che una persona daltonica possa notare la differenza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top