Sự khác biệt thực tế khi làm việc với màu sắc trong không gian RGB tuyến tính và không tuyến tính là gì?


88

Thuộc tính cơ bản của không gian RGB tuyến tính là gì và thuộc tính cơ bản của không gian phi tuyến tính là gì? Khi nói về các giá trị bên trong mỗi kênh trong 8 (hoặc nhiều hơn) bit đó, điều gì sẽ thay đổi?

Trong OpenGL, màu sắc là giá trị 3 + 1 và với điều này, tôi có nghĩa là RGB + alpha, với 8 bit dành riêng cho mỗi kênh và đây là phần mà tôi hiểu rõ ràng.

Nhưng khi nói đến hiệu chỉnh gamma, tôi không hiểu tác dụng của việc làm việc trong không gian RGB phi tuyến tính là gì.

Vì tôi biết cách sử dụng một đường cong trong phần mềm đồ họa để chỉnh sửa ảnh, nên lời giải thích của tôi là trong không gian RGB tuyến tính, bạn lấy các giá trị như chúng vốn có, không cần thao tác và không có hàm toán học nào kèm theo, thay vào đó, mỗi kênh thường phát triển theo một hành vi chức năng quyền lực cổ điển.

Ngay cả khi tôi coi lời giải thích này là giải thích thực, tôi vẫn không hiểu được không gian tuyến tính thực là gì, bởi vì sau khi tính toán, tất cả các không gian RGB phi tuyến tính trở thành tuyến tính và quan trọng nhất là tôi không hiểu được phần mà không -không gian màu tuyến tính phù hợp hơn với mắt người vì cuối cùng tất cả các không gian RGB đều tuyến tính đối với những gì tôi hiểu.


Trên một lưu ý thực tế, bạn có thể chỉ định một trong hai tuyến tính hoặc RGB tiêu chuẩn như không gian màu của bạn trong SVG và tôi không có ý tưởng những gì hiệu quả là, ngoài việc thực tế rằng đây có vẻ là quan trọng :-)
Michael Mullany

@MichaelMull nhiều thứ tuyến tính này trông giống như một gợi ý cho người dùng hơn là một chất lượng đặc biệt thực sự.
Ken

Điều gì đó đã giúp tôi: Tôi nghĩ đó là một giáo viên toán đã nói với tôi "dòng suy nghĩ khi bạn nghe thấy tuyến tính". Không chắc liệu điều này có giúp ích gì không, nhưng đối với tôi, đó là một câu "Ồ đúng rồi!" khoảnh khắc
Joe Plante

Câu trả lời:


229

Giả sử bạn đang làm việc với màu RGB: mỗi màu được biểu thị bằng ba cường độ hoặc độ sáng. Bạn phải chọn giữa "RGB tuyến tính" và "sRGB". Hiện tại, chúng tôi sẽ đơn giản hóa mọi thứ bằng cách bỏ qua ba cường độ khác nhau và giả sử bạn chỉ có một cường độ: nghĩa là, bạn chỉ xử lý các sắc thái của màu xám.

Trong không gian màu tuyến tính, mối quan hệ giữa các số bạn lưu trữ và cường độ mà chúng đại diện là tuyến tính. Thực tế, điều này có nghĩa là nếu bạn tăng gấp đôi con số, bạn sẽ tăng gấp đôi cường độ (độ đậm nhạt của màu xám). Nếu bạn muốn thêm hai cường độ lại với nhau (vì bạn đang tính toán cường độ dựa trên sự đóng góp của hai nguồn sáng hoặc vì bạn đang thêm một vật thể trong suốt lên trên một vật thể mờ đục), bạn có thể thực hiện việc này bằng cách thêm hai số với nhau. Nếu bạn đang thực hiện bất kỳ loại pha trộn 2D hoặc đổ bóng 3D nào hoặc hầu như bất kỳ xử lý hình ảnh nào, thì bạn muốn các cường độ của mình trong không gian màu tuyến tính, vì vậy bạn có thể chỉ cần cộng, trừ, nhân và chia các số để có cùng tác động đến các cường độ. Hầu hết các thuật toán xử lý và kết xuất màu chỉ cho kết quả chính xác với RGB tuyến tính, trừ khi bạn thêm trọng số bổ sung cho mọi thứ.

