Công thức xác định độ sáng của màu RGB


387

Tôi đang tìm kiếm một số loại công thức hoặc thuật toán để xác định độ sáng của màu cho các giá trị RGB. Tôi biết điều đó không thể đơn giản như việc cộng các giá trị RGB lại với nhau và có tổng tiền cao hơn sẽ sáng hơn, nhưng tôi không biết phải bắt đầu từ đâu.


8
Độ sáng cảm nhận là những gì tôi nghĩ rằng tôi đang tìm kiếm, cảm ơn bạn.
robmerica

2
Có một bài viết hay ( Thao tác màu sắc trong .NET - Phần 1 ) về không gian màu và các cuộc hội thoại giữa chúng bao gồm cả lý thuyết và mã (C #). Để biết câu trả lời, hãy nhìn vào Chuyển đổi giữa các chủ đề mô hình trong bài viết.
gạch dưới

4
Tôi đã là một thành viên trong nhiều năm, và tôi chưa bao giờ làm điều này trước đây. Tôi có thể đề nghị bạn xem lại câu trả lời và suy nghĩ lại nên chấp nhận cái nào không?
Jive Dadson

Câu trả lời:


456

Bạn có nghĩa là độ sáng? Nhận thức độ sáng? Độ chói?

  • Độ chói (tiêu chuẩn cho các không gian màu nhất định): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Độ chói (tùy chọn nhận thức 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Độ chói (tùy chọn nhận thức 2, tính toán chậm hơn): 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 )(nhờ @MatthewHerbst ) [3]

26
Lưu ý rằng cả hai đều nhấn mạnh các khía cạnh sinh lý: nhãn cầu của con người nhạy cảm nhất với ánh sáng xanh, ít hơn màu đỏ và ít nhất là màu xanh.
Bob Cross

16
Cũng lưu ý rằng tất cả những thứ này có lẽ là dành cho 0-1 RGB tuyến tính và có lẽ bạn đã điều chỉnh gamma 0-255 RGB. Họ không được chuyển đổi như bạn nghĩ.
alex lạ

4
Không chính xác. Trước khi áp dụng phép biến đổi tuyến tính, trước tiên người ta phải áp dụng nghịch đảo của hàm gamma cho không gian màu. Sau đó, sau khi áp dụng chức năng tuyến tính, chức năng gamma được áp dụng.
Jive Dadson

6
Trong công thức cuối cùng, nó là (0,299 * R) ^ 2 hay là 0,299 * (R ^ 2)?
Kaizer Sozay

3
@KaizerSozay Như được viết ở đây có nghĩa là 0.299*(R^2)(vì lũy thừa đi trước phép nhân)
Dantevg

298

Tôi nghĩ rằng những gì bạn đang tìm kiếm là công thức chuyển đổi RGB -> Luma .

Máy đo quang / kỹ thuật số ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

ITU BT.601 kỹ thuật số (tăng thêm trọng lượng cho các thành phần R và B):

Y = 0.299 R + 0.587 G + 0.114 B

Nếu bạn sẵn sàng đánh đổi độ chính xác để lấy nước hoa, có hai công thức gần đúng cho cái này:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Đây có thể được tính toán nhanh chóng như

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

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

47
Tôi thích rằng bạn đặt các giá trị chính xác, nhưng cũng bao gồm một phím tắt loại "đủ gần" nhanh chóng. +1.
Beska

3
@Jonathan Dumaine - cả hai công thức tính toán nhanh bao gồm màu xanh lam - Thứ nhất là (2 * Đỏ + Blue+ 3 * Xanh lục) / 6, thứ hai là (3 * Đỏ + Blue+ 4 * Xanh lục) >> 3. được cho phép, trong cả hai xấp xỉ nhanh, Blue có trọng lượng thấp nhất, nhưng nó vẫn ở đó.
Franci Penov

84
@JonathanDumaine Đó là vì mắt người ít cảm nhận nhất với Blue ;-)
Christopher Oezbek

4
Phiên bản nhanh hoạt động tốt. Đã thử nghiệm và áp dụng cho ứng dụng trong thế giới thực với hàng ngàn người dùng, mọi thứ đều ổn.
milosmns

