유사성 / 차이 성을 위해 두 가지 색상을 비교하는 방법


171

가변 색상과 더 유사한 5 가지 사전 정의 된 색상과 비율을 평가할 수있는 프로그램을 설계하려고합니다. 문제는 단계별로 수동으로 수행하는 방법을 모른다는 것입니다. 따라서 프로그램을 생각하기가 훨씬 더 어렵습니다.

세부 사항 : 색상은 젤이있는 튜브 사진에서 다른 색상으로 나타납니다. 각기 다른 색상의 5 개의 튜브가 있으며 각 레벨은 5 개 중 1 개입니다. 다른 샘플의 사진을 찍고 컴퓨터에서 색상을 비교하여 해당 샘플이 어느 수준에 속하는지 평가하고 근사 비율을 알고 싶습니다. http://www.colortools.net/color_matcher.html 과 같은 프로그램을 원합니다 .

내가 생각하고 수동으로해야 할 일이더라도 어떤 조치를 취해야하는지 알려주세요. 매우 도움이 될 것입니다.


1
나는 텍스트를 약간 변경하여 포르투갈어 단어를 올바른 영어와 동등한 것으로 바 꾸었습니다 ... 오류가 발생하면 다시 변경하십시오.
— Beska

13
색상 차이에 대한 wikipedia 기사가 있습니다. en.wikipedia.org/wiki/Color_difference
— Ocaso Protal

4
이것은 흥미 있어야한다 : stevehanov.ca/blog/index.php?id=116 그것은 세 가지 다른 색상 모델의 차이를 계산 탐구한다.
— Vlad

@OcasoProtal 님, 공유해 주셔서 감사합니다. 그리고 OP, 흥미로운 질문에.
— 인식

잠재적 인 사진 변이를 최소화하려고 노력하십시오 ... 자세한 내용은 아래를 참조하십시오.
— Beska

답변:


130

올바른 리드에 대해서는 색상 차이 에 관한 Wikipedia의 기사를 참조하십시오 . 기본적으로 일부 다차원 색 공간에서 거리 메트릭을 계산하려고합니다. 그러나 RGB는 "지각 적으로 균일"하지 않으므로 Vadim에서 제안한 유클리드 RGB 거리 측정법은 사람이 인식 한 색상 간 거리와 일치하지 않습니다. 시작을 위해, L a b *는 지각 적으로 균일 한 색 공간이되도록 의도되고, deltaE 메트릭이 일반적으로 사용된다. 그러나 사람의 인식과 일치하는 더 세련된 색상 공간과 더 세련된 deltaE 공식이 있습니다.

변환을 수행하기 위해 색 공간과 광원에 대해 더 배워야합니다. 그러나 유클리드 RGB 메트릭보다 나은 빠른 공식의 경우 다음과 같이하십시오. RGB 값이 sRGB 색상 공간에 있다고 가정하고 sRGB를 L a b * 변환 공식 을 찾은 다음 sRGB 색상을 L a b * 로 변환하십시오 . 두 L a b * 값 사이의 deltaE를 계산 합니다. 계산 비용이 많이 들지 않고 비선형 수식과 곱셈과 덧셈입니다.


11
"deltaE"의 경우 +1이며, 이는 가장 표준화 된 비교 방법이며 다른 사용 사례에 대한 deltaE 공식의 적용이 있습니다.
— Martin Hennings

9
당신은 여기에 변환 공식을 찾을 수 있습니다 brucelindbloom.com/index.html?Equations.html
— 기예르모 구티에레스

4
또는 Ruby에서 작업하는 경우 다른 색상 작업 중에서 deltaE 를 구현 하는 colorgem 을 확인하십시오 .
— Mike Jarema

다음은 Javascript에서 위의 구현에 대한 요지입니다. gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
— Ryan.C

46

처음으로 내 생각에 온 아이디어 (어리 석다면 미안합니다). 색상의 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)

28
RGB 색상 공간을 사용하는 경우 두 색상의 차이는 사람 이 차이를 인식 하는 방법 과 다릅니다. 그러나 그렇습니다. 기본 아이디어는 모든 곳에서 동일합니다. 우리는 다른 색 공간으로 매핑해야합니다.
— Voo

6
@Voo : HSV / HSL / LAB가 거리 기반 유사성 매칭에서 (s) RGB보다 훨씬 더 나은 색 공간 일 것입니다.
— Jon Purdy