Điều đó nghe thực sự dễ dàng, nhưng có một vấn đề. Độ nhạy của mắt người đối với ánh sáng ở cường độ thấp tốt hơn ở cường độ cao. Có nghĩa là, nếu bạn lập danh sách tất cả các cường độ mà bạn có thể phân biệt, sẽ có nhiều cường độ tối hơn cường độ sáng. Nói một cách khác, bạn có thể phân biệt các sắc độ xám đậm hơn so với các sắc độ xám nhạt. Đặc biệt, nếu bạn đang sử dụng 8 bit để thể hiện cường độ của mình và bạn làm điều này trong không gian màu tuyến tính, bạn sẽ có quá nhiều sắc thái sáng và không đủ sắc thái tối. Bạn nhận được dải trong các vùng tối của mình, trong khi ở các vùng sáng, bạn đang lãng phí các sắc thái gần trắng khác nhau mà người dùng không thể phân biệt được.

Để tránh vấn đề này và sử dụng tốt nhất 8 bit đó, chúng tôi có xu hướng sử dụng sRGB . Tiêu chuẩn sRGB cho bạn biết một đường cong để sử dụng, để làm cho màu sắc của bạn trở nên phi tuyến tính. Đường cong nông hơn ở phía dưới, vì vậy bạn có thể có nhiều màu xám đậm hơn và dốc hơn ở phía trên, do đó bạn có ít màu xám nhạt hơn. Nếu bạn tăng gấp đôi số lượng, bạn sẽ tăng gấp đôi cường độ. Điều này có nghĩa là nếu bạn thêm các màu sRGB với nhau, bạn sẽ có kết quả nhạt hơn so với mức cần thiết. Ngày nay, hầu hết các màn hình diễn giải màu đầu vào của chúng là sRGB. Vì vậy, khi bạn đặt màu trên màn hình hoặc lưu trữ nó trong kết cấu 8 bit mỗi kênh, hãy lưu trữ nó dưới dạng sRGB , để bạn sử dụng tốt nhất 8 bit đó.

Bạn sẽ nhận thấy hiện tại chúng tôi có một vấn đề: chúng tôi muốn màu sắc của chúng tôi được xử lý trong không gian tuyến tính, nhưng được lưu trữ trong sRGB. Điều này có nghĩa là bạn sẽ thực hiện chuyển đổi sRGB sang tuyến tính khi đọc và chuyển đổi tuyến tính thành sRGB khi ghi. Như chúng ta đã nói rằng cường độ 8 bit tuyến tính không có đủ tối, điều này sẽ gây ra vấn đề, vì vậy có một quy tắc thực tế hơn: không sử dụng màu tuyến tính 8 bit nếu bạn có thể tránh nó. Việc tuân theo quy tắc màu 8-bit luôn là sRGB đã trở nên thông thường, vì vậy bạn thực hiện chuyển đổi từ sRGB sang tuyến tính cùng lúc với việc mở rộng cường độ từ 8 lên 16 bit hoặc từ số nguyên sang dấu phẩy động; tương tự như vậy, khi bạn hoàn thành quá trình xử lý dấu phẩy động, bạn thu hẹp còn 8 bit cùng lúc khi chuyển đổi sang sRGB. Nếu bạn tuân theo các quy tắc này,

Khi bạn đang đọc một hình ảnh sRGB và bạn muốn có cường độ tuyến tính, hãy áp dụng công thức này cho từng cường độ:

float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);

Theo cách khác, khi bạn muốn viết một hình ảnh dưới dạng sRGB, hãy áp dụng công thức này cho từng cường độ tuyến tính:

float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )

Trong cả hai trường hợp, giá trị dấu phẩy động s nằm trong khoảng từ 0 đến 1, vì vậy nếu bạn đang đọc số nguyên 8 bit, bạn muốn chia cho 255 trước và nếu bạn đang viết số nguyên 8 bit, bạn muốn nhân với 255 cuối cùng, giống như cách bạn thường làm. Đó là tất cả những gì bạn cần biết để làm việc với sRGB.

Cho đến nay, tôi chỉ xử lý một cường độ, nhưng có những thứ khôn ngoan hơn liên quan đến màu sắc. Mắt người có thể phân biệt các độ sáng khác nhau tốt hơn các sắc thái khác nhau (về mặt kỹ thuật, nó có độ phân giải độ sáng tốt hơn độ chói), vì vậy bạn có thể sử dụng tốt hơn 24 bit của mình bằng cách lưu trữ độ sáng riêng biệt với sắc thái màu. Đây là những gì các đại diện YUV, YCrCb, v.v. cố gắng làm. Kênh Y là độ đậm nhạt tổng thể của màu và sử dụng nhiều bit hơn (hoặc có độ phân giải không gian cao hơn) so với hai kênh còn lại. Bằng cách này, bạn không (luôn luôn) cần phải áp dụng một đường cong giống như bạn làm với cường độ RGB. YUV là một không gian màu tuyến tính, vì vậy nếu bạn nhân đôi số trong kênh Y, bạn sẽ tăng gấp đôi độ đậm nhạt của màu, nhưng bạn không thể thêm hoặc nhân các màu YUV với nhau như bạn có thể làm với màu RGB, vì vậy '