10
Phiên bản nhanh thậm chí còn nhanh hơn nếu bạn làm như sau: Y = (R<<1+R+G<<2+B)>>3(đó chỉ là 3-4 chu kỳ CPU trên ARM) nhưng tôi đoán một trình biên dịch tốt sẽ thực hiện tối ưu hóa đó cho bạn.
rjmunro 11/03/2015

105

Tôi đã thực hiện so sánh ba thuật toán trong câu trả lời được chấp nhận. Tôi đã tạo ra các màu theo chu kỳ trong đó chỉ khoảng 400 màu được sử dụng. Mỗi màu được biểu thị bằng 2x2 pixel, các màu được sắp xếp từ tối nhất đến sáng nhất (trái sang phải, từ trên xuống dưới).

Hình thứ 1 - Độ chói (tương đối)

0.2126 * R + 0.7152 * G + 0.0722 * B

Bức ảnh thứ 2 - http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

Hình thứ 3 - Mô hình màu HSP

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

4 bức tranh - WCAG 2.0 SC 1.4.3 tương đối sángđộ tương phản công thức (xem @ Synchro của câu trả lời ở đây )

Hoa văn đôi khi có thể được phát hiện trên hình ảnh thứ 1 và thứ 2 tùy thuộc vào số lượng màu trong một hàng. Tôi không bao giờ phát hiện bất kỳ mẫu nào trên hình ảnh từ thuật toán thứ 3 hoặc thứ 4.

Nếu tôi phải chọn, tôi sẽ sử dụng thuật toán số 3 vì nó dễ thực hiện hơn và nhanh hơn khoảng 33% so với thuật toán thứ 4.

So sánh thuật toán độ sáng cảm nhận


3
Đối với tôi đây là câu trả lời tốt nhất bởi vì oyu sử dụng một mẫu hình ảnh cho phép bạn nhận biết nếu các màu khác nhau được hiển thị với cùng độ chói. Đối với tôi và người theo dõi hiện tại của tôi, bức tranh thứ 3 là "đẹp nhất" vì nó cũng nhanh hơn thứ 4 đó là một điểm cộng
CoffeDeveloper

8
Hình ảnh so sánh của bạn không chính xác vì bạn không cung cấp đầu vào chính xác cho tất cả các chức năng. Hàm đầu tiên yêu cầu đầu vào RGB tuyến tính ; Tôi chỉ có thể tái tạo hiệu ứng dải bằng cách cung cấp RGB phi tuyến (tức là đã hiệu chỉnh gamma). Khắc phục sự cố này, bạn không nhận được các tạo phẩm dải và chức năng thứ 1 là người chiến thắng rõ ràng.
Tối đa

1
@Max ^2và được sqrtbao gồm trong công thức thứ ba là một cách nhanh hơn để xấp xỉ RGB tuyến tính từ RGB không tuyến tính thay vì ^2.2^(1/2.2)điều đó sẽ đúng hơn. Sử dụng đầu vào phi tuyến thay vì đầu vào tuyến tính là rất đáng tiếc.
Đánh dấu tiền chuộc

53

Dưới đây là thuật toán CORRECT duy nhất để chuyển đổi hình ảnh sRGB, như được sử dụng trong các trình duyệt, v.v., sang thang độ xám.

Cần phải áp dụng nghịch đảo của hàm gamma cho không gian màu trước khi tính toán sản phẩm bên trong. Sau đó, bạn áp dụng hàm gamma cho giá trị giảm. Việc không kết hợp chức năng gamma có thể dẫn đến sai số lên tới 20%.

Đối với các công cụ máy tính thông thường, không gian màu là sRGB. Các số phù hợp cho sRGB là khoảng. 0,21, 0,72, 0,07. Gamma cho sRGB là một hàm tổng hợp xấp xỉ lũy thừa bằng 1 / (2.2). Đây là toàn bộ điều trong 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
Đó chỉ là cách sRGB được định nghĩa. Tôi nghĩ lý do là nó tránh được một số vấn đề về số gần bằng không. Sẽ không có nhiều khác biệt nếu bạn chỉ tăng các con số lên lũy thừa 2.2 và 1 / 2.2.
Jive Dadson 22/03/13