4
이것은 두 가지 색이 얼마나 다른지를 알려주는 좋은 방법이지만, 어떻게 다른 색이 인식되는지는 잘 알지 못합니다. 인간의 눈은 완벽하지 않습니다. 우리는 빨강이나 파랑보다 녹색에 더 민감합니다. 우리의 밝기 인식은 대수적입니다. OP는 결코 원하는 것을 지정하지 않았습니다. 그러나 인간의 시력에 맞게 특별히 설계된 알고리즘 은 여기 를 참조하십시오 .
— BlueRaja-대니 Pflughoeft

+ 나의 첫 아이디어이기도합니다.
— ST3 2016.

9
여기서 또 다른 문제는 255, 0, 0은 0, 255, 0과 0, 0, 255와 같은 거리입니다.

27

실제로 나는 두 달 전에 같은 길을 걸었습니다. 이 질문에 대한 완벽한 대답은 없지만 ( 여기 에 몇 번이나 묻었습니다) 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));
}

이 방법은 저에게 효과적이었습니다. 색상 이름 목록에서 가장 가까운 색상을 찾는 데 도움이되었습니다.
— faisalbhagat

23

색상 값은 하나 이상의 차원을 가지므로 두 색상을 비교하는 본질적인 방법은 없습니다. 유스 케이스에 대해 색상의 의미와이를 가장 잘 비교하는 방법을 결정해야합니다.

빨강 / 녹색 / 파랑 구성 요소와 반대로 색상의 색조, 채도 및 / 또는 명도 속성을 비교하려고합니다. 어떻게 비교하고 싶은지 알아내는 데 어려움이 있다면 몇 가지 샘플 색상 쌍을 가져 와서 정신적으로 비교 한 다음 왜 비슷한 지 / 다른지 자신을 정당화하거나 설명하십시오.

비교하려는 색상의 속성 / 구성 요소를 알고 나면 색상에서 해당 정보를 추출하는 방법을 찾아야합니다.

대부분의 경우 일반적인 RedGreenBlue 표현에서 HueSaturationLightness로 색상을 변환 한 다음 다음과 같은 것을 계산하면됩니다.

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

이 예제는 색상의 그라디언트 / 색조가 얼마나 멀리 떨어져 있는지를 나타내는 간단한 스칼라 값을 제공합니다.

Wikipedia의 HSL 및 HSV를 참조하십시오 .


2
이러한 것들에 대한 강의에서 기억하는 것들에서 이미지를 HSV / HSL이 아닌 Lab 색상 공간으로 변환 할 것입니다. 그 중 하나를 고른 이유가 있습니까?
— Voo

아니. RGB와 HSL이 가장 익숙한 것이므로 "기본"RGB가 유일한 옵션이 아니라는 사실을 강조하기 위해 HSL을 선택했습니다. 실제로 응용 프로그램에 따라 다릅니다. Lab 색상 공간에 대해 알려 주셔서 감사합니다.
— Supr

1
어쨌든 나는 당신에게 +1을주었습니다. 왜냐하면 기본 원리는 "올바른"답입니다 (인식 된 차이를 균일하게 처리하는 색상 공간으로 변환 한 다음 비교를 수행하십시오). 어떤 공간이 가장 좋은지 잘 모르겠습니다.이 모든 다른 색상 공간은 hell imo와 혼동됩니다.)
— Voo

21

두 개의 Color객체 가있는 경우 c1및 c2각 RGB 값을의 RGB 값 c1과 비교할 수 있습니다 c2.

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)으로 나눌 수 있으며 두 값의 차이를 얻게됩니다.

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

그 후에는 평균 색상 차이를 백분율로 찾을 수 있습니다.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

c1와 사이의 비율 차이를 줄 수 있습니다 c2.


2 가지 사소한 것들 : <b> 1 </ b> pctDiffRed = diffRed / 255;은 어딘가에 뜨지 않는 한 0을 줄 것입니다. <b> 2 </ b> 백분율을 얻으려면 어딘가에 100을 곱해야합니다.
— vaughandroid

18
사람의 눈은 색 변화를 다르게 인식하기 때문에 이것은 "가시적 인"최상의 차이를 나타내지 않을 수 있습니다. 즉, 나는 이것이 그녀가 찾고있는 것이라고 추측하고 있습니다. 왜냐하면 그녀는 아마도 인식 된 차이가 아니라 동등하게 계량 가능한 차이를 찾고 있기 때문입니다. 여기에 관련이있을 경우 고려해야 할 것으로 생각했습니다.
— Beska

14

인간의 인식으로 두 가지 색상을 비교하는 가장 좋은 방법 중 하나는 CIE76입니다. 차이점을 델타 -E라고합니다. 1보다 작 으면 육안으로 차이를 인식 할 수 없습니다.

