วิธีการเปรียบเทียบสองสีเพื่อความเหมือน / ความแตกต่าง


171

ฉันต้องการออกแบบโปรแกรมที่สามารถช่วยฉันประเมินระหว่าง 5 สีที่กำหนดไว้ล่วงหน้าซึ่งสีใดสีหนึ่งคล้ายกับสีของตัวแปรและมีเปอร์เซ็นต์เท่าใด สิ่งหนึ่งคือฉันไม่รู้วิธีการทำอย่างนั้นทีละขั้นตอน ดังนั้นจึงเป็นการยากที่จะคิดโปรแกรม

รายละเอียดเพิ่มเติม: สีมาจากภาพถ่ายของหลอดที่มีเจลซึ่งเป็นสีที่ต่างกัน ฉันมี 5 หลอดที่มีสีแตกต่างกันซึ่งแต่ละตัวเป็นตัวแทนของ 1 ใน 5 ระดับ ฉันต้องการถ่ายภาพตัวอย่างอื่น ๆ และในคอมพิวเตอร์ประเมินว่าระดับใดที่กลุ่มตัวอย่างนั้นเป็นสมาชิกโดยการเปรียบเทียบสีและฉันต้องการรู้ว่าด้วยเปอร์เซ็นต์ของการประมาณเช่นกัน ฉันต้องการโปรแกรมที่ทำสิ่งนี้: http://www.colortools.net/color_matcher.html

หากคุณสามารถบอกฉันได้ว่าต้องทำอะไรถึงแม้ว่าจะเป็นสิ่งที่ฉันต้องคิดและทำด้วยตนเอง มันจะมีประโยชน์มาก


1
ฉันเปลี่ยนข้อความเล็กน้อยเปลี่ยนคำภาษาโปรตุเกสเป็นสิ่งที่ฉันคิดว่าเทียบเท่าภาษาอังกฤษที่ถูกต้อง ... เปลี่ยนกลับถ้าฉันผิด
Beska

13
มีบทความวิกิพีเดียเกี่ยวกับความแตกต่างของสี: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
สิ่งนี้น่าสนใจ: stevehanov.ca/blog/index.php?id=116มันสำรวจการคำนวณความแตกต่างในแบบจำลองสีสามแบบ
Vlad

เฮ้ @OcasoProtal นั่นเป็นลิงค์ที่ยอดเยี่ยมสำหรับการแบ่งปัน และสำหรับ OP คำถามที่น่าสนใจ
การรับรู้

พยายามลดความแปรปรวนของการถ่ายภาพให้น้อยที่สุดเช่นกัน ... รายละเอียดเพิ่มเติมในคำตอบด้านล่าง
Beska

คำตอบ:


130

ดูบทความของ Wikipedia เกี่ยวกับความแตกต่างของสีสำหรับโอกาสในการขายที่เหมาะสม โดยทั่วไปคุณต้องการคำนวณตัวชี้วัดระยะทางในสเปซหลายมิติหลายมิติ แต่ RGB ไม่ใช่ "รูปแบบการรับรู้" ดังนั้นตัวชี้วัดระยะทางแบบปริภูมิแบบยุคลิดของคุณที่ Vadim แนะนำจะไม่ตรงกับระยะห่างระหว่างมนุษย์ที่รับรู้สี สำหรับการเริ่มต้น L a b * มีวัตถุประสงค์เพื่อให้เป็น colorpace ที่มีรูปแบบเหมือนกันทุกประการและโดยทั่วไปจะใช้เมตริก deltaE แต่มีขอบเขตสีที่ละเอียดยิ่งขึ้นและสูตร deltaE ที่ละเอียดยิ่งขึ้นซึ่งเข้าใกล้ความเข้าใจของมนุษย์มากขึ้น

คุณจะต้องเรียนรู้เพิ่มเติมเกี่ยวกับคัลเลอร์สเปซและไฟเพื่อทำการแปลง แต่สำหรับสูตรรวดเร็วที่ดีกว่ายุคลิด RGB ตัวชี้วัดเพียงแค่ทำสิ่งนี้: สมมติว่าค่า RGB ของคุณอยู่ใน colorspace sRGB หา sRGB ไป L b * สูตรการแปลงแปลงสี sRGB ของคุณเพื่อ L b *, และคำนวณ deltaE ระหว่างค่า L a b * สองค่าของคุณ มันไม่ได้คำนวณราคาแพงมันเป็นเพียงสูตรที่ไม่เชิงเส้นและบางตัวคูณและเพิ่ม


11
+1 สำหรับ "deltaE" ซึ่งเป็นวิธีการเปรียบเทียบที่ได้มาตรฐานที่สุดและมีการดัดแปลงสูตร deltaE สำหรับกรณีการใช้งานที่แตกต่างกัน
Martin Hennings