Tôi nghĩ rằng điều đó trả lời câu hỏi của bạn, vì vậy tôi sẽ kết thúc bằng một ghi chú lịch sử nhanh chóng. Trước sRGB, các CRT cũ đã từng được tích hợp sẵn tính năng phi tuyến tính. Nếu bạn tăng gấp đôi điện áp cho một pixel, bạn sẽ tăng gấp đôi cường độ. Mỗi màn hình khác nhau thêm bao nhiêu và thông số này được gọi là gamma . Hành vi này hữu ích vì nó có nghĩa là bạn có thể nhận được nhiều bóng tối hơn ánh sáng, nhưng cũng có nghĩa là bạn không thể biết màu sắc của mình sẽ sáng như thế nào trên CRT của người dùng, trừ khi bạn đã hiệu chỉnh nó trước. Hiệu chỉnh gammanghĩa là chuyển đổi các màu bạn bắt đầu (có thể là tuyến tính) và biến đổi chúng cho gamma của CRT của người dùng. OpenGL xuất phát từ thời đại này, đó là lý do tại sao hành vi sRGB của nó đôi khi hơi khó hiểu. Nhưng các nhà cung cấp GPU hiện có xu hướng làm việc với quy ước mà tôi đã mô tả ở trên: đó là khi bạn lưu trữ cường độ 8-bit trong kết cấu hoặc bộ đệm khung, đó là sRGB và khi bạn xử lý màu, đó là tuyến tính. Ví dụ: OpenGL ES 3.0, mỗi bộ đệm khung và kết cấu đều có "cờ sRGB" mà bạn có thể bật để bật chuyển đổi tự động khi đọc và ghi. Bạn không cần phải thực hiện chuyển đổi sRGB hoặc hiệu chỉnh gamma một cách rõ ràng.


6
câu trả lời tuyệt vời, cảm ơn bạn, luôn luôn tuyệt vời khi mọi thứ được giải thích, tôi chỉ yêu cầu một cuốn sách hoặc một tài nguyên mà theo ý kiến ​​của bạn là đủ tốt cho chủ đề này, không gian màu và công thức được sử dụng để thực hiện chuyển đổi sRGB <-> tuyến tính hoặc hàm nào có thể xấp xỉ hành vi này.
Ken

Tôi e rằng tôi không biết bất kỳ cuốn sách hay tài nguyên nào hay. Các trang Wikipedia là toàn diện, và bao gồm tất cả những thứ về điểm trắng và nhỏ như thế nào gam là (mà tôi đã không được đề cập, như hầu hết mọi người không cần phải biết về nó), nhưng mà làm cho nó một chút bất khả xâm phạm .
Dan Hulme



1

Tôi không phải là "chuyên gia phát hiện màu sắc của con người", nhưng tôi đã gặp điều tương tự trên chuyển đổi YUV-> RGB. Có các trọng số khác nhau cho các kênh R / G / B, vì vậy nếu bạn thay đổi màu nguồn theo x, các giá trị RGB sẽ thay đổi số lượng khác nhau.

Như đã nói, tôi không phải là một chuyên gia, tôi nghĩ, nếu bạn muốn thực hiện một số chuyển đổi màu sắc chính xác, bạn nên thực hiện nó trong không gian YUV, sau đó chuyển đổi nó thành RGB (hoặc thực hiện phép toán tương đương trên RGB, hãy cẩn thận mất dữ liệu). Ngoài ra, tôi không chắc rằng YUV là biểu thị màu gốc tốt nhất, nhưng máy quay video cung cấp định dạng đó, đó là nơi tôi đã gặp vấn đề.

Đây là công thức YUV-> RGB kỳ diệu với những con số bí mật được bao gồm: http://www.fourcc.org/fccyvrgb.php


1
Hãy cẩn thận về chuyển đổi RGB <-> YUV và ngược lại. Tôi không chắc điều này có đúng trong mọi trường hợp hay không, nhưng không gian màu YUV đôi khi chuyển đổi thành phạm vi 16-235 thay vì 0-255 trong 24-bit RGB. Vì vậy, bạn có thể mất dữ liệu mỗi khi thực hiện chuyển đổi không gian màu. Hầu hết mọi người có xu hướng nói rằng hãy giữ trong cùng một không gian màu nếu bạn có thể giúp được.
Joe Plante
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.