สูตรในการกำหนดความสว่างของสี RGB


387

ฉันกำลังมองหาสูตรหรืออัลกอริทึมบางอย่างเพื่อกำหนดความสว่างของสีที่ให้ค่า RGB ฉันรู้ว่ามันไม่ง่ายเหมือนการเพิ่มค่า RGB เข้าด้วยกันและการมีผลรวมที่สูงกว่านั้นจะสว่างกว่า แต่ฉันก็รู้สึกว่าจะเริ่มต้นตรงไหน


8
การรับรู้ความสว่างเป็นสิ่งที่ฉันคิดว่าฉันกำลังมองหาขอบคุณ
robmerica

2
มีบทความที่ดี ( การจัดการสีใน. NET - ตอนที่ 1 ) เกี่ยวกับการเว้นวรรคสีและการสนทนาระหว่างพวกเขารวมถึงทฤษฎีและรหัส (C #) สำหรับคำตอบให้ดูที่หัวข้อการแปลงระหว่างรุ่นในบทความ
ขีดล่าง

4
ฉันเป็นสมาชิกมาหลายปีแล้วและไม่เคยทำแบบนี้มาก่อน ฉันขอแนะนำให้คุณทบทวนคำตอบแล้วคิดว่าควรยอมรับคำตอบแบบใดดี
Jive Dadson

คำตอบ:


456

คุณหมายถึงความสว่างหรือไม่ การรับรู้ความสว่าง? สว่าง?

  • ความส่องสว่าง (มาตรฐานสำหรับพื้นที่สี): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • ความส่องสว่าง (ตัวเลือกการรับรู้ 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • ความส่องสว่าง (ตัวเลือกการรับรู้ 2, การคำนวณช้าลง): sqrt( 0.241*R^2 + 0.691*G^2 + 0.068*B^2 )sqrt( 0.299*R^2 + 0.587*G^2 + 0.114*B^2 )(ขอบคุณ@MatthewHerbst ) [3]

26
โปรดทราบว่าทั้งสองสิ่งนี้เน้นด้านสรีรวิทยา: ลูกตามนุษย์มีความไวต่อแสงสีเขียวมากที่สุด, น้อยไปจนถึงแดงและน้อยที่สุดถึงสีน้ำเงิน
Bob Cross

16
โปรดทราบว่าสิ่งเหล่านี้อาจเป็นเชิงเส้น 0-1 RGB และคุณอาจแก้ไขแกมม่า 0-255 RGB ได้ พวกเขาไม่ได้กลับใจใหม่อย่างที่คุณคิด
alex แปลก

4
ไม่ถูกต้อง. ก่อนที่จะใช้การแปลงเชิงเส้นเราต้องใช้ฟังก์ชันผกผันของแกมม่ากับพื้นที่สีก่อน จากนั้นหลังจากใช้ฟังก์ชันเชิงเส้นฟังก์ชันแกมม่าจะถูกนำไปใช้
Jive Dadson

6
ในสูตรสุดท้ายเป็น (0.299 * R) ^ 2 หรือเป็น 0.299 * (R ^ 2) หรือไม่
Kaizer Sozay

3
@KaizerSozay ในฐานะที่มันเขียนนี่มันจะหมายถึง0.299*(R^2)(เพราะการยกกำลังไปก่อนคูณ)
Dantevg

298

ฉันคิดว่าสิ่งที่คุณกำลังมองหาคือสูตรการแปลงRGB -> Luma

แสง / ดิจิตอลITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Digital ITU BT.601 (ให้น้ำหนักมากกว่าส่วนประกอบ R และ B):

Y = 0.299 R + 0.587 G + 0.114 B

หากคุณยินดีแลกเปลี่ยนความถูกต้องเพื่อความสมบูรณ์มีสองสูตรโดยประมาณสำหรับสูตรนี้:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

สามารถคำนวณได้อย่างรวดเร็วดังนี้

Y = (R+R+B+G+G+G)/6

Y = (R+R+R+B+G+G+G+G)>>3

47
ฉันชอบที่คุณใส่ค่าที่แม่นยำ แต่ยังรวมถึงทางลัดประเภท "ใกล้พอ" ที่รวดเร็ว +1
Beska

3
@Janathan Dumaine - สูตรการคำนวณสองสูตรทั้งสองอย่างรวมถึงสีน้ำเงิน - อันแรกคือ (2 * สีแดง + Blue+ 3 * สีเขียว) / 6, อันที่สองคือ (3 * สีแดง + Blue+ 4 * สีเขียว) >> 3 ได้รับทั้งในการประมาณอย่างรวดเร็ว Blue มีน้ำหนักต่ำสุด แต่ก็ยังอยู่ที่นั่น
Franci Penov

84
@JonathanDumaine นั่นเป็นเพราะสายตามนุษย์นั้นเป็นสีฟ้าน้อยที่สุด ;-)
Christopher Oezbek

4
รุ่นด่วนทำงานได้ดี ผ่านการทดสอบและนำไปใช้กับแอปในโลกแห่งความจริงด้วยผู้ใช้หลายพันคนทุกอย่างดูดี
milosmns

10
รุ่นที่รวดเร็วยิ่งเร็วขึ้นถ้าคุณทำมันเป็น: Y = (R<<1+R+G<<2+B)>>3(นั่นเป็นเพียงรอบ CPU 3-4 รอบบน ARM) แต่ฉันคิดว่าคอมไพเลอร์ที่ดีจะทำการเพิ่มประสิทธิภาพนั้นสำหรับคุณ
rjmunro

105

ฉันทำการเปรียบเทียบอัลกอริทึมทั้งสามในคำตอบที่ยอมรับแล้ว ฉันสร้างสีในวงรอบซึ่งมีการใช้สีเพียงประมาณ 400 สีเท่านั้น แต่ละสีจะถูกแทนด้วย 2x2 พิกเซลสีจะถูกจัดเรียงจากที่มืดที่สุดถึงเบาที่สุด (ซ้ายไปขวาบนลงล่าง)

ภาพที่ 1 - ความสว่าง (สัมพัทธ์)

0.2126 * R + 0.7152 * G + 0.0722 * B

ภาพที่ 2 - http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

ภาพที่ 3 - โมเดลสี HSP

sqrt(0.299 * R^2 + 0.587 * G^2 + 0.114 * B^2)

ภาพที่ 4 - WCAG 2.0 SC 1.4.3 ความสว่างสัมพัทธ์และสูตรอัตราส่วนคอนทราสต์ (ดูที่@คำตอบของซิงโครที่นี่ )

รูปแบบสามารถมองเห็นได้บางครั้งในภาพที่ 1 และ 2 ขึ้นอยู่กับจำนวนของสีในแถวเดียว ฉันไม่เคยเห็นลวดลายบนรูปภาพจากอัลกอริธึมที่ 3 หรือ 4

ถ้าฉันต้องเลือกฉันจะใช้อัลกอริธึมหมายเลข 3 เนื่องจากง่ายต่อการใช้งานมากและเร็วกว่าอันดับ 4 ประมาณ 33%

การเปรียบเทียบความสว่างของอัลกอริทึม


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

8
ภาพเปรียบเทียบของคุณไม่ถูกต้องเนื่องจากคุณไม่ได้ป้อนข้อมูลที่ถูกต้องให้กับฟังก์ชั่นทั้งหมด ฟังก์ชั่นแรกต้องการอินพุต RGB เชิงเส้น ฉันสามารถสร้างเอฟเฟกต์แถบคาดขึ้นใหม่ได้โดยการให้RGB แบบไม่เชิงเส้น (เช่นแก้ไขแกมม่า) การแก้ไขปัญหานี้คุณจะไม่ได้รับสิ่งประดิษฐ์แถบและฟังก์ชั่นที่ 1 เป็นผู้ชนะที่ชัดเจน
สูงสุด

1
@Max ^2และsqrtรวมอยู่ในสูตรที่สามเป็นวิธีที่เร็วใกล้เคียงกับเส้นตรงจาก RGB ที่ไม่ใช่เชิงเส้น RGB แทน^2.2และ^(1/2.2)ที่จะถูกต้องมากขึ้น การใช้อินพุตที่ไม่ใช่เชิงเส้นแทนที่จะเป็นลิเนียร์นั้นเป็นเรื่องปกติ
Mark Ransom

53

ด้านล่างเป็นอัลกอริทึมที่ถูกต้องเพียงอย่างเดียวสำหรับการแปลงภาพ sRGB ตามที่ใช้ในเบราว์เซอร์และอื่น ๆ

มีความจำเป็นต้องใช้ฟังก์ชันผกผันของแกมม่ากับพื้นที่สีก่อนคำนวณผลิตภัณฑ์ด้านใน จากนั้นคุณใช้ฟังก์ชันแกมมากับค่าที่ลดลง การไม่รวมฟังก์ชันแกมมาอาจส่งผลให้เกิดข้อผิดพลาดสูงถึง 20%

สำหรับคอมพิวเตอร์ทั่วไปสิ่งต่าง ๆ พื้นที่สีคือ sRGB ตัวเลขที่ถูกต้องสำหรับ sRGB นั้นประมาณ 0.21, 0.72, 0.07 แกมมาสำหรับ sRGB เป็นฟังก์ชั่นคอมโพสิตที่ใกล้เคียงกับการยกกำลังโดย 1 / (2.2) นี่คือสิ่งทั้งหมดใน C ++

// sRGB luminance(Y) values
const double rY = 0.212655;
const double gY = 0.715158;
const double bY = 0.072187;

// Inverse of sRGB "gamma" function. (approx 2.2)
double inv_gam_sRGB(int ic) {
    double c = ic/255.0;
    if ( c <= 0.04045 )
        return c/12.92;
    else 
        return pow(((c+0.055)/(1.055)),2.4);
}

// sRGB "gamma" function (approx 2.2)
int gam_sRGB(double v) {
    if(v<=0.0031308)
        v *= 12.92;
    else 
        v = 1.055*pow(v,1.0/2.4)-0.055;
    return int(v*255+0.5); // This is correct in C++. Other languages may not
                           // require +0.5
}

// GRAY VALUE ("brightness")
int gray(int r, int g, int b) {
    return gam_sRGB(
            rY*inv_gam_sRGB(r) +
            gY*inv_gam_sRGB(g) +
            bY*inv_gam_sRGB(b)
    );
}

5
นั่นเป็นเพียงวิธีกำหนด sRGB ฉันคิดว่าเหตุผลก็คือมันช่วยหลีกเลี่ยงปัญหาตัวเลขบางอย่างใกล้ศูนย์ มันจะไม่สร้างความแตกต่างมากนักถ้าคุณเพิ่งยกตัวเลขให้เป็นพลังของ 2.2 และ 1 / 2.2
Jive Dadson

8
JMD - เป็นส่วนหนึ่งของงานในแล็บการรับรู้ด้วยสายตาฉันได้ทำการวัดค่าความส่องสว่างโดยตรงบนจอภาพ CRT และสามารถยืนยันได้ว่ามีขอบเขตเชิงเส้นของความส่องสว่างที่ด้านล่างของช่วงของค่า
Jerry Federspiel

2
ฉันรู้ว่ามันเก่ามาก แต่ก็ยังอยู่ที่นั่นเพื่อค้นหา ฉันไม่คิดว่ามันจะถูกต้อง ไม่ควรเป็นสีเทา (255,255,255) = สีเทา (255,0,0) + สีเทา (0,255,0) + สีเทา (0,0,255)? มันไม่ได้
DCBillen

2
@DCBillen: ไม่เนื่องจากค่าอยู่ในพื้นที่ sRGB ที่แก้ไขด้วยแกมม่าไม่เชิงเส้นคุณจึงไม่สามารถเพิ่มได้ หากคุณต้องการที่จะเพิ่มพวกเขาคุณควรทำก่อนที่จะโทร gam_sRGB
rdb

1
@DCBillen Rdb ถูกต้อง วิธีที่จะเพิ่มพวกเขาจะปรากฏในฟังก์ชั่น int สีเทา (int r, int g, int b) ซึ่ง "uncalls" gam_sRGB มันช่วยฉันได้ว่าหลังจากสี่ปีคำตอบที่ถูกต้องนั้นอยู่ในระดับต่ำมาก :-) ไม่จริง .. ฉันจะผ่านมันไป
Jive Dadson