8
JMD - là một phần của công việc trong phòng thí nghiệm nhận thức trực quan, tôi đã thực hiện các phép đo độ chói trực tiếp trên màn hình CRT và có thể xác nhận rằng có một vùng độ chói tuyến tính ở dưới cùng của phạm vi giá trị.
Jerry Federspiel

2
Tôi biết điều này rất cũ, nhưng nó vẫn còn để tìm kiếm. Tôi không nghĩ nó có thể đúng. Không nên màu xám (255,255,255) = xám (255,0,0) + xám (0,255,0) + xám (0,0,255)? Nó không.
DCBillen

2
@DCBillen: không, vì các giá trị nằm trong không gian sRGB được điều chỉnh gamma phi tuyến tính, bạn không thể thêm chúng lên. Nếu bạn muốn thêm chúng lên, bạn nên làm như vậy trước khi gọi gam_sRGB.
rdb

1
@DCBillen Rdb là chính xác. Cách để thêm chúng được hiển thị trong hàm int grey (int r, int g, int b), "unalls" gam_sRGB. Tôi đau đớn rằng sau bốn năm, câu trả lời đúng được đánh giá rất thấp. :-) Không thực sự .. Tôi sẽ vượt qua nó.
Jive Dadson

45

Câu trả lời "Được chấp nhận" là không chính xác và không đầy đủ

Câu trả lời duy nhất chính xác là câu trả lời @ jive-Dadson@EddingtonsMonkey và hỗ trợ @ nils-pipenbrinck . Các câu trả lời khác (bao gồm cả được chấp nhận) đang liên kết đến hoặc trích dẫn các nguồn sai, không liên quan, lỗi thời hoặc bị hỏng.

Tóm tắt:

  • sRGB phải được TUYỆT VỜI trước khi áp dụng các hệ số.
  • Độ chói (L hoặc Y) là tuyến tính như ánh sáng.
  • Sự nhẹ nhàng nhận thức (L *) là phi tuyến tính như nhận thức của con người.
  • HSV và HSL thậm chí không chính xác từ xa về mặt nhận thức.
  • Tiêu chuẩn IEC cho sRGB chỉ định ngưỡng 0,04045 KHÔNG phải là 0,03928 (đó là từ một dự thảo ban đầu đã lỗi thời).
  • Hữu ích (tức là liên quan đến nhận thức) , khoảng cách Euclidian đòi hỏi một không gian vectơ Cartesian đồng nhất về mặt nhận thức như CIELAB. sRGB không phải là một.

Điều gì sau đây là một câu trả lời đúng và đầy đủ:

Bởi vì chủ đề này xuất hiện rất nhiều trong các công cụ tìm kiếm, tôi đang thêm câu trả lời này để làm rõ những quan niệm sai lầm khác nhau về chủ đề này.

Độ sáng là một thuộc tính tri giác, nó không có thước đo trực tiếp.

Độ sáng cảm nhận được đo bằng một số mô hình tầm nhìn như CIELAB, ở đây L * (Lstar) là thước đo độ sáng cảm nhận và là phi tuyến tính để xấp xỉ đường cong phản ứng phi tuyến tính của thị giác con người.

Độ chói là một thước đo tuyến tính của ánh sáng, có trọng số phổ cho tầm nhìn bình thường nhưng không được điều chỉnh cho nhận thức phi tuyến tính về độ sáng.

Luma ( Y tướng Prime) là tín hiệu được mã hóa, có trọng số gamma được sử dụng trong một số mã hóa video. Nó không bị nhầm lẫn với độ chói tuyến tính.

Gamma hoặc đường cong chuyển (TRC) là một đường cong thường tương tự như đường cong tri giác và thường được áp dụng cho dữ liệu hình ảnh để lưu trữ hoặc phát sóng để giảm nhiễu nhận biết và / hoặc cải thiện việc sử dụng dữ liệu (và lý do liên quan).