9
คุณสามารถค้นหาสูตรการแปลงได้ที่นี่: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
หรือหากคุณกำลังทำงานใน Ruby ให้ตรวจสอบcolorอัญมณีที่ใช้การแยกประเภทระหว่างการทำงานสีอื่น ๆ
Mike Jarema

นี่คือส่วนสำคัญสำหรับการดำเนินการด้านบนใน Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C

46

แค่ความคิดที่มาถึงใจของฉันเป็นครั้งแรก (ขออภัยถ้าโง่) ส่วนประกอบของสีสามารถสันนิษฐานได้ว่าเป็นพิกัดสามมิติของจุดแล้วคุณสามารถคำนวณระยะห่างระหว่างจุด

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 ความแตกต่างระหว่าง 2 สีนั้นไม่เหมือนกับที่มนุษย์รับรู้ถึงความแตกต่าง แต่ใช่ความคิดพื้นฐานเหมือนกันทุกที่ - เราแค่ต้องทำแผนที่ให้เป็นพื้นที่สีอื่น (แล็บฉันคิด)
Voo

6
@Voo: ฉันเห็นด้วย HSV / HSL / LAB จะมีช่องว่างสีที่ดีกว่า RGB สำหรับการจับคู่ความเหมือนกันตามระยะทาง
จอน Purdy

4
นี่เป็นวิธีที่ดีในการบอกคุณว่ามีสองสีแตกต่างกันอย่างไร แต่งานที่แย่ในการบอกคุณว่าพวกเขาจะได้รับการรับรู้แตกต่างกันอย่างไร ดวงตามนุษย์ยังห่างไกลจากความสมบูรณ์แบบ: เราไวต่อสีเขียวมากกว่าสีแดงหรือสีน้ำเงินการรับรู้ความสว่างของเรานั้นเป็นแบบลอการิทึม ฯลฯ OP ไม่เคยระบุว่าเขา / เธอต้องการอะไร; แต่ดูที่นี่สำหรับอัลกอริทึมที่ออกแบบมาเป็นพิเศษสำหรับสายตามนุษย์
BlueRaja - Danny Pflughoeft

มันเป็นความคิดแรกของฉันเช่นกัน
ST3

9
ปัญหาอื่นที่นี่คือ 255, 0, 0 คือระยะห่างเดียวกันจาก 0, 255, 0 เพราะมันคือ 0, 0, 255.

27

จริงๆแล้วฉันก็เดินไปบนเส้นทางเดียวกันเมื่อสองสามเดือนก่อน ไม่มีคำตอบที่สมบูรณ์แบบสำหรับคำถาม (ที่ถูกถามที่นี่สองสามครั้ง) แต่มีอีกหนึ่งความซับซ้อนมากขึ้นแล้วคำตอบ sqrt (rr) และง่ายต่อการสอดใส่โดยตรงกับ RGB โดยไม่ต้องย้ายไปยังพื้นที่สีอื่นทุกชนิด ฉันพบสูตรนี้ที่นี่ซึ่งเป็นการประมาณค่าใช้จ่ายต่ำของสูตรจริงที่ค่อนข้างซับซ้อน (โดย CIE ซึ่งเป็น W3C ของสีเนื่องจากนี่เป็นเควสต์ที่ไม่เสร็จคุณจึงสามารถหาสมการแตกต่างของสีที่เก่ากว่าและง่ายกว่า) โชคดี

แก้ไข: สำหรับลูกหลานนี่คือรหัส 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)

ตัวอย่างนี้จะให้ค่าสเกลาร์ง่าย ๆ ให้คุณเห็นว่าการไล่ระดับสี / สีของแต่ละสีนั้นมาจากกัน

ดูHSL และ HSV ที่วิกิพีเดีย


2
จากสิ่งที่ฉันจำได้จากการบรรยายเกี่ยวกับสิ่งเหล่านี้ฉันจะแปลงภาพให้เป็นพื้นที่สี Lab ไม่ใช่ HSV / HSL มีเหตุผลใดที่เลือกอันนั้น?
Voo

Nope RGB และ HSL เป็นสิ่งที่ฉันคุ้นเคยมากที่สุดดังนั้นฉันจึงเลือก HSL เพื่อเน้นแนวคิดที่ว่า "ค่าเริ่มต้น" RGB ไม่ใช่ตัวเลือกเดียว - จริง ๆ แล้วมันขึ้นอยู่กับแอปพลิเคชัน ขอขอบคุณที่แจ้งให้เราทราบเกี่ยวกับพื้นที่สีของ Lab
Supr

