205

事前に定義された 5 つの色の中から、どの色が可変色にどの程度似ているか、またその割合を評価するのに役立つプログラムを設計したいと考えています。問題は、それを手動で段階的に行う方法がわからないということです。そのため、プログラムを考えるのはさらに困難です。

詳細: 色は、異なる色のゲルを含むチューブの写真からのものです。異なる色の 5 つのチューブがあり、それぞれが 5 つのレベルのうちの 1 つを表しています。他のサンプルの写真を撮り、コンピュータ上で色を比較してそのサンプルがどのレベルに属しているかを評価し、近似のパーセンテージも知りたい. 次のようなプログラムが欲しいです: http://www.colortools.net/color_matcher.html

私が手動で考えて実行するものであっても、どのような手順を踏むべきか教えていただければ. とても役に立ちます。

4

19 に答える 19

154

適切なリードについては、ウィキペディアの色の違いに関する記事を参照してください。基本的に、多次元色空間で距離メトリックを計算する必要があります。

ただし、 「知覚的に均一」RGBではないため、Vadim によって提案されたユークリッド距離メトリックは、人間が知覚する色間の距離と一致しません。まず、知覚的に均一な色空間を意図しており、deltaE メトリックが一般的に使用されています。しかし、より洗練された色空間とより洗練された deltaE 式があり、人間の知覚と一致するように近づいています。RGBL*a*b*

変換を行うには、色空間と光源についてさらに学ぶ必要があります。しかし、ユークリッドRGBメトリックよりも優れた簡単な数式を作成するには、次のようにします。

  • RGB値が色空間にあるsRGBと仮定します
  • 変換式sRGBを見つけるL*a*b*
  • あなたのsRGB色をL*a*b*
  • L*a*b*2 つの値の間の deltaE を計算します

計算コストは​​高くありません。非線形の数式と乗算と加算だけです。

于 2012-01-26T14:20:29.467 に答える
55

最初に頭に浮かんだアイデアです(バカならごめんなさい)。色の3つのコンポーネントは、ポイントの3D座標と見なすことができ、ポイント間の距離を計算できます。

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

色間の距離は

d=sqrt((r2-r1)^2+(g2-g1)^2+(b2-b1)^2)

パーセンテージは

p=d/sqrt((255)^2+(255)^2+(255)^2)
于 2012-01-26T12:33:02.223 に答える
37

実は私も数ヶ月前に同じ道を歩きました。質問に対する完璧な答えはありません (ここで数回尋ねられました) が、sqrt(rr) などの答えよりも洗練されていて、あらゆる種類の代替色に移動することなく、RGB で直接実装するのがより簡単な答えがあります。スペース。私はこの式をここで見つけました。これは、非常に複雑な実際のの低コストの近似値です(色の W3C である CIE による。これは未完成のクエストであるため、古くて単純な色差式を見つけることができます)。幸運を。

編集:後世のために、関連するCコードは次のとおりです。

typedef struct {
     unsigned char r, g, b;
} RGB;

double ColourDistance(RGB e1, RGB e2)
{
    long rmean = ( (long)e1.r + (long)e2.r ) / 2;
    long r = (long)e1.r - (long)e2.r;
    long g = (long)e1.g - (long)e2.g;
    long b = (long)e1.b - (long)e2.b;
    return sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}
于 2012-01-31T19:19:01.190 に答える
24

色の値には複数の次元があるため、2つの色を比較する本質的な方法はありません。ユースケースについて、色の意味を決定し、それによってそれらを最もよく比較する方法を決定する必要があります。

ほとんどの場合、赤/緑/青のコンポーネントとは対照的に、色の色相、彩度、および/または明度のプロパティを比較する必要があります。それらをどのように比較したいかがわからない場合は、サンプルの色のペアをいくつか取り、それらを精神的に比較してから、それらが類似/異なる理由を正当化/説明してみてください。

比較する色のプロパティ/コンポーネントがわかったら、色からその情報を抽出する方法を理解する必要があります。

ほとんどの場合、色を一般的なRedGreenBlue表現からHueSaturationLightnessに変換してから、次のように計算する必要があります。

avghue = (color1.hue + color2.hue)/2
distance = abs(color1.hue-avghue)

この例では、色のグラデーション/色相が互いにどれだけ離れているかを示す単純なスカラー値が得られます。

ウィキペディアのHSLとHSVを参照してください。

于 2012-01-26T12:42:48.567 に答える
24

