2つの色がどれだけ「遠い」(またははっきりしている)かを定量化しようとする関数を探しています。この質問は実際には2つの部分に分かれています。
- 人間の視覚を最もよく表す色空間はどれですか?
- その空間のどの距離計量が人間の視覚を最もよく表しているか(ユークリッド?)
2つの色がどれだけ「遠い」(またははっきりしている)かを定量化しようとする関数を探しています。この質問は実際には2つの部分に分かれています。
La*b* に変換します (単純な "Lab" とも呼ばれ、"CIELAB" への参照も表示されます)。色の違いを簡単に測定するには、
(L1-L2)^2 + (a1-a2)^2 + (b1-b2)^2
色の科学者は、他のより洗練された測定法を持っていますが、あなたがしていることに必要な精度によっては、面倒な価値はないかもしれません.
a
とのb
値は、円錐がどのように機能するかに似た方法で反対の色を表し、負または正の場合があります。ニュートラルカラー - ホワイト、グレーはa=0
、b=0
. はL
、特定の方法で定義された明るさであり、ゼロ (純粋な暗闇) から任意の値までです。
大まかな説明 :>> 色が与えられると、私たちの目は 2 つの広い波長範囲 (青と長波長) を区別します。そして、より最近の遺伝子変異のおかげで、より長い波長のコーンが 2 つに分岐し、赤と緑を区別しました。
ところで、「RGB」や「CMYK」はデバイスには適しているが、本格的な認識作業には向いていないことしか知らない、色の穴場の同僚よりも上に上がれば、あなたのキャリアが素晴らしいものになるでしょう。私は、このことについて何も知らなかった画像科学者のために働いてきました!
色差理論をもっと楽しく読むには、次のことを試してください。
Lab の詳細はhttp://en.kioskea.net/video/cie-lab.php3にあります。現時点では、実際に変換式が記載された見苦しくないページを見つけることはできませんが、誰かがこれを編集してくれると確信しています。 1つを含めるように答えてください。
上記のcmetric.htmリンクが失敗したため、および私が見つけた色距離の他の多くの実装(非常に長い旅の後..)最良の色距離を計算する方法、および..最も科学的に正確なもの:deltaEおよびfrom 2 OpenCV を使用した RGB (!) 値:
これには、3 つの色空間変換と、javascript ( http://svn.int64.org/viewvc/int64/colors/colors.js ) から C++ へのコード変換が必要でした。
そして最後に、コード (箱から出してすぐに動作するようです。深刻なバグが見つからないことを願っています...しかし、多くのテストの後は問題ないようです)
#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 );
}
それが誰かを助けることを願っています:)
HSLとHSVは、人間の色覚に適しています。ウィキペディアによると:
モデルが人間の色の知覚方法をエミュレートする方法が異なるため、画材、デジタル画像、またはその他のメディアを使用する場合は、RGBやCMYKなどの代替モデルよりもHSVまたはHSLカラーモデルを使用する方が望ましい場合があります。RGBとCMYKは、それぞれ加法混色モデルと減法混色モデルであり、原色のライトまたは顔料が(それぞれ)組み合わされて、混合されたときに新しい色を形成する方法をモデル化します。
もちろん、最も簡単な距離は、色を同じ原点から発生する3Dベクトルと見なし、それらの端点間の距離を取ることです。
強度を判断する際に緑がより目立つような要因を考慮する必要がある場合は、値を比較検討することができます。
ImageMagicは次のスケールを提供します:
もちろん、このような値は、他の色の他の値との関係でのみ意味があり、人間にとって意味のあるものとしては意味がないため、値を使用できるのは類似性の順序だけです。
色の違いに関するウィキペディアの記事には、色の距離に対する人間の知覚に一致するように設計された多くの色空間と距離測定基準がリストされています。
さて、最初の呼びかけとして、HSV(色相、彩度、明度)またはHSLは、RGBやCYMKよりも人間が色を知覚する方法をよりよく表していると思います。ウィキペディアのHSL、HSVを参照してください。
素朴に、2色のHSL空間の点をプロットし、差ベクトルの大きさを計算すると思います。ただし、これは、明るい黄色と明るい緑が、緑から濃い緑と同じように異なると見なされることを意味します。しかし、多くの人が赤とピンクの2つの異なる色を検討しています。
さらに、このパラメータ空間の同じ方向の差ベクトルは等しくありません。たとえば、人間の目は他の色よりもはるかによく緑を拾います。赤からのシフトと同じ量の緑からの色相のシフトは、より大きく見える場合があります。また、彩度の少量からゼロへのシフトは、グレーとピンクの違いです。それ以外の場合、シフトは、2つの赤の色合いの違いになります。
プログラマーの観点からは、差ベクトルをプロットする必要がありますが、HSL空間のさまざまな領域でそれに応じて長さを調整する比例行列によって変更されます-これはかなり恣意的であり、さまざまな色彩理論のアイデアに基づいていますがこれを何に適用したいかに応じて、かなり恣意的に微調整します。
さらに良いことに、誰かがすでにそのようなことをオンラインで行っているかどうかを確認できます...
色覚異常の人として、私は通常の視力よりも多くの分離を追加しようとするのは良いことだと思います。色覚異常の最も一般的な形態は、赤/緑の欠乏です。赤や緑が見えないという意味ではなく、違いが見えにくく、見えにくいということです。したがって、色覚異常の人が違いを見分けるには、より大きな分離が必要です。