Để xác định độ sáng cảm nhận , trước tiên, chuyển đổi các giá trị hình ảnh gamma được mã hóa RmaGNHBB thành độ chói tuyến tính ( Lhoặc Y) và sau đó thành độ sáng cảm nhận phi tuyến tính ( L*)


ĐỂ TÌM KIẾM LUMINANCE:

... Bởi vì rõ ràng nó đã bị mất ở đâu đó ...

Bước một:

Chuyển đổi tất cả các giá trị nguyên 8 bit sRGB thành số thập phân 0,0-1,0

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

Bước hai:

Chuyển đổi một gamma được mã hóa RGB thành giá trị tuyến tính. Chẳng hạn, sRGB (tiêu chuẩn máy tính) yêu cầu đường cong công suất xấp xỉ V ^ 2.2, mặc dù biến đổi "chính xác" là:

sRGB thành tuyến tính

Trong đó Vạc là kênh R, G hoặc B được mã hóa gamma của sRGB.
Mã giả:

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

Bước thứ ba:

Để tìm Độ chói (Y), hãy áp dụng các hệ số tiêu chuẩn cho sRGB:

Áp dụng các hệ số Y = R * 0.2126 + G * 0.7152 + B * 0.0722

Mã giả sử dụng các chức năng trên:

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

ĐỂ TÌM KIẾM ÁNH SÁNG HOÀN HẢO:

Bước bốn:

Lấy độ chói Y từ trên xuống và biến đổi thành L *

L * từ phương trình Y
Mã giả:

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 * là một giá trị từ 0 (đen) đến 100 (trắng) trong đó 50 là "màu xám trung bình" nhận thức. L * = 50 tương đương với Y = 18,4, hay nói cách khác, thẻ màu xám 18%, đại diện cho phần giữa của một bức ảnh phơi sáng (Ansel Adams khu V).

Người giới thiệu:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
Câu hỏi thường gặp về Gamma của Charles Poynton


@Rotem cảm ơn bạn - Tôi đã thấy một số tuyên bố kỳ lạ và không đầy đủ và cảm thấy nó sẽ hữu ích để đóng nó xuống, đặc biệt khi chủ đề này vẫn được xếp hạng cao trên các công cụ tìm kiếm.
Myndex

Tôi đã tạo một bản trình diễn so sánh BT.601 LumaCIE 1976 L * Perceptionual Grey , sử dụng một số lệnh MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem

@Myndex Tôi đã sử dụng các công thức của bạn để truy cập L *, nhưng tôi vẫn nhận được một số kết quả lạ, dù tôi sử dụng công thức nào ... Với bạn, L * của # d05858 tối hơn L * của # c51c2a ... Có gì không làm thế nào để có được điều này phải không? Tại sao không có công thức làm việc như mong đợi? :(
Malaysia

1
@asdfasdfads Có, L*a*b*không tính đến một số thuộc tính tâm sinh lý. Hiệu ứng Helmholtz-Kohlrausch là một, nhưng có nhiều hiệu ứng khác. CIELAB không phải là một mô hình đánh giá hình ảnh "đầy đủ" bằng bất kỳ phương tiện nào. Trong bài viết của mình, tôi đã cố gắng bao quát các khái niệm cơ bản một cách hoàn toàn nhất có thể mà không mạo hiểm vào những chi tiết rất sâu. Mô hình Hunt, mô hình của Fairchild và các mô hình khác làm một công việc hoàn thiện hơn, nhưng cũng phức tạp hơn nhiều.
Myndex