1
ฉันให้ +1 กับคุณเพราะหลักการพื้นฐานนี่คือคำตอบ "ถูก" (แปลงเป็นพื้นที่สีที่จัดการความแตกต่างที่รับรู้อย่างสม่ำเสมอแล้วทำการเปรียบเทียบ) ฉันไม่แน่ใจว่าช่องว่างใดจะดีที่สุด - ช่องว่างสีที่แตกต่างเหล่านี้ทั้งหมดสร้างความสับสนราวกับเป็นนรก imo;)
Voo

21

หากคุณมีสองColorวัตถุc1และc2คุณก็สามารถเปรียบเทียบค่า RGB จากแต่ละกับที่ของc1c2

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

ค่าเหล่านั้นคุณสามารถหารด้วยจำนวน saturations ที่แตกต่างกัน (255) และคุณจะได้รับความแตกต่างระหว่างสอง

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

หลังจากนั้นคุณจะพบความแตกต่างของสีโดยเฉลี่ยเป็นเปอร์เซ็นต์

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

ซึ่งจะทำให้คุณมีความแตกต่างในอัตราร้อยละระหว่างและc1c2


อีกสองสิ่งเล็ก ๆ น้อย ๆ : <b> 1 </b> pctDiffRed = diffRed / 255;จะให้ 0 คุณเว้นแต่ว่าคุณจะส่งทุ่นไปที่ใดที่หนึ่ง <b> 2 </b> คุณจะต้องคูณด้วย 100 ที่ไหนสักแห่งเพื่อให้ได้เปอร์เซ็นต์
vaughandroid

18
สิ่งนี้อาจไม่ให้ความแตกต่าง "ที่มองเห็นได้" ที่ดีที่สุดเนื่องจากสายตาของมนุษย์รับรู้การเปลี่ยนแปลงสีต่างกัน ที่ถูกกล่าวว่าฉันเดาว่านี่คือสิ่งที่เธอกำลังมองหาเพราะเธออาจมองหาความแตกต่างเชิงปริมาณอย่างเท่าเทียมกันมากกว่าความแตกต่างที่รับรู้ แค่คิดว่าฉันออกจากที่นี่เป็นสิ่งที่ต้องพิจารณาในกรณีที่เกี่ยวข้อง
Beska

14

หนึ่งในวิธีที่ดีที่สุดในการเปรียบเทียบสองสีโดยการรับรู้ของมนุษย์คือ CIE76 ความแตกต่างนี้เรียกว่า Delta-E เมื่อมันน้อยกว่า 1 ดวงตามนุษย์จะไม่สามารถรับรู้ถึงความแตกต่าง

มี ColorUtils คลาสยูทิลิตี้สีที่ยอดเยี่ยม (รหัสด้านล่าง) ซึ่งรวมถึงวิธีการเปรียบเทียบ CIE76 มันเขียนโดย 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 โดย 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: การหารด้วย 12 ควรถูกแทนที่ด้วยการหารด้วย 12.92 ในการแปลง r, g และ b มิฉะนั้นฟังก์ชันจะไม่ต่อเนื่องที่ r = 0.04045
John Smith เมื่อ

10

อีกคำตอบหนึ่งแม้ว่ามันจะคล้ายกับของ Supr - เพียงแค่พื้นที่สีที่ต่าง

สิ่งคือมนุษย์รับรู้ถึงความแตกต่างของสีที่ไม่สม่ำเสมอและพื้นที่สี RGB ไม่สนใจสิ่งนี้ ดังนั้นหากคุณใช้พื้นที่สี RGB และคำนวณระยะทางแบบยุคลิดระหว่าง 2 สีคุณอาจได้รับความแตกต่างที่ถูกต้องทางคณิตศาสตร์อย่างแน่นอน แต่จะไม่ตรงกับสิ่งที่มนุษย์จะบอกคุณ

นี่อาจไม่ใช่ปัญหา - ความแตกต่างไม่ใช่ว่าใหญ่ฉันคิด แต่ถ้าคุณต้องการแก้ปัญหานี้ "ดีกว่า" คุณควรแปลงสี RGB ของคุณเป็นพื้นที่สีที่ถูกออกแบบมาโดยเฉพาะเพื่อหลีกเลี่ยงปัญหาข้างต้น มีการปรับปรุงหลายอย่างจากแบบจำลองก่อนหน้านี้ (เนื่องจากสิ่งนี้ขึ้นอยู่กับการรับรู้ของมนุษย์เราจำเป็นต้องวัดค่า "ถูกต้อง" ตามข้อมูลการทดลอง) มีColorpace Labที่ฉันคิดว่าดีที่สุดแม้ว่าจะมีความซับซ้อนเล็กน้อยในการแปลงเป็น ที่เรียบง่ายจะเป็น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

