Correct SRGB Dithering

栏目: IT技术 · 发布时间: 4年前

内容简介:This is a brain-dump inspired by a thread on twit­ter about cor­rect™ dither inSo, this top­ic came up on twit­ter:I had pre­vi­ous­ly spent some time to wrap my head around this exact prob­lem, so I shot from the hip with some pseu­do code that I used in

This is a brain-dump inspired by a thread on twit­ter about cor­rect™ dither in sRGB , mean­ing, to choose the dither pat­tern in such a way as to pre­serve the phys­i­cal bright­ness of the orig­i­nal pix­els. This is in prin­ci­ple a solved prob­lem, but the dev­il is in the details that are eas­i­ly over­looked, espe­cial­ly when dither­ing to only a few quan­ti­za­tion lev­els.

So, this top­ic came up on twit­ter:

Correct SRGB Dithering

I had pre­vi­ous­ly spent some time to wrap my head around this exact prob­lem, so I shot from the hip with some pseu­do code that I used in Space Glid­er on Shader­toy. Code post­ings on twit­ter are nev­er a good idea, so here is a cleaned up ver­sion wrapped up in a prop­er func­tion:

vec3 oetf( vec3 );    // = pow( .4545 )
vec3 eotf( vec3 );    // = pow( 2.2 )
 
vec3 dither( vec3 linear_color, vec3 noise, float quant )
{
    vec3 c0 = floor( oetf( linear_color ) / quant ) * quant;
    vec3 c1 = c0 + quant;
    vec3 discr = mix( eotf( c0 ), eotf( c1 ), noise );
    return mix( c0, c1, lessThan( discr, linear_color ) );
}

Con­tents

How the code works

The linear_color is the val­ue that is going to be writ­ten out to the ren­der tar­get. This is the val­ue to be dithered and is sup­posed to be in lin­ear RGB . The noise argu­ment can be any uni­form noise in the range 0 to 1 (prefer­ably some form of blue noise, or it could be an ordered Bay­er-pat­tern). Last­ly, the quant argu­ment is the quan­ti­za­tion inter­val, which is “one over one minus the num­ber of lev­els”; for exam­ple: 1/255 for quan­ti­za­tion to 256 lev­els, or 51/255 so emu­late the palette of web-safe col­ors (6 lev­els). The free func­tion oetf is used here to stand for an arbi­trary opto-elec­tron­ic trans­fer func­tion , which for sRGB is noth­ing more than the good old gam­ma curve.

Here is how the dither func­tion works: It first com­putes the quan­tized low­er and upper bounds, c0 and c1 , that brack­et the input val­ue. The out­put is then select­ed as either c0 or c1 based on a com­par­i­son of the input against a dis­crim­i­nant, discr . The salient point is that this com­par­i­son is per­formed in lin­ear space!

Correct SRGB Dithering

So why is it nec­es­sary to com­pute the dis­crim­i­nant in lin­ear space? Because what mat­ters is phys­i­cal bright­ness, which is lin­ear in the num­ber of pix­els (at least it should be, on a sane dis­play), but it is not in gen­er­al lin­ear in the RGB val­ue ifself.

Why the code works

To illus­trate fur­ther lets con­tin­ue with the web palette exam­ple where there are 6 quan­ti­za­tion lev­els. The fol­low­ing table shows how these 6 lev­els should map to phys­i­cal lumi­nance, accord­ing to the sRGB-stan­dard:

val­ue

PERCENT

)

val­ue

(8 bit)

Val­ue

HEX

)

lumi­nance

(cD per sq meter)

exam­ple
0% 0 #00 0
20% 51 #33 3,31
40% 102 #66 13,3
60% 153 #99 31,9
80% 204 # CC 60,4
100% 255 # FF 80