との2 つのColorオブジェクトがある場合、 の各 RGB 値を の RGB 値と比較するだけです。c1c2c1c2

int diffRed   = Math.abs(c1.getRed()   - c2.getRed());
int diffGreen = Math.abs(c1.getGreen() - c2.getGreen());
int diffBlue  = Math.abs(c1.getBlue()  - c2.getBlue());

これらの値を彩度の差 (255) で割ると、2 つの差が得られます。

float pctDiffRed   = (float)diffRed   / 255;
float pctDiffGreen = (float)diffGreen / 255;
float pctDiffBlue   = (float)diffBlue  / 255;

その後、平均色差をパーセンテージで見つけることができます。

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

c1との間のパーセンテージの違いが得られますc2

于 2012-01-26T12:28:56.703 に答える
10

ちょうど別の答えですが、それはSuprのものと似ていますが、色空間が異なります。

重要なのは、人間は色の違いを均一に認識しておらず、RGB色空間はこれを無視しているということです。その結果、RGB色空間を使用し、2色間のユークリッド距離を計算すると、数学的には絶対的に正しい差が得られる可能性がありますが、人間が言うこととは一致しません。

これは問題ではないかもしれません-違いはそれほど大きくないと思いますが、この「より良い」を解決したい場合は、RGBカラーを上記の問題を回避するために特別に設計された色空間に変換する必要があります。以前のモデルからの改善点がいくつかあります(これは人間の知覚に基づいているため、実験データに基づいて「正しい」値を測定する必要があります)。変換するのは少し複雑ですが、私が最高だと思うラボ色空間があります。より単純なのはCIEXYZです

これは、少し実験できるように、異なる色空間間で変換する式をリストしたサイトです。

于 2012-01-26T13:04:06.210 に答える
3

以下のすべてのメソッドは、0 ~ 100 のスケールになります。

internal static class ColorDifference
{
    internal enum Method
    {
        Binary, // true or false, 0 is false
        Square,
        Dimensional,
        CIE76
    }

    public static double Calculate(Method method, int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return Calculate(method, c1[1], c2[1], c1[2], c2[2], c1[3], c2[3], c1[0], c2[0]);
    }

    public static double Calculate(Method method, int r1, int r2, int g1, int g2, int b1, int b2, int a1 = -1, int a2 = -1)
    {
        switch (method)
        {
            case Method.Binary:
                return (r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2) ? 0 : 100;
            case Method.CIE76:
                return CalculateCIE76(r1, r2, g1, g2, b1, b2);
            case Method.Dimensional:
                if (a1 == -1 || a2 == -1) return Calculate3D(r1, r2, g1, g2, b1, b2);
                else return Calculate4D(r1, r2, g1, g2, b1, b2, a1, a2);
            case Method.Square:
                return CalculateSquare(r1, r2, g1, g2, b1, b2, a1, a2);
            default:
                throw new InvalidOperationException();
        }
    }

    public static double Calculate(Method method, Color c1, Color c2, bool alpha)
    {
        switch (method)
        {
            case Method.Binary:
                return (c1.R == c2.R && c1.G == c2.G && c1.B == c2.B && (!alpha || c1.A == c2.A)) ? 0 : 100;
            case Method.CIE76:
                if (alpha) throw new InvalidOperationException();
                return CalculateCIE76(c1, c2);
            case Method.Dimensional:
                if (alpha) return Calculate4D(c1, c2);
                else return Calculate3D(c1, c2);
            case Method.Square:
                if (alpha) return CalculateSquareAlpha(c1, c2);
                else return CalculateSquare(c1, c2);
            default:
                throw new InvalidOperationException();
        }
    }