45

คำตอบ "ยอมรับ" ไม่ถูกต้องและไม่สมบูรณ์

คำตอบเดียวที่มีความถูกต้องเป็น@ Jive-dadsonและ@EddingtonsMonkeyคำตอบและในการสนับสนุน@ Nils-Pipenbrinck คำตอบอื่น ๆ(รวมถึงคำตอบที่ยอมรับ)กำลังเชื่อมโยงหรืออ้างถึงแหล่งข้อมูลที่ไม่ถูกต้องไม่เกี่ยวข้องล้าสมัยหรือแตกหัก

สั้น ๆ :

  • sRGB ต้องเป็นLINEARIZEDก่อนที่จะใช้สัมประสิทธิ์
  • ความส่องสว่าง (L หรือ Y) เป็นเส้นตรงเช่นเดียวกับแสง
  • การรับรู้ความสว่าง (L *) ไม่เชิงเส้นเหมือนกับการรับรู้ของมนุษย์
  • HSV และ HSL นั้นไม่ถูกต้องจากระยะไกลในแง่ของการรับรู้
  • มาตรฐาน IEC สำหรับ sRGB ระบุขีด จำกัด 0.04045 ไม่ใช่ 0.03928 (นั่นมาจากแบบร่างแรกที่ล้าสมัย)
  • จะมีประโยชน์(เช่นเมื่อเทียบกับการรับรู้) , ระยะทางแบบยุคลิดจำเป็นต้องมีพื้นที่เวกเตอร์คาร์ทีเซียนแบบรับรู้เช่น CIELAB sRGB ไม่ใช่หนึ่งเดียว

