สมมติว่าคุณกำลังทำงานกับสี RGB: แต่ละสีจะแสดงด้วยความเข้มหรือความสว่างสามระดับ คุณต้องเลือกระหว่าง "linear RGB" และ "sRGB" สำหรับตอนนี้เราจะลดความซับซ้อนของสิ่งต่าง ๆ โดยมองข้ามความเข้มที่แตกต่างกันสามระดับและสมมติว่าคุณมีความเข้มเดียวนั่นคือคุณกำลังจัดการกับเฉดสีเทา
ในปริภูมิสีเชิงเส้นความสัมพันธ์ระหว่างตัวเลขที่คุณจัดเก็บและความเข้มที่แสดงเป็นเส้นตรง ในทางปฏิบัติหมายความว่าถ้าคุณเพิ่มจำนวนเป็นสองเท่าคุณจะเพิ่มความเข้มเป็นสองเท่า (ความสว่างของสีเทา) หากคุณต้องการเพิ่มความเข้มสองค่าเข้าด้วยกัน (เนื่องจากคุณกำลังคำนวณความเข้มจากการมีส่วนร่วมของแหล่งกำเนิดแสงสองแหล่งหรือเนื่องจากคุณกำลังเพิ่มวัตถุโปร่งใสที่ด้านบนของวัตถุทึบแสง) คุณสามารถทำได้โดยเพิ่ม สองตัวเลขด้วยกัน หากคุณกำลังทำการผสม 2D หรือแรเงา 3D หรือการประมวลผลภาพเกือบทุกประเภทคุณต้องการความเข้มของคุณในพื้นที่สีเชิงเส้นคุณจึงสามารถบวกลบคูณหารตัวเลขเพื่อให้มีผลเหมือนกันกับความเข้ม อัลกอริธึมการประมวลผลและการแสดงสีส่วนใหญ่จะให้ผลลัพธ์ที่ถูกต้องกับ RGB เชิงเส้นเท่านั้นเว้นแต่คุณจะเพิ่มน้ำหนักพิเศษให้กับทุกอย่าง
ฟังดูง่าย แต่มีปัญหา ความไวต่อแสงของดวงตามนุษย์นั้นละเอียดกว่าที่ความเข้มต่ำกว่าความเข้มสูง กล่าวคือถ้าคุณสร้างรายการความเข้มทั้งหมดที่คุณสามารถแยกแยะได้มีความมืดมากกว่าความสว่าง หากต้องการกล่าวอีกนัยหนึ่งคุณสามารถแยกเฉดสีเทาเข้มออกจากกันได้ดีกว่าที่คุณทำได้ด้วยเฉดสีเทาอ่อน โดยเฉพาะอย่างยิ่งถ้าคุณใช้ 8 บิตเพื่อแสดงความเข้มของคุณและคุณทำสิ่งนี้ในพื้นที่สีเชิงเส้นคุณจะได้เฉดสีอ่อนมากเกินไปและมีเฉดสีเข้มไม่เพียงพอ คุณได้รับแถบในบริเวณที่มืดของคุณในขณะที่อยู่ในบริเวณที่มีแสงของคุณคุณกำลังเสียบิตไปกับเฉดสีใกล้ขาวที่แตกต่างกันซึ่งผู้ใช้ไม่สามารถแยกออกจากกันได้
เพื่อหลีกเลี่ยงปัญหานี้และทำให้การใช้งานที่ดีที่สุดของผู้ที่ 8 บิตเรามักจะใช้sRGB มาตรฐาน sRGB บอกเส้นโค้งที่จะใช้เพื่อให้สีของคุณไม่เป็นเส้นตรง เส้นโค้งจะตื้นกว่าที่ด้านล่างคุณจึงมีสีเทาเข้มได้มากกว่าและด้านบนชันกว่าคุณจึงมีสีเทาอ่อนน้อยลง หากคุณเพิ่มจำนวนขึ้นเป็นสองเท่าคุณจะมีความเข้มมากกว่าสองเท่า ซึ่งหมายความว่าหากคุณเพิ่มสี sRGB เข้าด้วยกันคุณจะได้ผลลัพธ์ที่เบากว่าที่ควรจะเป็น ทุกวันนี้จอภาพส่วนใหญ่ตีความสีที่ป้อนเป็น sRGB ดังนั้นเมื่อคุณใส่สีบนหน้าจอหรือเก็บไว้ในพื้นผิว 8 บิตต่อช่องให้จัดเก็บเป็น sRGBดังนั้นคุณจึงใช้ประโยชน์จาก 8 บิตเหล่านั้นให้เกิดประโยชน์สูงสุด
คุณจะสังเกตเห็นว่าตอนนี้เรามีปัญหา: เราต้องการให้สีของเราประมวลผลในพื้นที่เชิงเส้น แต่เก็บไว้ใน sRGB ซึ่งหมายความว่าคุณจะทำการแปลง sRGB เป็นเชิงเส้นในการอ่านและการแปลง linear-to-sRGB ในการเขียน ดังที่เราได้กล่าวไปแล้วว่าความเข้มเชิงเส้น 8 บิตไม่มีความมืดเพียงพอสิ่งนี้จะทำให้เกิดปัญหาดังนั้นจึงมีกฎที่ใช้ได้จริงอีกข้อหนึ่งคืออย่าใช้สีเชิงเส้น 8 บิตหากคุณสามารถหลีกเลี่ยงได้ มันกลายเป็นเรื่องธรรมดาที่จะปฏิบัติตามกฎที่ว่าสี 8 บิตจะเป็น sRGB เสมอดังนั้นคุณจึงทำการแปลง sRGB เป็นเชิงเส้นในเวลาเดียวกันกับการขยายความเข้มจาก 8 เป็น 16 บิตหรือจากจำนวนเต็มเป็นทศนิยม ในทำนองเดียวกันเมื่อคุณประมวลผลทศนิยมเสร็จแล้วคุณจะ จำกัด ให้แคบลงเหลือ 8 บิตในเวลาเดียวกันกับการแปลงเป็น sRGB หากคุณปฏิบัติตามกฎเหล่านี้
เมื่อคุณอ่านภาพ sRGB และคุณต้องการความเข้มเชิงเส้นให้ใช้สูตรนี้กับแต่ละความเข้ม:
float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);
ในทางกลับกันเมื่อคุณต้องการเขียนภาพเป็น sRGB ให้ใช้สูตรนี้กับความเข้มเชิงเส้นแต่ละเส้น:
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 )
ในทั้งสองกรณีค่าทศนิยมจะอยู่ในช่วง 0 ถึง 1 ดังนั้นหากคุณกำลังอ่านจำนวนเต็ม 8 บิตคุณต้องหารด้วย 255 ก่อนและหากคุณกำลังเขียนจำนวนเต็ม 8 บิตคุณต้องการคูณด้วย 255 สุดท้ายแบบเดียวกับที่คุณทำ นั่นคือทั้งหมดที่คุณต้องรู้เพื่อทำงานกับ sRGB
ถึงตอนนี้ฉันจัดการกับความเข้มเดียวเท่านั้น แต่มีสิ่งที่ฉลาดกว่าที่เกี่ยวข้องกับสี ดวงตาของมนุษย์สามารถบอกความสว่างที่แตกต่างกันได้ดีกว่าโทนสีต่างๆ (ในทางเทคนิคแล้วจะมีความละเอียดของการส่องสว่างที่ดีกว่าโครเมียม) ดังนั้นคุณจึงสามารถใช้ประโยชน์จาก 24 บิตของคุณได้ดียิ่งขึ้นโดยการจัดเก็บความสว่างแยกต่างหากจากโทนสี นี่คือสิ่งที่ตัวแทนของ YUV, YCrCb และอื่น ๆ พยายามทำ ช่อง Y คือความสว่างโดยรวมของสีและใช้บิตมากกว่า (หรือมีความละเอียดเชิงพื้นที่มากกว่า) มากกว่าอีกสองช่อง ด้วยวิธีนี้คุณไม่จำเป็นต้องใช้เส้นโค้งเช่นเดียวกับที่คุณทำกับความเข้ม RGB YUV เป็นช่องว่างสีเชิงเส้นดังนั้นหากคุณเพิ่มจำนวนเป็นสองเท่าในช่อง Y คุณจะมีความสว่างของสีเป็นสองเท่า แต่คุณไม่สามารถเพิ่มหรือคูณสี YUV เข้าด้วยกันได้เหมือนที่คุณทำได้ด้วยสี RGB ดังนั้น
ฉันคิดว่าตอบคำถามของคุณได้ดังนั้นฉันจะปิดท้ายด้วยบันทึกประวัติศาสตร์สั้น ๆ ก่อนหน้า sRGB CRT แบบเก่าเคยมีความไม่เป็นเชิงเส้นในตัว หากคุณเพิ่มแรงดันไฟฟ้าเป็นสองเท่าสำหรับพิกเซลคุณจะมีความเข้มมากกว่าสองเท่า วิธีการมากขึ้นแตกต่างกันสำหรับแต่ละตรวจสอบและพารามิเตอร์นี้ถูกเรียกว่ารังสีแกมมา พฤติกรรมนี้มีประโยชน์เพราะหมายความว่าคุณอาจมืดมากกว่าแสง แต่ก็หมายความว่าคุณไม่สามารถบอกได้ว่าสีของคุณจะสว่างแค่ไหนใน CRT ของผู้ใช้เว้นแต่คุณจะปรับเทียบก่อน การแก้ไขแกมมาหมายถึงการเปลี่ยนสีที่คุณเริ่มต้นด้วย (อาจเป็นเชิงเส้น) และเปลี่ยนเป็นแกมมาของ CRT ของผู้ใช้ OpenGL มาจากยุคนี้ซึ่งเป็นสาเหตุที่ทำให้พฤติกรรม sRGB บางครั้งสับสนเล็กน้อย แต่ตอนนี้ผู้จำหน่าย GPU มักจะทำงานร่วมกับรูปแบบที่ฉันอธิบายไว้ข้างต้นนั่นคือเมื่อคุณจัดเก็บความเข้ม 8 บิตในพื้นผิวหรือเฟรมบัฟเฟอร์จะเป็น sRGB และเมื่อคุณประมวลผลสีจะเป็นแบบเส้นตรง ตัวอย่างเช่น OpenGL ES 3.0 แต่ละเฟรมบัฟเฟอร์และพื้นผิวมี "แฟล็ก sRGB" ที่คุณสามารถเปิดเพื่อเปิดใช้งานการแปลงอัตโนมัติเมื่ออ่านและเขียน คุณไม่จำเป็นต้องทำการแปลง sRGB หรือแก้ไขแกมม่าอย่างชัดเจนเลย