    // A simple idea, based on on a Square
    public static double CalculateSquare(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return CalculateSquare(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3]);
    }

    public static double CalculateSquare(Color c1, Color c2)
    {
        return CalculateSquare(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B);
    }

    public static double CalculateSquareAlpha(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return CalculateSquare(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3], c1[0], c2[0]);
    }

    public static double CalculateSquareAlpha(Color c1, Color c2)
    {
        return CalculateSquare(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B, c1.A, c2.A);
    }

    public static double CalculateSquare(int r1, int r2, int g1, int g2, int b1, int b2, int a1 = -1, int a2 = -1)
    {
        if (a1 == -1 || a2 == -1) return (Math.Abs(r1 - r2) + Math.Abs(g1 - g2) + Math.Abs(b1 - b2)) / 7.65;
        else return (Math.Abs(r1 - r2) + Math.Abs(g1 - g2) + Math.Abs(b1 - b2) + Math.Abs(a1 - a2)) / 10.2;
    }

    // from:http://stackoverflow.com/questions/9018016/how-to-compare-two-colors
    public static double Calculate3D(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return Calculate3D(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3]);
    }

    public static double Calculate3D(Color c1, Color c2)
    {
        return Calculate3D(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B);
    }

    public static double Calculate3D(int r1, int r2, int g1, int g2, int b1, int b2)
    {
        return Math.Sqrt(Math.Pow(Math.Abs(r1 - r2), 2) + Math.Pow(Math.Abs(g1 - g2), 2) + Math.Pow(Math.Abs(b1 - b2), 2)) / 4.41672955930063709849498817084;
    }

    // Same as above, but made 4D to include alpha channel
    public static double Calculate4D(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return Calculate4D(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3], c1[0], c2[0]);
    }

    public static double Calculate4D(Color c1, Color c2)
    {
        return Calculate4D(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B, c1.A, c2.A);
    }

    public static double Calculate4D(int r1, int r2, int g1, int g2, int b1, int b2, int a1, int a2)
    {
        return Math.Sqrt(Math.Pow(Math.Abs(r1 - r2), 2) + Math.Pow(Math.Abs(g1 - g2), 2) + Math.Pow(Math.Abs(b1 - b2), 2) + Math.Pow(Math.Abs(a1 - a2), 2)) / 5.1;
    }

    /**
    * Computes the difference between two RGB colors by converting them to the L*a*b scale and
    * comparing them using the CIE76 algorithm { http://en.wikipedia.org/wiki/Color_difference#CIE76}
    */
    public static double CalculateCIE76(int argb1, int argb2)
    {
        return CalculateCIE76(Color.FromArgb(argb1), Color.FromArgb(argb2));
    }

    public static double CalculateCIE76(Color c1, Color c2)
    {
        return CalculateCIE76(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B);
    }

    public static double CalculateCIE76(int r1, int r2, int g1, int g2, int b1, int b2)
    {
        int[] lab1 = ColorConversion.ColorToLab(r1, g1, b1);
        int[] lab2 = ColorConversion.ColorToLab(r2, g2, b2);
        return Math.Sqrt(Math.Pow(lab2[0] - lab1[0], 2) + Math.Pow(lab2[1] - lab1[1], 2) + Math.Pow(lab2[2] - lab1[2], 2)) / 2.55;
    }
}


internal static class ColorConversion
{

    public static int[] ArgbToArray(int argb)
    {
        return new int[] { (argb >> 24), (argb >> 16) & 0xFF, (argb >> 8) & 0xFF, argb & 0xFF };
    }

    public static int[] ColorToLab(int R, int G, int B)
    {
        // http://www.brucelindbloom.com

        double r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
        double Ls, fas, fbs;
        double eps = 216.0f / 24389.0f;
        double k = 24389.0f / 27.0f;

        double Xr = 0.964221f;  // reference white D50
        double Yr = 1.0f;
        double Zr = 0.825211f;

        // RGB to XYZ
        r = R / 255.0f; //R 0..1
        g = G / 255.0f; //G 0..1
        b = B / 255.0f; //B 0..1

        // assuming sRGB (D65)
        if (r <= 0.04045) r = r / 12;
        else r = (float)Math.Pow((r + 0.055) / 1.055, 2.4);

        if (g <= 0.04045) g = g / 12;
        else g = (float)Math.Pow((g + 0.055) / 1.055, 2.4);

        if (b <= 0.04045) b = b / 12;
        else b = (float)Math.Pow((b + 0.055) / 1.055, 2.4);

        X = 0.436052025f * r + 0.385081593f * g + 0.143087414f * b;
        Y = 0.222491598f * r + 0.71688606f * g + 0.060621486f * b;
        Z = 0.013929122f * r + 0.097097002f * g + 0.71418547f * b;

        // XYZ to Lab
        xr = X / Xr;
        yr = Y / Yr;
        zr = Z / Zr;

        if (xr > eps) fx = (float)Math.Pow(xr, 1 / 3.0);
        else fx = (float)((k * xr + 16.0) / 116.0);

        if (yr > eps) fy = (float)Math.Pow(yr, 1 / 3.0);
        else fy = (float)((k * yr + 16.0) / 116.0);

        if (zr > eps) fz = (float)Math.Pow(zr, 1 / 3.0);
        else fz = (float)((k * zr + 16.0) / 116);

        Ls = (116 * fy) - 16;
        fas = 500 * (fx - fy);
        fbs = 200 * (fy - fz);

        int[] lab = new int[3];
        lab[0] = (int)(2.55 * Ls + 0.5);
        lab[1] = (int)(fas + 0.5);
        lab[2] = (int)(fbs + 0.5);
        return lab;
    }
}
于 2015-06-28T17:40:41.387 に答える
3