สิ่งต่อไปนี้เป็นคำตอบที่ถูกต้องและครบถ้วน:

เนื่องจากหัวข้อนี้ปรากฏอย่างสูงในเครื่องมือค้นหาฉันจึงเพิ่มคำตอบนี้เพื่อชี้แจงความเข้าใจที่คลาดเคลื่อนในเรื่องต่างๆ

ความสว่างเป็นคุณสมบัติการรับรู้มันไม่มีการวัดโดยตรง

การรับรู้ความสว่างถูกวัดโดยแบบจำลองการมองเห็นบางอย่างเช่น CIELAB ที่นี่ L * (Lstar) เป็นการวัดการรับรู้แสงและไม่ตรงกับเส้นโค้งโดยประมาณสำหรับการมองเห็นของมนุษย์

ความส่องสว่างเป็นตัวชี้วัดเชิงเส้นของแสงซึ่งมีน้ำหนักเชิงสเปกตรัมสำหรับการมองเห็นปกติ แต่ไม่ได้ปรับสำหรับการรับรู้ที่ไม่ใช่เชิงเส้นของความสว่าง

Luma ( Y´prime ) คือสัญญาณแกมม่าที่เข้ารหัสแล้วและมีน้ำหนักถ่วงน้ำหนักที่ใช้ในการเข้ารหัสวิดีโอบางรายการ เพื่อไม่ให้สับสนกับความส่องสว่างเชิงเส้น

แกมมาหรือการถ่ายโอนโค้ง (TRC) เป็นโค้งที่มักจะคล้ายกับการรับรู้โค้งและมักจะใช้กับข้อมูลภาพสำหรับการจัดเก็บหรือออกอากาศเพื่อลดเสียงที่รับรู้และ / หรือปรับปรุงการใช้ข้อมูล (และเหตุผลที่เกี่ยวข้อง)

ในการพิจารณาความสว่างที่รับรู้อันดับแรกให้แปลงค่าภาพแกมม่าที่เข้ารหัส R´G´B´ เป็นความส่องสว่างเชิงเส้น ( LหรือY) จากนั้นเป็นความสว่างที่มองไม่เห็นเชิงเส้น ( L*)


เพื่อค้นหา LUMINANCE:

... เพราะเห็นได้ชัดว่ามันหายไปที่ไหนสักแห่ง ...

ขั้นตอนแรก:

แปลงค่าจำนวนเต็ม sRGB 8 บิตทั้งหมดเป็นทศนิยม 0.0-1.0

  vR = sR / 255;
  vG = sG / 255;
  vB = sB / 255;

ขั้นตอนที่สอง:

แปลง RGB ที่เข้ารหัสแกมม่าเป็นค่าเชิงเส้น ตัวอย่างเช่น sRGB (มาตรฐานคอมพิวเตอร์) ต้องใช้ power curve ประมาณ V ^ 2.2 แม้ว่าการแปลง "ถูกต้อง" จะเป็น:

sRGB ถึงเป็น Linear

โดยที่ V´เป็นช่องสัญญาณ s, RGB, G หรือ B เข้ารหัสของแกมมา
pseudocode:

function sRGBtoLin(colorChannel) {
        // Send this function a decimal sRGB gamma encoded color value
        // between 0.0 and 1.0, and it returns a linearized value.

    if ( colorChannel <= 0.04045 ) {
            return colorChannel / 12.92;
        } else {
            return pow((( colorChannel + 0.055)/1.055),2.4));
        }
    }

ขั้นตอนที่สาม:

หากต้องการค้นหา Luminance (Y) ให้ใช้ค่าสัมประสิทธิ์มาตรฐานสำหรับ sRGB:

ใช้ค่าสัมประสิทธิ์ Y = R * 0.2126 + G * 0.7152 + B * 0.0722

Pseudocode โดยใช้ฟังก์ชั่นด้านบน:

Y = (0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB))

เพื่อค้นหา LIGHTNESS ที่รับรู้:

ขั้นตอนที่สี่:

ใช้ความส่องสว่าง Y จากด้านบนแล้วแปลงเป็น L *

L * จากสมการ Y
pseudocode:

function YtoLstar(Y) {
        // Send this function a luminance value between 0.0 and 1.0,
        // and it returns L* which is "perceptual lightness"

    if ( Y <= (216/24389) {       // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
            return Y * (24389/27);  // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
        } else {
            return pow(Y,(1/3)) * 116 - 16;
        }
    }

L * คือค่าตั้งแต่ 0 (ดำ) ถึง 100 (ขาว) โดยที่ 50 คือการรับรู้ "สีเทากลาง" L * = 50 เท่ากับ Y = 18.4 หรือกล่าวอีกนัยหนึ่งว่าการ์ดสีเทา 18% ซึ่งเป็นตัวแทนของการถ่ายภาพกลาง (Ansel Adams zone V)

อ้างอิง:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
คำถามที่พบบ่อยเกี่ยวกับแกมมาของ Charles Poynton


@Remem ขอบคุณ - ฉันเห็นข้อความแปลก ๆ และไม่สมบูรณ์และรู้สึกว่ามันจะเป็นประโยชน์ในการตอกย้ำลงโดยเฉพาะอย่างยิ่งเนื่องจากกระทู้นี้ยังมีอันดับสูงในเครื่องมือค้นหา
Myndex

ฉันสร้างการสาธิตเปรียบเทียบBT.601 LumaและCIE 1976 L * Perceptual Grayโดยใช้คำสั่ง MATLAB ไม่กี่คำ:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem

@Myndex ฉันใช้สูตรของคุณเพื่อไปที่ L * แต่ฉันยังได้รับผลลัพธ์ที่แปลกไม่ว่าสูตรที่ฉันใช้ ... กับคุณ L * ของ # d05858 นั้นมืดกว่า L * ของ # c51c2a ... มีไหม วิธีที่จะทำให้ถูกต้อง? เหตุใดจึงไม่มีสูตรทำงานตามที่คาดไว้ :(
sjahan

1
@asdfasdfads ใช่L*a*b*ไม่คำนึงถึงคุณลักษณะทางจิตวิทยาจำนวนมาก Helmholtz-Kohlrausch effect เป็นหนึ่งเดียว แต่ก็มีอีกหลายอย่าง CIELAB ไม่ใช่รูปแบบการประเมินภาพ "เต็ม" ไม่ว่าด้วยวิธีใด ในโพสต์ของฉันฉันพยายามที่จะครอบคลุมแนวคิดพื้นฐานอย่างสมบูรณ์ที่สุดโดยไม่ต้องเข้าไปใน minutiae ลึกมาก โมเดล Hunt, โมเดลของ Fairchild และอื่น ๆ ทำงานได้อย่างสมบูรณ์มากขึ้น แต่ก็มีความซับซ้อนมากขึ้น
Myndex

1
@Myndex ไม่เป็นไรการดำเนินการของฉันเป็นไปตามความเหนื่อยล้าและผลลัพธ์ที่ไม่ดีของฉันมาจากที่ :( ขอบคุณมากสำหรับความช่วยเหลือและโพสต์ของคุณซึ่งมีค่ามาก!
sjahan

11

ฉันพบรหัสนี้ (เขียนใน C #) ซึ่งทำงานได้ดีเยี่ยมในการคำนวณ "ความสว่าง" ของสี ในสถานการณ์สมมตินี้รหัสพยายามตรวจสอบว่าจะใส่ข้อความสีขาวหรือสีดำทับสี


1
นั่นคือสิ่งที่ฉันต้องการ ฉันกำลังทำการสาธิต "แถบสี" แบบคลาสสิกและต้องการติดป้ายบนสีด้วยตัวเลือกสีดำหรือขาวที่ดีที่สุด!
RufusVS

10

น่าสนใจสูตรนี้สำหรับ RGB => HSVใช้เพียง v = MAX3 (r, g, b) คุณสามารถใช้จำนวนสูงสุดได้ (r, g, b) เป็น V ใน HSV

ฉันตรวจสอบและในหน้า 575 ของHearn & Bakerนี่คือวิธีที่พวกเขาคำนวณ "คุณค่า" เช่นกัน

จาก Hearn & Baker หน้า 319


เพียงเพื่อบันทึกการเชื่อมโยงจะตายรุ่นเก็บถาวรที่นี่ - web.archive.org/web/20150906055359/http://…
ปีเตอร์

HSV นั้นไม่เหมือนกัน (และยังไม่ใกล้เคียง) มันถูกใช้เป็นเพียงวิธีที่ "สะดวก" ในการปรับสี แต่มันไม่เกี่ยวข้องกับการรับรู้และ V ไม่เกี่ยวข้องกับค่าที่แท้จริงของ L หรือ Y (CIE Luminance)
Myndex

9

แทนที่จะหลงทางในการเลือกสูตรที่กล่าวถึงในที่นี้ฉันขอแนะนำให้คุณลองสูตรที่แนะนำโดยมาตรฐาน W3C

นี่คือตรงไปตรงมา แต่แน่นอนการดำเนิน PHP ของWCAG 2.0 SC 1.4.3 ญาติความสว่างและความคมชัดอัตราส่วนสูตร มันสร้างค่าที่เหมาะสมสำหรับการประเมินอัตราส่วนที่จำเป็นสำหรับการปฏิบัติตาม WCAG เช่นเดียวกับในหน้านี้และเช่นนี้มีความเหมาะสมและเหมาะสมสำหรับเว็บแอปใด ๆ นี่เป็นเรื่องเล็กน้อยที่จะย้ายไปยังภาษาอื่น

/**
 * Calculate relative luminance in sRGB colour space for use in WCAG 2.0 compliance
 * @link http://www.w3.org/TR/WCAG20/#relativeluminancedef
 * @param string $col A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function relativeluminance($col) {
    //Remove any leading #
    $col = trim($col, '#');
    //Convert 3-digit to 6-digit
    if (strlen($col) == 3) {
        $col = $col[0] . $col[0] . $col[1] . $col[1] . $col[2] . $col[2];
    }
    //Convert hex to 0-1 scale
    $components = array(
        'r' => hexdec(substr($col, 0, 2)) / 255,
        'g' => hexdec(substr($col, 2, 2)) / 255,
        'b' => hexdec(substr($col, 4, 2)) / 255
    );
    //Correct for sRGB
    foreach($components as $c => $v) {
        if ($v <= 0.04045) {
            $components[$c] = $v / 12.92;
        } else {
            $components[$c] = pow((($v + 0.055) / 1.055), 2.4);
        }
    }
    //Calculate relative luminance using ITU-R BT. 709 coefficients
    return ($components['r'] * 0.2126) + ($components['g'] * 0.7152) + ($components['b'] * 0.0722);
}

/**
 * Calculate contrast ratio acording to WCAG 2.0 formula
 * Will return a value between 1 (no contrast) and 21 (max contrast)
 * @link http://www.w3.org/TR/WCAG20/#contrast-ratiodef
 * @param string $c1 A 3 or 6-digit hex colour string
 * @param string $c2 A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function contrastratio($c1, $c2) {
    $y1 = relativeluminance($c1);
    $y2 = relativeluminance($c2);
    //Arrange so $y1 is lightest
    if ($y1 < $y2) {
        $y3 = $y1;
        $y1 = $y2;
        $y2 = $y3;
    }
    return ($y1 + 0.05) / ($y2 + 0.05);
}

ทำไมคุณถึงต้องการคำนิยาม w3c เป็นการส่วนตัวฉันได้ใช้ทั้ง CCIR 601 และ w3c ที่แนะนำและฉันก็พอใจกับผลลัพธ์ CCIR 601 มากขึ้น
user151496

1
เพราะอย่างที่ฉันพูดมันแนะนำโดยทั้ง W3C และ WCAG?
ซิงโคร

1
สูตร W3C ไม่ถูกต้องในหลายระดับ มันไม่ได้คำนึงถึงการรับรู้ของมนุษย์พวกเขากำลังใช้ความคมชัด "ง่าย" โดยใช้ความส่องสว่างซึ่งเป็นเส้นตรงและไม่เหมือนกันทุกการรับรู้ เหนือสิ่งอื่นใดมันปรากฏว่าพวกเขาใช้มาตรฐานบางอย่างที่เก่าแก่ที่สุดในปี 1988 (!!!) ซึ่งไม่เกี่ยวข้องในวันนี้ (มาตรฐานเหล่านั้นมาจากจอภาพโมโนโครมเช่นสีเขียว / ดำ ไม่พิจารณาสีเทาหรือสี)
Myndex

1
นั่นคือขยะที่สมบูรณ์ Luma รับรู้โดยเฉพาะ - นั่นคือสาเหตุที่มันมีค่าสัมประสิทธิ์ที่แตกต่างกันสำหรับสีแดงสีเขียวและสีน้ำเงิน อายุไม่มีอะไรเกี่ยวข้องกับมัน - วันที่การรับรู้สี CIE Lab ที่ยอดเยี่ยมตั้งแต่ปี 1976 พื้นที่ W3C ไม่ดีเท่าไหร่ แต่เป็นการประมาณในทางปฏิบัติที่ดีซึ่งง่ายต่อการคำนวณ หากคุณมีสิ่งที่สร้างสรรค์ที่จะนำเสนอโพสต์นั้นแทนการวิจารณ์ที่ว่างเปล่า
Synchro

3
เพียงเพื่อเพิ่ม / อัปเดต : เรากำลังทำการวิจัยขั้นตอนวิธีการเปลี่ยนรูปแบบที่ดีขึ้นความคมชัดในการรับรู้(การอภิปรายใน Github ฉบับที่ 695) อย่างไรก็ตามเป็นปัญหา FYI ​​ที่แยกต่างหากเกณฑ์สำหรับ sRGB คือ0.04045และไม่ใช่ 0.03928 ซึ่งอ้างอิงจากร่าง sRGB ที่ล้าสมัยในช่วงต้น IEC std ที่เชื่อถือได้ใช้ 0.04045 และคำขอการดึงกำลังจะเกิดขึ้นเพื่อแก้ไขข้อผิดพลาดนี้ใน WCAG (อ้างอิง: IEC 61966-2-1: 1999) นี่คือปัญหา Github 360 แม้ว่าจะพูดถึงใน 8 บิตไม่มีความแตกต่างจริง - ใกล้ถึงจุดสิ้นสุดของเธรด 360 ฉันมีแผนภูมิข้อผิดพลาดรวมถึง 0.04045 / 0.03928 ใน 8 บิต
Myndex

8

ในการเพิ่มสิ่งที่คนอื่นพูด:

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

ความแตกต่างของความส่องสว่างระหว่างการฝังแกมม่ากับการทำแกมม่าที่เหมาะสมนั้นสูงถึง 20% ในสีเทาเข้ม


2

ฉันกำลังแก้ไขงานที่คล้ายกันในวันนี้ด้วยจาวาสคริปต์ ฉันgetPerceivedLightness(rgb)ใช้ฟังก์ชันนี้เป็นสี HEX RGB มันเกี่ยวข้องกับเอฟเฟ็กต์ Helmholtz-Kohlrausch ผ่านสูตร Fairchild และ Perrotta สำหรับการแก้ไขความสว่าง

/**
 * Converts RGB color to CIE 1931 XYZ color space.
 * https://www.image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz
 * @param  {string} hex
 * @return {number[]}
 */
export function rgbToXyz(hex) {
    const [r, g, b] = hexToRgb(hex).map(_ => _ / 255).map(sRGBtoLinearRGB)
    const X =  0.4124 * r + 0.3576 * g + 0.1805 * b
    const Y =  0.2126 * r + 0.7152 * g + 0.0722 * b
    const Z =  0.0193 * r + 0.1192 * g + 0.9505 * b
    // For some reason, X, Y and Z are multiplied by 100.
    return [X, Y, Z].map(_ => _ * 100)
}

/**
 * Undoes gamma-correction from an RGB-encoded color.
 * https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
 * /programming/596216/formula-to-determine-brightness-of-rgb-color
 * @param  {number}
 * @return {number}
 */
function sRGBtoLinearRGB(color) {
    // Send this function a decimal sRGB gamma encoded color value
    // between 0.0 and 1.0, and it returns a linearized value.
    if (color <= 0.04045) {
        return color / 12.92
    } else {
        return Math.pow((color + 0.055) / 1.055, 2.4)
    }
}

/**
 * Converts hex color to RGB.
 * /programming/5623838/rgb-to-hex-and-hex-to-rgb
 * @param  {string} hex
 * @return {number[]} [rgb]
 */
function hexToRgb(hex) {
    const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    if (match) {
        match.shift()
        return match.map(_ => parseInt(_, 16))
    }
}

/**
 * Converts CIE 1931 XYZ colors to CIE L*a*b*.
 * The conversion formula comes from <http://www.easyrgb.com/en/math.php>.
 * https://github.com/cangoektas/xyz-to-lab/blob/master/src/index.js
 * @param   {number[]} color The CIE 1931 XYZ color to convert which refers to
 *                           the D65/2° standard illuminant.
 * @returns {number[]}       The color in the CIE L*a*b* color space.
 */
// X, Y, Z of a "D65" light source.
// "D65" is a standard 6500K Daylight light source.
// https://en.wikipedia.org/wiki/Illuminant_D65
const D65 = [95.047, 100, 108.883]
export function xyzToLab([x, y, z]) {
  [x, y, z] = [x, y, z].map((v, i) => {
    v = v / D65[i]
    return v > 0.008856 ? Math.pow(v, 1 / 3) : v * 7.787 + 16 / 116
  })
  const l = 116 * y - 16
  const a = 500 * (x - y)
  const b = 200 * (y - z)
  return [l, a, b]
}

/**
 * Converts Lab color space to Luminance-Chroma-Hue color space.
 * http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html
 * @param  {number[]}
 * @return {number[]}
 */
export function labToLch([l, a, b]) {
    const c = Math.sqrt(a * a + b * b)
    const h = abToHue(a, b)
    return [l, c, h]
}

/**
 * Converts a and b of Lab color space to Hue of LCH color space.
 * /programming/53733379/conversion-of-cielab-to-cielchab-not-yielding-correct-result
 * @param  {number} a
 * @param  {number} b
 * @return {number}
 */
function abToHue(a, b) {
    if (a >= 0 && b === 0) {
        return 0
    }
    if (a < 0 && b === 0) {
        return 180
    }
    if (a === 0 && b > 0) {
        return 90
    }
    if (a === 0 && b < 0) {
        return 270
    }
    let xBias
    if (a > 0 && b > 0) {
        xBias = 0
    } else if (a < 0) {
        xBias = 180
    } else if (a > 0 && b < 0) {
        xBias = 360
    }
    return radiansToDegrees(Math.atan(b / a)) + xBias
}

function radiansToDegrees(radians) {
    return radians * (180 / Math.PI)
}

function degreesToRadians(degrees) {
    return degrees * Math.PI / 180
}

/**
 * Saturated colors appear brighter to human eye.
 * That's called Helmholtz-Kohlrausch effect.
 * Fairchild and Pirrotta came up with a formula to
 * calculate a correction for that effect.
 * "Color Quality of Semiconductor and Conventional Light Sources":
 * https://books.google.ru/books?id=ptDJDQAAQBAJ&pg=PA45&lpg=PA45&dq=fairchild+pirrotta+correction&source=bl&ots=7gXR2MGJs7&sig=ACfU3U3uIHo0ZUdZB_Cz9F9NldKzBix0oQ&hl=ru&sa=X&ved=2ahUKEwi47LGivOvmAhUHEpoKHU_ICkIQ6AEwAXoECAkQAQ#v=onepage&q=fairchild%20pirrotta%20correction&f=false
 * @return {number}
 */
function getLightnessUsingFairchildPirrottaCorrection([l, c, h]) {
    const l_ = 2.5 - 0.025 * l
    const g = 0.116 * Math.abs(Math.sin(degreesToRadians((h - 90) / 2))) + 0.085
    return l + l_ * g * c
}

export function getPerceivedLightness(hex) {
    return getLightnessUsingFairchildPirrottaCorrection(labToLch(xyzToLab(rgbToXyz(hex))))
}

1

HSV colourspace ควรทำตามเคล็ดลับดูบทความ wikipediaขึ้นอยู่กับภาษาที่คุณใช้ในการทำงานคุณอาจได้รับการแปลงห้องสมุด

H คือสีซึ่งเป็นค่าตัวเลขสำหรับสี (เช่นสีแดงสีเขียว ... )

S คือความอิ่มตัวของสีนั่นคือ 'รุนแรง' อย่างไร

V คือ 'ความสว่าง' ของสี


7
ปัญหาเกี่ยวกับพื้นที่สี HSV คือการที่คุณสามารถมีความอิ่มตัวของสีเดียวกันและความคุ้มค่า แต่สีที่แตกต่างกันสำหรับสีฟ้าและสีเหลือง สีเหลืองเป็นมากสว่างกว่าสีฟ้า ไปเหมือนกันสำหรับ HSL
Ian Boyd

hsv ให้ "ความสว่าง" ของสีในแง่เทคนิค ในความสว่างความเข้าใจ hsv ล้มเหลวจริงๆ
user151496

HSV และ HSL นั้นไม่แม่นยำนัก (และยังไม่ใกล้เคียง) พวกมันมีประโยชน์สำหรับ "การควบคุม" สำหรับการปรับสีสัมพัทธ์ แต่ไม่ใช่สำหรับการทำนายที่แม่นยำของการรับรู้แสง ใช้ L * จาก CIELAB เพื่อการรับรู้แสง
Myndex

1

ค่าความส่องสว่าง RGB = 0.3 R + 0.59 G + 0.11 B

http://www.scantips.com/lumin.html

หากคุณกำลังมองหาว่าสีใกล้เคียงกับสีขาวมากแค่ไหนคุณสามารถใช้ Euclidean Distance ได้ตั้งแต่ (255, 255, 255)

ฉันคิดว่าพื้นที่สีของ RGB นั้นดูไม่สม่ำเสมอเมื่อเทียบกับระยะทางยูคอนลิเดียน L2 พื้นที่ที่สม่ำเสมอรวมถึง CIE LAB และ LUV


1

สูตรผกผัน - แกมม่าโดย Jive Dadson จำเป็นต้องลบการปรับครึ่งเมื่อใช้งานใน Javascript นั่นคือการกลับมาจากฟังก์ชั่น gam_sRGB จะต้องกลับ int (v * 255); ไม่คืนค่า int (v * 255 + .5); ปรับครึ่งรอบขึ้นและอาจทำให้ค่าหนึ่งสูงเกินไปใน R = G = B เช่นสีเทาสามกลุ่ม การแปลง Greyscale บน R = G = B สามควรสร้างมูลค่าเท่ากับ R; มันเป็นข้อพิสูจน์ว่าสูตรนั้นถูกต้อง ดูNine Shades of Greyscaleสำหรับสูตรที่ใช้งาน (โดยไม่ต้องปรับครึ่ง)


ดูเหมือนว่าคุณจะรู้สิ่งของของคุณดังนั้นฉันจึงลบ +0.5
Jive Dadson

ฉันทำการทดลอง ใน C ++ มันต้องการ +0.5 ดังนั้นฉันจึงใส่กลับเข้าไปฉันเพิ่มความคิดเห็นเกี่ยวกับการแปลเป็นภาษาอื่น
Jive Dadson

1

ฉันสงสัยว่าสัมประสิทธิ์ rgb เหล่านั้นถูกกำหนดอย่างไร ฉันทำการทดลองด้วยตัวเองและฉันก็จบลงด้วยสิ่งต่อไปนี้:

Y = 0.267 R + 0.642 G + 0.091 B

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

สำหรับการอ้างอิง:

ITU BT.709:

Y = 0.2126 R + 0.7152 G + 0.0722 B

ITU BT.601:

Y = 0.299 R + 0.587 G + 0.114 B

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

และใช่ฉันน่าจะมีการมองเห็นสีปกติ


ในการทดลองของคุณคุณปรับขนาดเชิงเส้นเพื่อลบองค์ประกอบแกมมาก่อนหรือไม่ หากคุณไม่สามารถอธิบายผลลัพธ์ได้ แต่ค่าสัมประสิทธิ์เกี่ยวข้องกับการทดลอง CIE 1931 และค่าเฉลี่ยของผู้สังเกตการณ์ 17 คนดังนั้นใช่มีความแปรปรวนส่วนบุคคลในผลลัพธ์
Myndex

1

นี่คือรหัส C เล็กน้อยที่ควรคำนวณความส่องสว่างที่รับรู้ได้อย่างเหมาะสม

// reverses the rgb gamma
#define inverseGamma(t) (((t) <= 0.0404482362771076) ? ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4))

//CIE L*a*b* f function (used to convert XYZ to L*a*b*)  http://en.wikipedia.org/wiki/Lab_color_space
#define LABF(t) ((t >= 8.85645167903563082e-3) ? powf(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0))


float
rgbToCIEL(PIXEL p)
{
   float y;
   float r=p.r/255.0;
   float g=p.g/255.0;
   float b=p.b/255.0;

   r=inverseGamma(r);
   g=inverseGamma(g);
   b=inverseGamma(b);

   //Observer = 2°, Illuminant = D65 
   y = 0.2125862307855955516*r + 0.7151703037034108499*g + 0.07220049864333622685*b;

   // At this point we've done RGBtoXYZ now do XYZ to Lab

   // y /= WHITEPOINT_Y; The white point for y in D65 is 1.0

    y = LABF(y);

   /* This is the "normal conversion which produces values scaled to 100
    Lab.L = 116.0*y - 16.0;
   */
   return(1.16*y - 0.16); // return values for 0.0 >=L <=1.0
}

0

กรุณากำหนดความสว่าง หากคุณกำลังมองหาว่าสีใกล้เคียงกับสีขาวมากแค่ไหนคุณสามารถใช้Euclidean Distance ได้ตั้งแต่ (255, 255, 255)


1
ไม่คุณไม่สามารถใช้ระยะ euclidian ระหว่างค่า sRGB ได้ sRGB ไม่ใช่พื้นที่คาร์ทีเซียน / เวกเตอร์แบบรับรู้ หากคุณต้องการใช้ระยะทาง Euclidian เป็นเครื่องวัดความแตกต่างของสีคุณต้องแปลงอย่างน้อยเป็น CIELAB หรือดีกว่าให้ใช้ CAM อย่าง CIECAM02
Myndex

0

'V' ของ HSV อาจเป็นสิ่งที่คุณกำลังมองหา MATLAB มีฟังก์ชั่น rgb2hsv และบทความ wikipedia ที่อ้างถึงก่อนหน้านี้เต็มไปด้วย pseudocode หากการแปลง RGB2HSV เป็นไปไม่ได้แบบจำลองที่แม่นยำน้อยกว่าจะเป็นรูปภาพระดับสีเทา


0

ลิงค์นี้จะอธิบายทุกอย่างในเชิงลึกรวมถึงสาเหตุที่ค่าคงที่ตัวคูณเหล่านั้นมีอยู่ก่อนค่า R, G และ B

แก้ไข: มันมีคำอธิบายหนึ่งในคำตอบที่นี่ด้วย (0.299 * R + 0.587 * G + 0.114 * B)


0

ในการพิจารณาความสว่างของสีด้วย R ฉันจะแปลงสีระบบ RGB ในสีระบบ HSV

ในสคริปต์ของฉันฉันใช้รหัสระบบ HEX ก่อนด้วยเหตุผลอื่น ๆ แต่คุณสามารถเริ่มต้นยังมีรหัสระบบสี rgb2hsv {grDevices}RGB เอกสารที่ได้คือที่นี่

นี่คือส่วนหนึ่งของรหัสของฉัน:

 sample <- c("#010101", "#303030", "#A6A4A4", "#020202", "#010100")
 hsvc <-rgb2hsv(col2rgb(sample)) # convert HEX to HSV
 value <- as.data.frame(hsvc) # create data.frame
 value <- value[3,] # extract the information of brightness
 order(value) # ordrer the color by brightness

0

เพื่อความชัดเจนสูตรที่ใช้รากที่สองจะต้องเป็น

sqrt(coefficient * (colour_value^2))

ไม่

sqrt((coefficient * colour_value))^2

การพิสูจน์เรื่องนี้อยู่ในการแปลงค่า R = G = B สามเป็น greyscale R ซึ่งจะเป็นจริงถ้าคุณกำหนดค่าสีเป็นสองเท่าไม่ใช่ค่าสีคูณค่าสัมประสิทธิ์ ดูNine Shades of Greyscale


5
มี mistmatches วงเล็บอยู่
log0

นอกจากค่าสัมประสิทธิ์ที่คุณใช้คือสแควร์รูทของค่าสัมประสิทธิ์ที่ถูกต้อง
RufusVS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.