CIE76 비교 방법을 포함하는 멋진 색상 유틸리티 클래스 ColorUtils (아래 코드)가 있습니다. 그것은 취리히 대학 Daniel Strebel에 의해 작성되었습니다.

ColorUtils.class에서 나는 방법을 사용합니다 :

static double colorDifference(int r1, int g1, int b1, int r2, int g2, int b2)

r1, g1, b1-첫 번째 색상의 RGB 값

r2, g2, b2-비교하려는 두 번째 색상의 RGB 값

Android로 작업하면 다음과 같은 값을 얻을 수 있습니다.

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


ColorUtils.class by Daniel Strebel, 취리히 대학교 :

import android.graphics.Color;

public class ColorUtil {
public static int argb(int R, int G, int B) {
    return argb(Byte.MAX_VALUE, R, G, B);
}

public static int argb(int A, int R, int G, int B) {
    byte[] colorByteArr = {(byte) A, (byte) R, (byte) G, (byte) B};
    return byteArrToInt(colorByteArr);
}

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

public static int byteArrToInt(byte[] colorByteArr) {
    return (colorByteArr[0] << 24) + ((colorByteArr[1] & 0xFF) << 16)
            + ((colorByteArr[2] & 0xFF) << 8) + (colorByteArr[3] & 0xFF);
}

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

    float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
    float Ls, as, bs;
    float eps = 216.f / 24389.f;
    float k = 24389.f / 27.f;

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

    // RGB to XYZ
    r = R / 255.f; //R 0..1
    g = G / 255.f; //G 0..1
    b = B / 255.f; //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.);
    else
        fx = (float) ((k * xr + 16.) / 116.);

    if (yr > eps)
        fy = (float) Math.pow(yr, 1 / 3.);
    else
        fy = (float) ((k * yr + 16.) / 116.);

    if (zr > eps)
        fz = (float) Math.pow(zr, 1 / 3.);
    else
        fz = (float) ((k * zr + 16.) / 116);

    Ls = (116 * fy) - 16;
    as = 500 * (fx - fy);
    bs = 200 * (fy - fz);

    int[] lab = new int[3];
    lab[0] = (int) (2.55 * Ls + .5);
    lab[1] = (int) (as + .5);
    lab[2] = (int) (bs + .5);
    return lab;
}