RGBのみを使用する簡単な方法は

cR=R1-R2 
cG=G1-G2 
cB=B1-B2 
uR=R1+R2 
distance=cR*cR*(2+uR/256) + cG*cG*4 + cB*cB*(2+(255-uR)/256)

私はこれをしばらく使用してきましたが、ほとんどの目的で十分に機能します。

于 2016-08-10T05:58:43.240 に答える
2

最良の方法は deltaE です。DeltaE は、色の違いを示す数値です。deltae < 1 の場合、違いは人間の目では認識できません。rgb を lab に変換してデルタ e を計算するためのコードを canvas と js で作成しました。この例では、コードは、LAB1 として保存した基本色とは異なる色のピクセルを認識しています。それが異なる場合、それらのピクセルを赤にします。デルタ e の許容範囲を増加または減少させることで、色差の感度を増加または減少させることができます。この例では、私が書いた行で deltaE に 10 を割り当てました (deltae <= 10):

<script>   
  var constants = {
    canvasWidth: 700, // In pixels.
    canvasHeight: 600, // In pixels.
    colorMap: new Array() 
          };



  // -----------------------------------------------------------------------------------------------------

  function fillcolormap(imageObj1) {


    function rgbtoxyz(red1,green1,blue1){ // a converter for converting rgb model to xyz model
 var red2 = red1/255;
 var green2 = green1/255;
 var blue2 = blue1/255;
 if(red2>0.04045){
      red2 = (red2+0.055)/1.055;
      red2 = Math.pow(red2,2.4);
 }
 else{
      red2 = red2/12.92;
 }
 if(green2>0.04045){
      green2 = (green2+0.055)/1.055;
      green2 = Math.pow(green2,2.4);    
 }
 else{
      green2 = green2/12.92;
 }
 if(blue2>0.04045){
      blue2 = (blue2+0.055)/1.055;
      blue2 = Math.pow(blue2,2.4);    
 }
 else{
      blue2 = blue2/12.92;
 }
 red2 = (red2*100);
 green2 = (green2*100);
 blue2 = (blue2*100);
 var x = (red2 * 0.4124) + (green2 * 0.3576) + (blue2 * 0.1805);
 var y = (red2 * 0.2126) + (green2 * 0.7152) + (blue2 * 0.0722);
 var z = (red2 * 0.0193) + (green2 * 0.1192) + (blue2 * 0.9505);
 var xyzresult = new Array();
 xyzresult[0] = x;
 xyzresult[1] = y;
 xyzresult[2] = z;
 return(xyzresult);
} //end of rgb_to_xyz function
function xyztolab(xyz){ //a convertor from xyz to lab model
 var x = xyz[0];
 var y = xyz[1];
 var z = xyz[2];
 var x2 = x/95.047;
 var y2 = y/100;
 var z2 = z/108.883;
 if(x2>0.008856){
      x2 = Math.pow(x2,1/3);
 }
 else{
      x2 = (7.787*x2) + (16/116);
 }
 if(y2>0.008856){
      y2 = Math.pow(y2,1/3);
 }
 else{
      y2 = (7.787*y2) + (16/116);
 }
 if(z2>0.008856){
      z2 = Math.pow(z2,1/3);
 }
 else{
      z2 = (7.787*z2) + (16/116);
 }
 var l= 116*y2 - 16;
 var a= 500*(x2-y2);
 var b= 200*(y2-z2);
 var labresult = new Array();
 labresult[0] = l;
 labresult[1] = a;
 labresult[2] = b;
 return(labresult);

}

    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');
    var imageX = 0;
    var imageY = 0;

    context.drawImage(imageObj1, imageX, imageY, 240, 140);
    var imageData = context.getImageData(0, 0, 240, 140);
    var data = imageData.data;
    var n = data.length;
   // iterate over all pixels

    var m = 0;
    for (var i = 0; i < n; i += 4) {
      var red = data[i];
      var green = data[i + 1];
      var blue = data[i + 2];
    var xyzcolor = new Array();
    xyzcolor = rgbtoxyz(red,green,blue);
    var lab = new Array();
    lab = xyztolab(xyzcolor);
    constants.colorMap.push(lab); //fill up the colormap array with lab colors.         
      } 

  }