วิธีที่ดีที่สุดคือ deltaE DeltaE เป็นตัวเลขที่แสดงความแตกต่างของสี ถ้า deltae <1 ดังนั้นความแตกต่างก็ไม่สามารถจำแนกได้ด้วยสายตามนุษย์ ฉันเขียนโค้ดใน Canvas และ js เพื่อแปลง rgb เป็น lab แล้วคำนวณเดลต้า e ในตัวอย่างนี้รหัสกำลังจดจำพิกเซลที่มีสีแตกต่างกันด้วยสีพื้นฐานที่ฉันบันทึกเป็น LAB1 และถ้ามันแตกต่างกันทำให้พิกเซลเหล่านั้นเป็นสีแดง คุณสามารถเพิ่มหรือลดความไวของความแตกต่างของสีด้วยการเพิ่มหรือลดช่วงของเดลต้าอีที่ยอมรับได้ ในตัวอย่างนี้ฉันกำหนด 10 สำหรับ deltaE ในบรรทัดที่ฉันเขียน (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>

1
ฉันกังวลเล็กน้อยจากแผนกจำนวนเต็มของคุณ 1/3และ16/116ประเมินทั้งสอง0อย่างซึ่งแทบจะไม่ใช่สิ่งที่คุณต้องการ อาจอัลกอริทึมของคุณถูกต้อง แต่รหัสของคุณไม่แน่นอน
Dawood ibn Kareem

คุณกำลังอธิบาย CIE-LAB dE94 Delta E หมายถึงการเปลี่ยนแปลงในยุคลิด ซึ่งจะกล่าวในพื้นที่สี Lab แบบมาตรฐานระยะทางแบบยุคลิดที่กำหนดโดยสูตรระยะทางแบบยุคลิดแบบมาตรฐานของคุณ ในขณะที่การปรับเปลี่ยนของ Delta E คือ 76, 94, 2000 (นอกจากนี้ยังมี Delta E, CMC ซึ่งใช้สำหรับสิ่งทอและสิ่งที่คล้ายกัน) เป็นสูตรระยะทางที่แตกต่างกันระหว่างตำแหน่งภายใน Labourspace รหัสสำหรับห้องปฏิบัติการนั้นเหมือนกันในแต่ละรหัสสำหรับความแตกต่างของสีไม่ใช่ . กล่าวโดยย่อ Delta 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)

ฉันใช้อันนี้มาระยะหนึ่งแล้วและใช้งานได้ดีพอสำหรับวัตถุประสงค์ส่วนใหญ่


การใช้สูตรด้านบนช่วงของค่าสำหรับระยะทางคืออะไร
Aman Aggarwal

มันค่อนข้างใกล้เคียงกับการประมาณความแตกต่างของสีแบบยุคลิด ฉันเดาว่ามันกำลังข้ามส่วนประกอบรูทเพื่อเพิ่มความเร็วในการคำนวณดังนั้นมันจึงมีค่าตั้งแต่ 0 ถึง 100 ^ 3 หากคุณต้องการทำให้ปกติเป็น 100 ให้ทำระยะทางกับพลังของ1/3
แดเนียล

2

ฉันใช้สิ่งนี้ในระบบ Android ของฉันและดูเหมือนว่าจะเป็นที่น่าพอใจแม้ว่าจะไม่แนะนำให้ใช้พื้นที่ 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 และฉันพบว่าความส่องสว่างนั้นใช้งานได้ดีสำหรับจุดประสงค์นี้

นี่คือเวอร์ชั่น Python

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

ความสว่างของสีไม่ใช่ความสว่างหรือไม่? ดังนั้นในกรณีนี้สีเขียวสีน้ำเงินและสีแดงจะไม่ถูกรายงานว่าแตกต่างกันตราบใดที่ความสว่างเหมือนกัน?
ปีเตอร์บี.

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 ที่กำหนดไว้ในช่วงหลายปีที่ผ่านมา ง่ายที่สุดที่เพียงแค่ตรวจสอบระยะ Euclidian ของสีแล็บสองสีใช้งานได้ แต่มีข้อบกพร่องเล็กน้อย

สะดวกยิ่งขึ้นกับการใช้งานจาวาของอัลกอริทึม CIEDE2000 ที่ซับซ้อนยิ่งขึ้นในโครงการOpenIMAJ ระบุสีแล็บสองชุดของคุณแล้วมันจะคืนค่าระยะทางเดียวให้กับคุณ


0

วิธีการเปรียบเทียบสีที่ "ถูกต้อง" เท่านั้นคือใช้กับ deltaE ใน CIELab หรือ CIELuv

แต่สำหรับแอปพลิเคชั่นมากมายฉันคิดว่านี่เป็นการประมาณที่ดีพอ:

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

Android สำหรับ ColorUtils API RGBToHSL: ฉันมีสองสีargb int (color1, 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.