1
@Myndex, nevermind, việc triển khai của tôi dựa trên sự mệt mỏi và kết quả kém cỏi của tôi đến từ đó :( Cảm ơn bạn rất nhiều vì sự giúp đỡ của bạn và bài đăng của bạn có giá trị lớn!
sjahan

11

Tôi tìm thấy mã này (được viết bằng C #) thực hiện công việc tuyệt vời để tính "độ sáng" của màu. Trong kịch bản này, mã đang cố gắng xác định xem nên đặt văn bản trắng hoặc đen trên màu.


1
Đó chính xác là những gì tôi cần. Tôi đang thực hiện một bản demo "thanh màu" cổ điển và muốn gắn nhãn chúng lên trên cùng với lựa chọn màu đen hoặc trắng tốt nhất!
RufusVS

10

Thật thú vị, công thức này cho RGB => HSV chỉ sử dụng v = MAX3 (r, g, b). Nói cách khác, bạn có thể sử dụng tối đa (r, g, b) làm V trong HSV.

Tôi đã kiểm tra và trên trang 575 của Hearn & Baker đây là cách họ tính toán "Giá trị".

Từ Hearn & Baker pg 319


Chỉ để ghi lại liên kết đã chết, lưu trữ phiên bản tại đây - web.archive.org/web/20150906055359/http://th
Peter

HSV không đồng nhất về mặt nhận thức (và thậm chí nó không đóng). Nó chỉ được sử dụng như một cách "thuận tiện" để điều chỉnh màu sắc, nhưng nó không liên quan đến nhận thức và V không liên quan đến giá trị thực của L hoặc Y (CIE Luminance).
Myndex

9

Thay vì bị lạc giữa lựa chọn ngẫu nhiên các công thức được đề cập ở đây, tôi khuyên bạn nên tìm công thức được đề xuất theo tiêu chuẩn W3C.

Dưới đây là một đơn giản nhưng chính xác PHP thi hành WCAG 2.0 SC 1.4.3 tương đối sángđộ tương phản công thức. Nó tạo ra các giá trị phù hợp để đánh giá các tỷ lệ cần thiết cho việc tuân thủ WCAG, như trên trang này , và như vậy là phù hợp và phù hợp cho bất kỳ ứng dụng web nào. Điều này là tầm thường để chuyển sang các ngôn ngữ khác.

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

Tại sao bạn thích định nghĩa w3c? cá nhân tôi đã triển khai cả CCIR 601 và w3c được đề xuất và tôi hài lòng hơn với kết quả CCIR 601
user151496 15/03/14

1
Bởi vì, như tôi đã nói, nó được cả W3C và WCAG khuyên dùng?
Synchro

1
Công thức W3C không chính xác trên một số cấp độ. Không tính đến nhận thức của con người, họ đang sử dụng độ tương phản "đơn giản" bằng cách sử dụng độ chói là tuyến tính và hoàn toàn không đồng nhất về mặt nhận thức. Trong số những thứ khác, có vẻ như họ dựa trên một số tiêu chuẩn cũ như năm 1988 (!!!) không liên quan ngày nay (những tiêu chuẩn này dựa trên màn hình đơn sắc như xanh / đen và được gọi là tương phản hoàn toàn từ tắt sang tắt , không xem xét màu xám cũng không màu sắc).
Myndex

1
Đó là rác hoàn chỉnh. Luma đặc biệt nhận thức - đó là lý do tại sao nó có các hệ số khác nhau cho màu đỏ, xanh lá cây và xanh dương. Tuổi không có gì để làm với nó - không gian màu sắc cảm nhận CIE Lab xuất sắc có từ năm 1976. Không gian W3C không tốt bằng, tuy nhiên đó là một xấp xỉ thực tế tốt, dễ tính toán. Nếu bạn có một cái gì đó mang tính xây dựng để cung cấp, hãy đăng nó thay vì những lời chỉ trích trống rỗng.
Synchro

3
Chỉ cần thêm / cập nhật : chúng tôi hiện đang nghiên cứu các thuật toán thay thế mô hình tương phản nhận thức tốt hơn (thảo luận trong Github số 695) . Tuy nhiên, như một vấn đề riêng FYI, ngưỡng cho sRGB là 0,04045 chứ không phải 0,03928 được tham chiếu từ một dự thảo sRGB sớm lỗi thời. Tiêu chuẩn IEC có thẩm quyền sử dụng 0,04045 và sắp có yêu cầu kéo để sửa lỗi này trong WCAG. (ref: IEC 61966-2-1: 1999) Đây là vấn đề Github 360, mặc dù vậy, trong 8bit không có sự khác biệt thực tế - gần cuối chuỗi 360 Tôi có các biểu đồ lỗi bao gồm 0,04045 / 0,03928 trong 8 bit.
Myndex

8

Để thêm những gì tất cả những người khác nói:

Tất cả các phương trình này hoạt động tốt trong thực tế, nhưng nếu bạn cần phải rất chính xác, trước tiên bạn phải chuyển đổi màu thành không gian màu tuyến tính (áp dụng hình ảnh nghịch đảo-gamma), làm trung bình trọng lượng của các màu chính và - nếu bạn muốn hiển thị màu sắc - đưa độ chói trở lại vào gamma màn hình.

Sự khác biệt về độ chói giữa gamma ăn sâu và làm gamma thích hợp lên tới 20% trong các màu xám tối.


2

Tôi đã giải quyết một nhiệm vụ tương tự ngày hôm nay trong javascript. Tôi đã giải quyết getPerceivedLightness(rgb)chức năng này cho màu HEX RGB. Nó liên quan đến hiệu ứng Helmholtz-Kohlrausch thông qua công thức Fairchild và Perrotta để điều chỉnh độ chói.

/**
 * 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

Không gian màu HSV nên thực hiện thủ thuật, xem bài viết trên wikipedia tùy thuộc vào ngôn ngữ bạn đang làm việc trong bạn có thể nhận được chuyển đổi thư viện.

H là màu sắc có giá trị bằng số cho màu (ví dụ: đỏ, xanh lục ...)

S là độ bão hòa của màu, nghĩa là độ đậm của nó

V là 'độ sáng' của màu.


7
Vấn đề với không gian màu HSV là bạn có thể có cùng độ bão hòa và giá trị, nhưng màu sắc khác nhau, cho màu xanhmàu vàng . Màu vàng là nhiều sáng hơn màu xanh. HSL cũng vậy.
Ian Boyd

hsv cung cấp cho bạn "độ sáng" của màu theo nghĩa kỹ thuật. trong độ sáng tri giác
hsv

HSV và HSL không chính xác về mặt nhận thức (và nó thậm chí không gần gũi). Chúng rất hữu ích cho "điều khiển" để điều chỉnh màu tương đối, nhưng không phải là dự đoán chính xác về độ sáng cảm nhận. Sử dụng L * từ CIELAB cho độ sáng cảm nhận.
Myndex

1

Giá trị độ chói RGB = 0,3 R + 0,59 G + 0,11 B

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

Nếu bạn đang tìm kiếm mức độ gần với màu trắng, bạn có thể sử dụng Khoảng cách Euclide từ (255, 255, 255)

Tôi nghĩ rằng không gian màu RGB không đồng nhất về khoảng cách điện tử L2. Không gian thống nhất bao gồm CIE LAB và LUV.


1

Công thức nghịch đảo gamma của Jive Dadson cần phải loại bỏ một nửa điều chỉnh khi được triển khai trong Javascript, tức là trả về từ hàm gam_sRGB cần phải được trả về int (v * 255); không trả về int (v * 255 + .5); Làm tròn một nửa điều chỉnh lên và điều này có thể khiến giá trị một quá cao trên bộ ba R = G = B tức là màu xám. Chuyển đổi màu xám trên bộ ba R = G = B sẽ tạo ra giá trị bằng R; đó là một bằng chứng cho thấy công thức hợp lệ. Xem Nine Shades of Greyscale để biết công thức hoạt động (không có nửa điều chỉnh).


Có vẻ như bạn biết công cụ của mình, vì vậy tôi đã xóa +0,5
Jive Dadson

Tôi đã làm thí nghiệm. Trong C ++, nó cần +0,5, vì vậy tôi đưa nó trở lại. Tôi đã thêm một nhận xét về việc dịch sang các ngôn ngữ khác.
Jive Dadson

1

Tôi tự hỏi làm thế nào những hệ số rgb đã được xác định. Tôi đã tự mình làm một thí nghiệm và tôi đã kết thúc như sau:

Y = 0.267 R + 0.642 G + 0.091 B

Gần nhưng rõ ràng khác với các hệ số ITU được thành lập từ lâu. Tôi tự hỏi liệu các hệ số đó có thể khác nhau đối với mỗi người quan sát hay không, bởi vì tất cả chúng ta có thể có một lượng hình nón và hình que khác nhau trên võng mạc trong mắt chúng ta, và đặc biệt là tỷ lệ giữa các loại hình nón khác nhau có thể khác nhau.

Để tham khảo:

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

Tôi đã thực hiện thử nghiệm bằng cách nhanh chóng di chuyển một thanh màu xám nhỏ trên nền đỏ tươi, xanh lục và xanh lam sáng, và điều chỉnh màu xám cho đến khi nó hòa trộn càng nhiều càng tốt. Tôi cũng lặp lại bài kiểm tra đó với các sắc thái khác. Tôi đã lặp lại thử nghiệm trên các màn hình khác nhau, thậm chí một màn hình có hệ số gamma cố định là 3.0, nhưng tất cả đều giống tôi. Hơn nữa, các hệ số ITU theo nghĩa đen là sai đối với mắt tôi.

Và vâng, tôi có lẽ có một tầm nhìn màu bình thường.


Trong các thí nghiệm của bạn, bạn đã tuyến tính hóa để loại bỏ thành phần gamma đầu tiên? Nếu bạn không thể giải thích kết quả của bạn. NHƯNG CSONG, các hệ số có liên quan đến các thí nghiệm CIE 1931 và đó là trung bình của 17 quan sát viên, do đó, có kết quả phương sai riêng lẻ.
Myndex

1

Đây là một chút mã C cần tính toán chính xác độ chói.

// 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

Hãy xác định độ sáng. Nếu bạn đang tìm kiếm mức độ gần với màu trắng, bạn có thể sử dụng Khoảng cách Euclide từ (255, 255, 255)


1
Không, bạn không thể sử dụng khoảng cách eidianidian giữa các giá trị sRGB, sRGB không phải là không gian Cartesian / vector đồng nhất về mặt nhận thức. Nếu bạn muốn sử dụng khoảng cách Euclidian làm thước đo độ chênh lệch màu, ít nhất bạn cần chuyển đổi sang CIELAB hoặc tốt hơn nữa, sử dụng CAM như CIECAM02.
Myndex

0

'V' của HSV có lẽ là thứ bạn đang tìm kiếm. MATLAB có chức năng rgb2hsv và bài viết wikipedia được trích dẫn trước đây có đầy đủ mã giả. Nếu chuyển đổi RGB2HSV không khả thi, một mô hình kém chính xác hơn sẽ là phiên bản thang độ xám của hình ảnh.


0

Liên kết này giải thích mọi thứ sâu sắc, bao gồm cả lý do tại sao các hằng số nhân đó tồn tại trước các giá trị R, G và B.

Chỉnh sửa: Nó cũng có lời giải thích cho một trong những câu trả lời ở đây (0.299 * R + 0.587 * G + 0.114 * B)


0

Để xác định độ sáng của màu với R, tôi chuyển đổi màu hệ thống RGB thành màu hệ thống HSV.

Trong kịch bản của tôi, tôi sử dụng mã hệ thống HEX trước đây vì lý do khác, nhưng bạn cũng có thể bắt đầu với mã hệ thống RGB với rgb2hsv {grDevices}. Các tài liệu ở đây .

Đây là một phần của mã của tôi:

 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

Để rõ ràng, các công thức sử dụng một căn bậc hai cần phải được

sqrt(coefficient * (colour_value^2))

không phải

sqrt((coefficient * colour_value))^2

Bằng chứng về điều này nằm ở việc chuyển đổi bộ ba R = G = B sang thang độ xám R. Điều đó sẽ chỉ đúng nếu bạn bình phương giá trị màu, chứ không phải hệ số lần giá trị màu. Xem Nine Shades of Greyscale


5
có những dấu ngoặc đơn
log0

trừ khi hệ số bạn sử dụng là căn bậc hai của hệ số chính xác.
RufusVS
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.