// ------------------------------------------------ -------------------------------------------------- ---

    function colorize(pixqty) {

         function deltae94(lab1,lab2){    //calculating Delta E 1994

         var c1 = Math.sqrt((lab1[1]*lab1[1])+(lab1[2]*lab1[2]));
         var c2 =  Math.sqrt((lab2[1]*lab2[1])+(lab2[2]*lab2[2]));
         var dc = c1-c2;
         var dl = lab1[0]-lab2[0];
         var da = lab1[1]-lab2[1];
         var db = lab1[2]-lab2[2];
         var dh = Math.sqrt((da*da)+(db*db)-(dc*dc));
         var first = dl;
         var second = dc/(1+(0.045*c1));
         var third = dh/(1+(0.015*c1));
         var deresult = Math.sqrt((first*first)+(second*second)+(third*third));
         return(deresult);
          } // end of deltae94 function
    var lab11 =  new Array("80","-4","21");
    var lab12 = new Array();
    var k2=0;
    var canvas = document.getElementById('myCanvas');
                                        var context = canvas.getContext('2d');
                                        var imageData = context.getImageData(0, 0, 240, 140);
                                        var data = imageData.data;

    for (var i=0; i<pixqty; i++) {

    lab12 = constants.colorMap[i];

    var deltae = deltae94(lab11,lab12);     
                                        if (deltae <= 10) {

                                        data[i*4] = 255;
                                        data[(i*4)+1] = 0;
                                        data[(i*4)+2] = 0;  
                                        k2++;
                                        } // end of if 
                                } //end of for loop
    context.clearRect(0,0,240,140);
    alert(k2);
    context.putImageData(imageData,0,0);
} 
// -----------------------------------------------------------------------------------------------------

$(window).load(function () {    
  var imageObj = new Image();
  imageObj.onload = function() {
  fillcolormap(imageObj);    
  }
  imageObj.src = './mixcolor.png';
});

// ---------------------------------------------------------------------------------------------------
 var pixno2 = 240*140; 
 </script>
于 2013-08-31T07:16:25.433 に答える
1

最後に画像全体を分析したいと思いますよね?したがって、アイデンティティカラーマトリックスとの最小/最大の違いを確認できます。

グラフィックスを処理するためのほとんどの数学演算は、行列を使用します。これは、それらを使用する可能なアルゴリズムが、従来のポイントごとの距離および比較計算よりも高速であることが多いためです。(たとえば、DirectX、OpenGLなどを使用した操作の場合)

だから私はあなたがここから始めるべきだと思います:

http://en.wikipedia.org/wiki/Identity_matrix

http://en.wikipedia.org/wiki/Matrix_difference_equation

...そしてBeskaがすでに上でコメントしたように:

これは最良の「目に見える」違いを与えないかもしれません...

これは、画像を処理している場合、アルゴリズムが「類似」の定義に依存することも意味します。

于 2012-01-26T12:43:43.923 に答える
0

色を比較する唯一の「正しい」方法は、CIELab または CIELuv で deltaE を使用することです。

しかし、多くのアプリケーションでは、これは十分な近似値であると思います:

distance = 3 * |dR| + 4 * |dG| + 3 * |dB|

色を比較する場合、加重マンハッタン距離の方がはるかに理にかなっていると思います。色の原色は私たちの頭の中にあることを忘れないでください。物理的な意味はありません。CIELab と CIELuv は、色の知覚から統計的にモデル化されています。

于 2015-05-25T11:08:52.483 に答える
0

素早く汚いために、あなたはすることができます

import java.awt.Color;
private Color dropPrecision(Color c,int threshold){
    return new Color((c.getRed()/threshold),
                     (c.getGreen()/threshold),
                     (c.getBlue()/threshold));
}
public boolean inThreshold(Color _1,Color _2,int threshold){
    return dropPrecision(_1,threshold)==dropPrecision(_2,threshold);
}

整数除算を使用して色を量子化します。

于 2017-06-08T04:20:33.620 に答える