The lumi­nance val­ues here were cal­cu­lat­ed by fol­low­ing the sRGB trans­fer func­tion and under the assump­tion of the stan­dard 80 cd/m² dis­play bright­ness. Now con­sid­er for exam­ple that we want to match the lumi­nance of the #33 grey val­ue (3,31 cd/m²) with a dither pat­tern. Accord­ing to table we should choose a 25% pat­tern when using the #66 pix­els (3,31 into 13,3), a 10% pat­tern for the #99 pix­els (3,31 into 31,9), a 5,4% pat­tern for the # CC pix­els (3,31 into 60,4) or a 4,1% pat­tern for the # FF pix­els (3,31 into 80). This has been real­ized in the fol­low­ing image:

All tiles in this image should appear approx­i­mate­ly with the same bright­ness. They may not match per­fect­ly on your dis­play, but they should do at least ok. Make sure the image is viewed at its orig­i­nal size. To min­i­mize resiz­ing errors I have includ­ed a 2× ver­sion for reti­na dis­plays that should get auto­mat­i­cal­ly select­ed on Mac­Books and the like.

In con­trast, using the raw RGB val­ue as the basis for the dither pat­tern as shown above does not pro­duce a match­ing appear­ance. In this case I used a 50% pat­tern with the #66 pix­els (20 into 40), a 33% pat­tern with #99 pix­els (20 into 60), a 25% pat­tern with # CC pix­els (20 into 80) and a 20% pat­tern with # FF pix­els (20 into 100). See for your­self how that does not match!

A real world example

As I said in the begin­ning, I came up with the above dither­ing code as a side effect of the con­tin­ued tin­ker­ing with Space Glid­er, as I want­ed to have a some­what faith­ful ren­di­tion of twi­light and night sit­u­a­tions, and that means that with­out dither­ing, the sky gra­di­ent would pro­duce very not­i­ca­ble band­ing, espe­cial­ly so in VR .

To illius­trate, a took a screen­shot of a twi­light scene, stand­ing in the moun­tains with the land­ing lights on. The dark­est pix­el in this image is #020204, which is some­where in the low­er left cor­ner. With a VR head­set on, and with the eyes dark-adapt­ed, the jumps between #02, #03 and #04 are clear­ly vis­i­ble and prop­er dither­ing is a must.

I will now show how the code shown in the begin­ning is work­ing as intend­ed by dither­ing this image to 2, 3, 4, 6 and 8 quan­ti­za­tion lev­els by sim­ply chanch­ing the quant vari­able. Again, all images should match phys­i­cal bright­ness impres­sion (and again, on the con­di­tion that your brows­er does not mess with the pix­els). The noise input used here is just the shader­toy builtin blue noise tex­ture, but 2 copies were added togeth­er at dif­fer­ent scales to make it effec­tive­ly one 16-bit noise tex­ture.

Conclusion

So that’s it as this is only a quick reac­tion post. To recap, the dither­ing prob­lem is com­pli­cat­ed by the fact that dis­play bright­ness in lin­ear in the num­ber of pix­els, but non-lin­ear in the RGB val­ue. Get­ting it right mat­ters for the low­est quan­ti­za­tion lev­els, be it either the dark parts of an image with many quan­ti­za­tion lev­els, or if there are only a few quan­ti­za­tion lev­els over­all.


以上所述就是小编给大家介绍的《Correct SRGB Dithering》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java遗传算法编程

Java遗传算法编程

Lee Jacobson、Burak Kanber / 王海鹏 / 人民邮电出版社 / 2016-12-6 / 49元

本书简单、直接地介绍了遗传算法,并且针对所讨论的示例问题,给出了Java代码的算法实现。全书共分灾6章。第1章简单介绍了人工智能和生物进化的知识背景,这也是遗传算法的历史知识背景。第2章给出了一个基本遗传算法的实现;第4章和第5章,分别针对机器人控制器、旅行商问题、排课问题展开分析和讨论,并给出了算法实现。在这些章的末尾,还给出了一些练习供读者深入学习和实践。第6章专门讨论了各种算法的优化问题。 ......一起来看看 《Java遗传算法编程》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具