/**
 * 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 getColorDifference(int a, int b) {
    int r1, g1, b1, r2, g2, b2;
    r1 = Color.red(a);
    g1 = Color.green(a);
    b1 = Color.blue(a);
    r2 = Color.red(b);
    g2 = Color.green(b);
    b2 = Color.blue(b);
    int[] lab1 = rgb2lab(r1, g1, b1);
    int[] lab2 = rgb2lab(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));
}
}

위의 코드는 rgb2lab에 오류가 있습니다. r, g 및 b 변환에서 12로 나누기는 12.92로 나누어야합니다. 그렇지 않으면 기능은 r = 0.04045에서 연속적이지 않습니다
— John Smith

10

다른 답변이지만 Supr의 것과 비슷하지만 다른 색상 공간입니다.

문제는 인간이 색의 차이를 균일하게 인식하지 못하고 RGB 색 공간이이를 무시한다는 것입니다. 결과적으로 RGB 색상 공간을 사용하고 두 색상 사이의 유클리드 거리를 계산하면 수학적으로는 정확한 차이가 있지만 인간이 말한 것과 일치하지 않는 차이가 발생할 수 있습니다.

이것은 문제가되지 않을 수도 있습니다. 차이점은 그렇게 큰 것이 아니라고 생각하지만,이 "더 나은"문제를 해결하려면 RGB 색상을 위의 문제를 피하기 위해 특별히 설계된 색상 공간으로 변환해야합니다. 이전 모델의 개선 사항이 몇 가지 있습니다 (이는 인간의 인식에 기초하기 때문에 실험 데이터를 기반으로 "올바른"값을 측정해야 함). 있다 실험실 색 공간 내가 조금로 변환 복잡하지만 가장 좋은 것 같아요. CIE XYZ 가 더 간단합니다 .

여기에 다른 색 공간 사이를 변환하는 공식을 나열하여 약간의 실험을 할 수 있는 사이트가 있습니다.


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

2

가장 좋은 방법은 델타 E입니다. DeltaE는 색상의 차이를 나타내는 숫자입니다. deltae <1 인 경우 차이는 사람의 눈으로 인식 할 수 없습니다. rgb를 lab으로 변환 한 다음 델타 e를 계산하기 위해 캔버스와 js에 코드를 작성했습니다. 이 예제에서 코드는 LAB1으로 저장 한 기본 색상과 다른 색상의 픽셀을 인식합니다. 다른 경우 픽셀이 빨간색이됩니다. 증분으로 색차의 감도를 높이거나 낮추고 허용 가능한 델타 e 범위를 줄일 수 있습니다. 이 예에서는 필자가 작성한 줄에 deltaE에 10을 할당했습니다 (델타 <= 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>

1
정수 부분이 약간 걱정됩니다. 1/3그리고 16/116둘 다로 평가합니다 0. 거의 확실하지 않습니다. 아마도 알고리즘은 정확하지만 코드는 확실하지 않습니다.
— 다우드 이븐 카림

CIE-LAB dE94를 설명하고 있습니다. 델타 E는 유클리드의 변화를 의미합니다. 즉, 표준 실험실 색상 공간에서 매우 표준적인 유클리드 거리 공식으로 주어진 유클리드 거리입니다. 델타 E의 수정, 즉 76, 94, 2000 (직물 등에 사용되는 델타 E, CMC도 있음)은 랩 색 공간 내의 위치들 사이의 다른 거리 공식이다. 랩 코드는 각각 동일하지만 색상 차이 코드는 다릅니다. . 간단히 말해, 델타 E는 그렇게 불리는 것이 아닙니다.
— Tatarize

2

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)

나는 이것을 잠시 동안 사용해 왔으며 대부분의 목적을 위해 충분히 잘 작동합니다.


상기 화학식 사용 거리 값의 범위는 무엇
— 아만 가르 왈

이것은 유클리드 색상 차이 근사치에 매우 가깝습니다. 계산 속도를 높이기 위해 루트 구성 요소를 건너 뛰고 있다고 추측하므로 0에서 100 ^ 3 사이의 범위입니다. 100으로 정규화하려면 동력의 거리를 두십시오1/3
— Daniel

2

내 안드로이드에서 이것을 사용했으며 RGB 공간이 권장되지 않지만 만족스러워 보입니다.

    public double colourDistance(int red1,int green1, int blue1, int red2, int green2, int blue2)
{
      double rmean = ( red1 + red2 )/2;
    int r = red1 - red2;
    int g = green1 - green2;
    int b = blue1 - blue2;
    double weightR = 2 + rmean/256;
    double weightG = 4.0;
    double weightB = 2 + (255-rmean)/256;
    return Math.sqrt(weightR*r*r + weightG*g*g + weightB*b*b);
}

그런 다음 다음을 사용하여 유사성의 백분율을 얻습니다.

double maxColDist = 764.8339663572415;
double d1 = colourDistance(red1,green1,blue1,red2,green2,blue2);
String s1 = (int) Math.round(((maxColDist-d1)/maxColDist)*100) + "% match";

충분히 잘 작동합니다.


2

LAB 색 공간, HSV 비교와 같은 다양한 방법을 시도했지만 광도가이 목적에 매우 효과적이라는 것을 알았습니다.

여기 파이썬 버전이 있습니다

def lum(c):
    def factor(component):
        component = component / 255;
        if (component <= 0.03928):
            component = component / 12.92;
        else:
            component = math.pow(((component + 0.055) / 1.055), 2.4);

        return component
    components = [factor(ci) for ci in c]

    return (components[0] * 0.2126 + components[1] * 0.7152 + components[2] * 0.0722) + 0.05;

def color_distance(c1, c2):

    l1 = lum(c1)
    l2 = lum(c2)
    higher = max(l1, l2)
    lower = min(l1, l2)

    return (higher - lower) / higher


c1 = ImageColor.getrgb('white')
c2 = ImageColor.getrgb('yellow')
print(color_distance(c1, c2))

당신에게 줄 것이다

0.0687619047619048

기원은 ImageColor무엇입니까? 내가 찾은 편집from PIL import ImageColor
— ademar111190

광도가 색상의 밝기가 아닙니까? 따라서이 경우 녹색, 파란색 및 빨간색은 밝기가 동일하다면 다른 것으로보고되지 않습니까?
— Peter B.

1

마지막에 전체 이미지를 분석하고 싶습니까? 따라서 식별 색상 매트릭스와의 최소 / 최고 차이를 확인할 수 있습니다.

그래픽 처리를위한 대부분의 수학 연산은 행렬을 사용합니다. 그래픽을 사용하는 가능한 알고리즘은 종종 포인트 단위 거리 및 비교 계산보다 고전적입니다. (예 : DirectX, OpenGL 등을 사용하는 작업의 경우)

그래서 여기서 시작해야한다고 생각합니다.

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

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

... Beska가 이미 위에서 언급했듯이 :

이것은 가장 "가시적 인"차이를 제공하지 않을 수 있습니다 ...

이는 이미지를 처리하는 경우 알고리즘이 "유사"정의에 의존한다는 것을 의미합니다.


1

얼마나 많은 비율을 일치시키고 싶은 Kotlin 버전.

퍼센트 선택적 인수를 사용한 메소드 호출

isMatchingColor(intColor1, intColor2, 95) // should match color if 95% similar

분석법 본문

private fun isMatchingColor(intColor1: Int, intColor2: Int, percent: Int = 90): Boolean {
    val threadSold = 255 - (255 / 100f * percent)

    val diffAlpha = abs(Color.alpha(intColor1) - Color.alpha(intColor2))
    val diffRed = abs(Color.red(intColor1) - Color.red(intColor2))
    val diffGreen = abs(Color.green(intColor1) - Color.green(intColor2))
    val diffBlue = abs(Color.blue(intColor1) - Color.blue(intColor2))

    if (diffAlpha > threadSold) {
        return false
    }

    if (diffRed > threadSold) {
        return false
    }

    if (diffGreen > threadSold) {
        return false
    }

    if (diffBlue > threadSold) {
        return false
    }

    return true
}

0

사람이 보는 방식으로 색상을 비교하려면 RGB 색상을 Lab 색상 공간으로 변환해야합니다. 그렇지 않으면 매우 이상한 방식으로 '일치하는'RGB 색상이 표시됩니다.

색상 차이 에 대한 위키 백과 링크 는 수년에 걸쳐 정의 된 다양한 Lab 색상 공간 차이 알고리즘에 대한 소개를 제공합니다. 두 가지 실험실 색상의 유클리드 거리를 확인하는 가장 간단한 방법은 작동하지만 몇 가지 결함이 있습니다.

OpenIMAJ 프로젝트 에는보다 정교한 CIEDE2000 알고리즘의 Java 구현이 편리 합니다. 두 세트의 Lab 색상을 제공하면 단일 거리 값을 다시 얻을 수 있습니다.


0

색상을 비교하는 유일한 "올바른"방법은 CIELab 또는 CIELuv에서 deltaE를 사용하는 것입니다.

그러나 많은 응용 프로그램의 경우 이것이 충분하다고 생각합니다.

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

가중 맨해튼 거리는 색상을 비교할 때 훨씬 더 의미가 있다고 생각합니다. 색상 기본은 우리 머리에만 있다는 것을 기억하십시오. 그들은 물리적으로 중요하지 않습니다. CIELab과 CIELuv는 색상에 대한 우리의 인식으로 통계적으로 모델링되었습니다.


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

정수 나누기를 사용하여 색상을 양자화합니다.


0

스위프트 5 답변

이 질문의 Swift 버전이 필요했기 때문에이 스레드를 찾았습니다. 아무도 해결책에 대해 대답하지 않았으므로 여기에 내 것이 있습니다.

extension UIColor {

    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        getRed(&red, green: &green, blue: &blue, alpha: &alpha)

        return (red, green, blue, alpha)
    }

    func isSimilar(to colorB: UIColor) -> Bool {
        let rgbA = self.rgba
        let rgbB = colorB.rgba

        let diffRed = abs(CGFloat(rgbA.red) - CGFloat(rgbB.red))
        let diffGreen = abs(rgbA.green - rgbB.green)
        let diffBlue = abs(rgbA.blue - rgbB.blue)

        let pctRed = diffRed
        let pctGreen = diffGreen
        let pctBlue = diffBlue

        let pct = (pctRed + pctGreen + pctBlue) / 3 * 100

        return pct < 10 ? true : false
    }
}

용법:

let black: UIColor = UIColor.black
let white: UIColor = UIColor.white

let similar: Bool = black.isSimilar(to: white)

비슷한 색상을 반환하기 위해 10 % 미만의 차이를 설정했지만 직접 사용자 지정할 수 있습니다.


0

안드로이드 ColorUtils API RGBToHSL을 위해 : 나는이 개 있었다 INT ARGB 색상 (색 1, COLOR2를) 나는 두 색상 사이의 거리 / 차이를 얻고 싶었다. 여기 내가 한 일이 있습니다.

private float getHue(int color) {
    int R = (color >> 16) & 0xff;
    int G = (color >>  8) & 0xff;
    int B = (color      ) & 0xff;
    float[] colorHue = new float[3];
    ColorUtils.RGBToHSL(R, G, B, colorHue);
    return colorHue[0];
}

그런 다음 아래 코드를 사용하여 두 색상 사이의 거리를 찾았습니다.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.