色彩与混合
色彩空间
人眼有什么样的生理特征?
了解眼睛的生理特点可以帮助我们更好地掌控图形系统地设计。例如,眼睛受限于可查觉的最小亮度和最小分辨率。如果显示器像素可显示的亮度等级小于人眼可察觉的最小亮度差,或者其像素对人眼所张视角为眼睛最小角度分辨率二的1/10,那么该显示器的复杂性就超出了必要。
人类视觉系统有强大的并行处理能力,这使得用户能接受从计算机传来的大量信息。视觉系统一方面可容纳简陋或粗糙的数据,如简笔画,但另一方面又对特定瑕疵极为敏感,能在数百万像素中找到错误的那一个。
视觉系统可以很好地完成许多人物,例如确定物体地大小和方向而不论视点的位置及与物体距离的远近;在不同光照下识别同一颜色;即使有噪声和失真也能识别形状。但它执行有些任务时会比较差,例如判断明亮度的绝对值,识别平行线,察觉位置不相邻的相同颜色等。
对图形学来说,绘制或显示是否完美,其最终评判标准是人眼感知。在计算机视觉领域判断两幅图像的相似度有一种简单方法,即计算两幅图像中所有对应像素的像素值的差,求它们的平方和,再取平方根,这种方法称为方差和或L2距离。但显然,在图形学领域这个公式并不完美。
L
2
=
∑
i
=
1
n
∣
C
1
−
C
2
∣
2
L^2=\sqrt{\sum_{i=1}^n\mid C_1-C_2\mid ^2}
L2=i=1∑n∣C1−C2∣2
眼睛中的视觉细胞检测到光后触发视觉系统反应;粗略的说,到达视觉细胞的光每增强一倍,引发的刺激响应将增加固定的强度。如果光源B与光源A几何上完全相同,但看上去光源B的亮度只有光源A的一般,则光源B发出的实际能量大约只有光源A的18%。
功率为50%的灰色,人眼实际感知亮度为:
0.5
2.2
×
100
%
=
72.97
%
\sqrt[2.2]{0.5}\times 100\% = 72.97\%
2.20.5×100%=72.97%
而人眼认为的50%中灰色,实际功率为:
0.
5
2.2
×
100
%
=
21.76
%
0.5^{2.2}\times 100\%=21.76\%
0.52.2×100%=21.76%
这意味着视觉系统的局部适应性意味着亮度的变化往往比其绝对值更受关注,在比较两幅图像时,其对应像素亮度的比值比两者的差值更重要。所以在画面设计时如果能选择,应优先考虑图像的梯度而不是亮度的绝对值。图形学家为此提出了伽马校正这一概念:
V
o
u
t
=
A
V
i
n
γ
V_{out}=AV_{in}^\gamma
Vout=AVinγ
其中A为常量,通常为1,而γ分为解码伽马值和加码伽马值,前者通常为2.2,后者通常为-2.2。这样,人眼中的50%灰就可以被加码为0.5储存在数据结构中,而在显示器显示时又被解码为0.2176的实际物理功耗。
通过伽马变换,我们可以线性的考虑所有关于色彩变换的问题而不再需要考虑人眼对光的非线性感知。
令人惊讶的是,人眼可以辨识的白天最明亮的光照亮度和夜晚最暗的光照亮度之比可以超过1 000 000 : 1,但人眼仅可辨识局部区域内100 : 1的亮度变化。一个极端的例子是:人眼可以在晴朗的夜晚识别一颗3等星,与此同时清晰地看到月亮,而这两者之间地亮度差大约为1 000 000倍,但当月亮在视野中与3等星非常近时则几乎肯定看不到这颗3等星。
眼睛中的光感受器聚集于视野中心附近,这意味着人眼对周围景物的分辨率是偏低的。但同时,人眼的运动感受器反而聚集于视野边缘,这意味着人眼可能对视野中心的运动漫不经心,却对周边区域的变化十分敏感。
眼睛对边界具有敏感性,显示器必须具有足够多的亮度等级才能生成明显光滑的图像。
人脑十分依赖阴影判断物体的位置。画面上有无阴影很关键,但准确地绘制它则并不重要。
什么是RGB标准?
物理学中的颜色严格依照光的波长划分。光子具有固定的波长,所以将3M个550nm的光子和3M个750nm的光子混合,它们并不会混合成6M个650nm的光子。但人眼并不能准确的识别出3M个550nm的光子和3M个750nm的光子,对人眼来说,3M个550nm的光子和3M个750nm的光子的混合光束,与6M个650nm的光子组成的光束差别不大。
人眼只有三种视觉感知细胞,分别识别红色、绿色、蓝色,这也是计算机领域通用的三基色。三基色(红绿蓝)与三原色(红黄青)不同,三原色是通过减色法进行混合的,它模拟的是吸收不同波长光的物体混合后的结果,所以三原色的物体混合后将吸收所有可见波长的光,呈现黑色;而三基色模拟的是不同波长光混合后的结果,所以三基色混合后将呈现白色。
人眼对三基色的识别在光谱上依旧是非线性的:
图中,人眼将S曲线全部识别为蓝色,M曲线全部识别为绿色,L曲线全部识别为红色。
由于人眼识别RGB三个分量也是非对称的,所以计算机也必须适应人类的这一特征,在根据人类视觉计算物理特性时必须考虑到三个分量的比例,比如在计算明度和灰度时对三个分量必须使用不同的系数。
但人眼对颜色的感知却是线性的,人感知的颜色是蓝绿红三种视锥细胞的神经强度的线性叠加。这方便了显示器的制造,对显示器来说,对某个像素输入一个RGB参数,只需要线性的改变红绿蓝三个子像素的亮度即可。这三个子像素的波长一般是三种视觉细胞最敏感的波长,比如450nm、550nm和580nm的组合。注意,亮度在伽马校正阶段已经处理好了,所以显示器不需要考虑人眼识别亮度的非线性特性。
视锥细胞的感应光子的能力存在一个范围,也就是到达某个强度或者低于某个强度就超过了它的能力范围。因此,人眼视锥细胞的响应也存在饱和以及最低响应值:蓝、绿和红不可能无限的亮,人类的感知只是这个三维色度空间里的一个有限空间区域。这个区域被称为CIE色表,它囊括了所有人眼能看到的色彩。
在测量某个光子的“颜色”时,只需要把那个波长处的三种视锥细胞的响应曲线的响应率分别乘以此波段的光的强度,得到三维色度空间里的坐标(s, m, l)。在光强度固定的状态下,380nm~780nm光谱会被映射到SML空间中的一条曲线上,这条曲线正是CIE色表中的边缘曲线,CIE色表边缘上标注的波长信息表面该颜色可以近似代表自然界中对应波长的单色光的“颜色”。
注意到,表示紫色的部分波长记为-500nm,这是因为人眼实际上并不能识别紫色波段的光。人眼看到的紫色实际上是大脑对红色和蓝色的线性混合。
得到一个RGB输入后,在CIE色表中截取一个三角形,然后将RGB坐标映射到这个三角形中,每个RGB值可以对应SML坐标中的一个颜色,这个三角形范围就被称为RGB色彩标准,映射使用的变换称为色彩空间变换,常见的色彩空间变换矩阵如下:
印刷行业主流的色彩标准称作sRGB,这个标准保证了同一份印刷产品能在不同品种的标准打印机下表现相同的颜色,而Adobe指定的ARGB标准移动了三角形在绿色区域的顶点,获得了更大的色彩范围。但由于显示器复杂的工艺,大部分显示器甚至还没办法达到sRGB的标准。显示器行业中没有准确的色彩标准,这导致不同显示器厂商面对相同的RGB输入可能产生不同的显示效果。
什么是色彩空间?
色彩空间可以被理解成是一个n维的线性空间。当然,从光学角度上讲,我们最常用的色彩空间RGB中的三个分量r、g、b实际上是非正交的,因为人眼的三种感光细胞实际上对所有可视光频段的光都存在一定程度的感知。
RGB色彩空间是最好理解的色彩空间,我们在三维空间中取一组标准正交基构建三维笛卡尔坐标系,将RGB分量分别映射在XYZ轴上,每个对应点是RGB分量的线性混合,可以轻易得到RGB立方体,如图a)。不同厂商具有不同的RGB输出标准,所以在不同屏幕上观察这幅图可能看到不同的色域,而RGB立方体的顶点处的色彩一般就是该显示器设定的三基色。
CMYK色彩空间是印刷行业常用的色彩空间。利用色料的三原色混色原理,加上黑色油墨,共计四种颜色混合叠加,形成所谓“全彩印刷”。将RGB模型取反就可以轻易的获得CMY立方体,如图b)。四种标准颜色是:
- C: Cyan = 青色
- M: Magenta = 洋红色
- Y: Yellow = 黄色
- K: blacK = 黑色
理想的CMY三原色就可以混合出包括黑色在内的所有色彩,但现实世界里的彩色印刷使用的CMY三色色料并不是物理学意义上的标准三原色,这使得三层CMY套印只能表现出深灰色或深褐色。为了节省成本并增强油印效果,即使黑色并非原色,也成为彩印的必须色之一。
理想的CMY与RGB的转换:
[
C
M
Y
]
+
[
R
G
B
]
=
[
1
1
1
]
\begin{bmatrix}C\\M\\Y\end{bmatrix}+\begin{bmatrix}R\\G\\B\end{bmatrix}=\begin{bmatrix}1\\1\\1\end{bmatrix}
⎣⎡CMY⎦⎤+⎣⎡RGB⎦⎤=⎣⎡111⎦⎤
HSV(HSB)色彩空间是基于柱坐标系的色彩空间,将色调、饱和度和明度分别映射在φ、r、z分量上,可以得到HSV(HSB)柱体,如图c)。柱体最外围一圈顶点的色彩一般是该显示器的RGB色彩标准边界上的色彩。
色调H用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°(360°),黄色为60°,绿色为120°,青色为180°,蓝色为240°,紫色为300°。
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
**明度V(B)**表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
RGB到HSV(HSB)的转换:
m
=
max
(
r
,
g
,
b
)
n
=
min
(
r
,
g
,
b
)
h
=
{
0
°
m
=
n
60
°
×
g
−
b
m
−
n
+
0
°
m
=
r
∧
g
≥
b
60
°
×
g
−
b
m
−
n
+
360
°
m
=
r
∧
g
<
b
60
°
×
b
−
r
m
−
n
+
120
°
m
=
g
60
°
×
r
−
g
m
−
n
+
240
°
m
=
b
s
=
min
(
0
,
m
−
n
m
)
v
=
m
m = \max(r,g,b)\\ n = \min(r,g,b)\\ h=\left \{ \begin{array}{c} 0°&m=n\\ 60°\times \frac{g-b}{m-n}+0°&m=r \land g\ge b\\ 60°\times \frac{g-b}{m-n}+360°&m=r \land g\lt b\\ 60°\times \frac{b-r}{m-n}+120°&m=g\\ 60°\times \frac{r-g}{m-n}+240°&m=b\\ \end{array}\right. \\s=\min(0,\frac{m-n}m)\\v=m
m=max(r,g,b)n=min(r,g,b)h=⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧0°60°×m−ng−b+0°60°×m−ng−b+360°60°×m−nb−r+120°60°×m−nr−g+240°m=nm=r∧g≥bm=r∧g<bm=gm=bs=min(0,mm−n)v=m
HSV(HSB)到RGB的转换:
h
i
=
⌊
h
60
°
⌋
m
o
d
6
f
=
h
60
°
−
h
i
p
=
v
×
(
1
−
s
)
q
=
v
×
(
1
−
f
×
s
)
t
=
v
×
(
1
−
(
1
−
f
)
×
s
)
(
r
,
g
,
b
)
=
{
(
v
,
t
,
p
)
h
i
=
0
(
q
,
v
,
p
)
h
i
=
1
(
p
,
v
,
t
)
h
i
=
2
(
p
,
q
,
v
)
h
i
=
3
(
t
,
p
,
v
)
h
i
=
4
(
v
,
p
,
q
)
h
i
=
5
h_i=\lfloor\frac h{60°}\rfloor \mod 6\\ f=\frac h{60°}-h_i\\ p=v\times(1-s)\\ q=v\times(1-f\times s)\\ t=v\times(1-(1-f)\times s)\\ (r,g,b)=\left \{ \begin{array}{c} (v,t,p)&h_i=0\\ (q,v,p)&h_i=1\\ (p,v,t)&h_i=2\\ (p,q,v)&h_i=3\\ (t,p,v)&h_i=4\\ (v,p,q)&h_i=5 \end{array}\right.
hi=⌊60°h⌋mod6f=60°h−hip=v×(1−s)q=v×(1−f×s)t=v×(1−(1−f)×s)(r,g,b)=⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧(v,t,p)(q,v,p)(p,v,t)(p,q,v)(t,p,v)(v,p,q)hi=0hi=1hi=2hi=3hi=4hi=5
HSL和HSV(HSB)类似,它的H分量具有一样的算法,但S和L不同:
l
=
1
2
(
m
+
n
)
s
=
{
m
−
n
2
l
,
l
≤
1
2
m
−
n
2
−
2
l
,
l
≤
1
2
l=\frac12 (m+n)\\ s=\left \{ \begin{array}{c} \frac{m-n}{2l},&l\le \frac{1}{2}\\ \frac{m-n}{2-2l},&l\le \frac12 \end{array}\right.
l=21(m+n)s={2lm−n,2−2lm−n,l≤21l≤21
HSL转化为RGB:
q
=
{
l
×
(
1
+
s
)
,
l
<
1
2
l
+
s
−
(
l
×
s
)
,
l
≥
1
2
p
=
2
×
l
−
q
h
k
=
h
360
t
R
=
{
h
k
+
1
3
,
h
k
<
2
3
h
k
−
2
3
,
h
k
≥
2
3
t
G
=
h
k
t
B
=
{
h
k
+
2
3
,
h
k
<
1
3
h
k
−
1
3
,
h
k
≥
1
3
C
o
l
o
r
C
=
{
p
+
(
(
q
−
p
)
×
6
×
t
)
,
t
C
<
1
6
q
,
1
6
≤
t
C
<
1
2
p
+
(
(
q
−
p
)
×
6
×
(
2
3
−
t
C
)
)
,
1
2
≤
t
C
<
2
3
p
,
t
C
≥
2
3
,
C
∈
{
R
,
G
,
B
}
C
o
l
o
r
=
(
C
o
l
o
r
R
,
C
o
l
o
r
G
,
C
o
l
o
r
B
)
q=\left \{ \begin{array}{c} l\times (1+s),&l\lt\frac12\\ l+s-(l\times s),&l\ge \frac12 \end{array}\right.\\ p=2\times l-q\\ h_k=\frac h{360}\\ t_R=\left \{ \begin{array}{c}h_k+\frac13,&h_k\lt\frac23\\h_k-\frac23,&h_k\ge\frac23\end{array}\right.\\ t_G=h_k\\ t_B=\left \{ \begin{array}{c}h_k+\frac23,&h_k\lt\frac13\\h_k-\frac13,&h_k\ge\frac13\end{array}\right.\\ Color_C=\left \{ \begin{array}{c} p+((q-p)\times 6\times t),&t_C\lt\frac16\\ q,&\frac16\le t_C\lt\frac12\\ p+((q-p)\times 6\times(\frac23-t_C)),&\frac12\le t_C\lt\frac23\\ p,&t_C\ge\frac23 \end{array}\right.,C\in \{R,G,B\}\\Color=(Color_R,Color_G,Color_B)
q={l×(1+s),l+s−(l×s),l<21l≥21p=2×l−qhk=360htR={hk+31,hk−32,hk<32hk≥32tG=hktB={hk+32,hk−31,hk<31hk≥31ColorC=⎩⎪⎪⎨⎪⎪⎧p+((q−p)×6×t),q,p+((q−p)×6×(32−tC)),p,tC<6161≤tC<2121≤tC<32tC≥32,C∈{R,G,B}Color=(ColorR,ColorG,ColorB)
RGB是面向硬件的色彩空间,而HSV(HSB)和HSL是面向用户的色彩空间,符合人类直觉,在许多图像编辑工具中应用比较广泛,但这也决定了它不适合使用在光照模型中,许多光线混合运算、光强运算等都无法直接使用HSV(HSB)来实现。
除了常见的色彩空间外,我们还可以将颜色转移到灰度空间。灰度可以表现物体的亮度,通过灰度函数我们可以将三通道的色彩混合为单通道。一般的自然景物平均灰度约为18%。
灰度的计算公式包括:
- 除数法:
G r a y = 30 ⋅ R + 59 ⋅ G + 11 ⋅ B 100 Gray=\frac{30\cdot R+59\cdot G+11\cdot B}{100} Gray=10030⋅R+59⋅G+11⋅B
- 移位法:
G r a y = ( 77 ⋅ R + 151 ⋅ G + 28 ⋅ B ) > > 8 Gray = (77\cdot R+151\cdot G+28\cdot B)>>8 Gray=(77⋅R+151⋅G+28⋅B)>>8
- 伽马校正法:
G r a y = R 2.2 + ( 1.5 G ) 2.2 + ( 0.6 B ) 2.2 1 + 1. 5 2.2 + 0. 6 2.2 2.2 Gray = \sqrt[2.2]{\frac{R^{2.2}+(1.5G)^{2.2}+(0.6B)^{2.2}}{1+1.5^{2.2}+0.6^{2.2}}} Gray=2.21+1.52.2+0.62.2R2.2+(1.5G)2.2+(0.6B)2.2
鉴于精确度的要求,在高质量图片处理中最好使用伽马校正灰度公式进行计算,以保证准确度。
什么是拮抗原理?
在三基色理论之外,黑林(Ewald Hering)提出了另一种颜色理论——拮抗原理(Opponent-Process Theory),简称四基色学说。黑林认为人眼对光产生视觉反应的过程是由三对互相拮抗的组织主导的,它们分别为红与绿、黄与蓝和黑与白。当一对拮抗组织中的一个受激兴奋时,与它同组的另一类组织就会受抑制。如黄与蓝,当人眼对蓝产生兴奋时,对黄色的感知就会受到抑制。
拮抗原理也能解释颜色互补现象。如果产生两种颜色的光波相混合,结果出现灰色,则两种颜色互补,或称为互补色。互补色在色环上的位置相对,红与绿、蓝与黄是互补色。根据拮抗原理,互补现象是由于两个互补色以相反方向刺激一对拮抗视觉单元,两种色彩相互抵消造成的。这三对相互拮抗的活动已得到研究证明。它们不是在视网膜上,而是在视神经通路坐功图的神经结细胞发生的。
使用HSV(HSB)或HSL调整色相时,具有人眼视觉感知亮度随之变化的问题。例如,纯蓝与纯红在HSV中具有相同的V值,但人类对其的感知显然是蓝色更亮(当然,伽马校正可以有效的改善这一点)。拮抗原理在计算机图形学中的应用恰好可以针对性的解决这个问题。
**LUV(Lab)**色彩空间是一个不基于RGB色彩空间的色彩方案,它是由拮抗原理衍生出的一套色彩空间方案。在LUV色彩空间中,色彩被分配到了一个平面中,和纹理坐标中的UV类似,在L确定的情况下,LUV色彩平面是由一组亮度相同的颜色组成的。其中,U值定义了颜色蓝的程度,而V值定义了颜色红的程度。
LUV虽然解决了RGB亮度不均的问题,但没有提供很好的变换色相的方案。
由CIE给出的官方算法整理出RGB和LUV转换的过程,先将RGB转换到XYZ空间:
X
Y
Z
=
[
0.636958
0.144616
0.168881
0.262700
0.677998
0.059301
0.000000
0.028072
1.060985
]
[
R
G
B
]
R
G
B
=
[
1.716651
−
0.355670
−
0.253366
−
0.666684
1.616481
0.015769
0.017639
−
0.042771
0.942103
]
[
X
Y
Z
]
XYZ=\begin{bmatrix}0.636958&0.144616&0.168881\\ 0.262700&0.677998&0.059301\\0.000000&0.028072&1.060985 \end{bmatrix}\begin{bmatrix}R\\G\\B\end{bmatrix}\\RGB=\begin{bmatrix} 1.716651&-0.355670&-0.253366\\-0.666684&1.616481&0.015769\\ 0.017639&-0.042771&0.942103 \end{bmatrix}\begin{bmatrix}X\\Y\\Z\end{bmatrix}
XYZ=⎣⎡0.6369580.2627000.0000000.1446160.6779980.0280720.1688810.0593011.060985⎦⎤⎣⎡RGB⎦⎤RGB=⎣⎡1.716651−0.6666840.017639−0.3556701.616481−0.042771−0.2533660.0157690.942103⎦⎤⎣⎡XYZ⎦⎤
注:浮点数保留小数点后六位,更精确的参数参见W3C的版本:
https://www.w3.org/TR/css-color-4/#color-conversion-code
然后将XYZ转换到LUV:
ϵ
=
(
6
29
)
3
κ
=
(
29
3
)
3
x
y
z
=
(
X
0.96422
,
Y
,
Z
0.82521
)
f
a
=
a
>
ϵ
?
a
3
:
(
κ
∗
a
+
16
)
116
,
a
∈
{
X
,
Y
,
Z
}
L
=
(
116
∗
f
Y
)
−
16
U
=
500
∗
(
f
X
−
f
Y
)
V
=
200
∗
(
f
Y
−
f
Z
)
\epsilon=(\frac6{29})^3\\\kappa=(\frac{29}3)^3\\ xyz=(\frac X{0.96422},Y,\frac Z{0.82521})\\ f_a=a>\epsilon\ ?\sqrt[3]a\ :\ \frac{(\kappa*a +16)}{116}, a\in \{X,Y,Z\} \\L=(116*f_Y)-16\\U=500*(f_X-f_Y)\\V=200*(f_Y-f_Z)
ϵ=(296)3κ=(329)3xyz=(0.96422X,Y,0.82521Z)fa=a>ϵ ?3a : 116(κ∗a+16),a∈{X,Y,Z}L=(116∗fY)−16U=500∗(fX−fY)V=200∗(fY−fZ)
将LUV转换为XYZ:
KaTeX parse error: Undefined control sequence: \ at position 247: …kappa})*0.82521\̲ ̲
HCL (Hue-Chroma-Luminance)**色彩空间是由拮抗原理衍生出的一套色彩空间方案,旨在确保当色相改变而 L 通道保持恒定时,对人眼而言,色彩对比度不变。HCL由CIE提出,所以也被称作CIEHCLuv。HCL可以被理解为LUV的极坐标表示,如图所示:
Luminance作为极坐标的纵轴,将LUV中的UV看作向量,向量的模为Chroma,夹角为Hue。
由于HCL是基于拮抗原理的色彩空间,它并不能由RGB色彩空间经过线性变化得到,在RGB和HCL之间转换存在一定的失配问题,具体表现为:转换后颜色但色相、感知明度均存在,但颜色的色距不一定在 HCL 色彩空间范围内。
由LUV转换到HCL:
L
=
L
H
′
=
arctan
(
V
U
)
∗
180
π
H
=
H
′
>
=
0
?
H
′
:
H
′
+
360
C
=
U
2
+
V
2
L = L\\ H'=\arctan(\frac{V}{U})*\frac{180}{\pi}\\ H = H'>=0\ ?\ H':H'+360\\ C=\sqrt{U^2+V^2}
L=LH′=arctan(UV)∗π180H=H′>=0 ? H′:H′+360C=U2+V2
反过来由HCL得到LUV:
L
=
L
U
=
C
cos
(
H
∗
π
180
)
V
=
C
sin
(
H
∗
π
180
)
L=L\\U=C\cos(H*\frac\pi{180})\\ V=C\sin(H*\frac\pi{180})\\
L=LU=Ccos(H∗180π)V=Csin(H∗180π)
色彩变换
如何调整色相?
前面我们已经利用拮抗原理和HCL色彩空间为色相的调整做好了铺垫,将RGB转换到HCL色彩空间,然后调整H即可,这样可以得到最稳定的色相变化。
当然,在HSV空间中调整H也可以实现色相变化,但可能不是特别符合人眼直觉。
如何调整亮度/曝光度?
调整亮度即调整HSV中的V(或HSL中的L),表现在伽马色彩空间中是对RGB增加一个变化值k:
[
R
′
G
′
B
′
]
=
[
R
+
k
G
+
k
B
+
k
]
\begin{bmatrix}R'\\G'\\B'\end{bmatrix}=\begin{bmatrix}R+k\\G+k\\B+k\end{bmatrix}
⎣⎡R′G′B′⎦⎤=⎣⎡R+kG+kB+k⎦⎤
调整亮度会使整体层次感明显下降,这是因为亮度的变化没有符合人眼对亮度的非线性感知。
调整曝光度时,饱和度和亮度同时变化,表现在伽马色彩空间中是对RGB的叠乘:
[
R
′
G
′
B
′
]
=
2
k
[
R
G
B
]
\begin{bmatrix}R'\\G'\\B'\end{bmatrix}=2^k\begin{bmatrix}R\\G\\B\end{bmatrix}
⎣⎡R′G′B′⎦⎤=2k⎣⎡RGB⎦⎤
调整曝光度不会影响图像的层次感。
如何调整饱和度?
饱和度是指色彩的鲜艳程度,也称色彩的纯度。美术色彩学的定义中,饱和度是颜色与灰色的混合度,向纯色中加入灰色则饱和度下降。感性理解的话,饱和度增加就是使RGB中较强的分量更加强,较弱的分量更加弱。满饱和度的图像指只由R、G、B、C、M、Y六色以及不同亮度的灰色组成的图像。
首先我们判断RGB中强的分量有哪些,为此我们必须求出一个阈值。阈值的求法可以使用算术平均数、几何平均数或平方平均数等,通常使用算术平均数,我们记这个阈值为μ,同时令饱和度变换率为k。则饱和度的计算公式为:
o
u
t
C
=
i
n
C
+
k
∗
(
i
n
C
−
μ
)
,
C
∈
{
R
,
G
,
B
}
out_C=in_C+k*(in_C-\mu),C\in\{R,G,B\}
outC=inC+k∗(inC−μ),C∈{R,G,B}
PhotoShop中的饱和度算法比起平均值公式更复杂,首先计算HSL中的L与S:
l
=
1
2
(
m
a
x
+
m
i
n
)
s
=
{
m
a
x
−
m
i
n
2
l
,
l
≤
1
2
m
a
x
−
m
i
n
2
−
2
l
,
l
≤
1
2
l=\frac12 (max+min)\\ s=\left \{ \begin{array}{c} \frac{max-min}{2l},&l\le \frac{1}{2}\\ \frac{max-min}{2-2l},&l\le \frac12 \end{array}\right.
l=21(max+min)s={2lmax−min,2−2lmax−min,l≤21l≤21
根据饱和度变化方向分类讨论,如果饱和度变化量k>=0,求出中间量α:
α
=
{
1
S
−
1
,
k
+
S
≥
1
1
1
−
k
−
1
,
k
+
S
<
1
o
u
t
C
=
i
n
C
+
(
i
n
C
−
L
)
×
α
,
C
∈
{
R
,
G
,
B
}
\alpha=\left \{ \begin{array}{c} \frac1S-1,&k+S\ge 1\\\frac1{1-k}-1,&k+S\lt 1 \end{array}\right.\\ out_C=in_C+(in_C-L)\times \alpha,\ C\in\{R,G,B\}
α={S1−1,1−k1−1,k+S≥1k+S<1outC=inC+(inC−L)×α, C∈{R,G,B}
如果饱和度变化量k<0:
o
u
t
C
=
L
+
(
i
n
C
−
L
)
×
(
1
+
k
)
,
C
∈
{
R
,
G
,
B
}
out_C=L+(in_C-L)\times(1+k),\ C\in\{R,G,B\}
outC=L+(inC−L)×(1+k), C∈{R,G,B}
如何调整对比度?
对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,差异范围越大代表对比越大,差异范围越小代表对比越小。反应到图像编辑上,调整对比度就是扩大或缩小亮的点和暗的点的差异。关于伽马空间的问题我们在上一部分已经谈过了,在此我们给出一个伽马色彩空间下的线性调整公式:
O
u
t
=
A
v
e
r
a
g
e
+
k
⋅
(
I
n
−
A
v
e
r
a
g
e
)
Out = Average + k\cdot(In-Average)
Out=Average+k⋅(In−Average)
我们知道了亮度的调节公式,将它转换为对RGB的操作,得到:
C
′
=
O
u
t
I
n
C
C'=\frac {Out}{In}C
C′=InOutC
对提供给美术人员的对比度调节接口来说,这个算法已经足矣,因为美术人员对处理效率并不在意,并且很在意操作的自由度。
但是对计算机自动化处理,尤其是实时处理来说,计算平均亮度是得不偿失的,因为我们更注重的是通过提高对比度突显一些因为灰度相近而被隐藏的信息,而不是保证灰度的精确性,而计算平均灰度对GPU来说是及其不友好的。在精确性和效率上这个公式都不足够。
如果照顾GPU的并行特性,就必须去掉计算平均值这样的数据依赖,这里给出一个无数据依赖的调整公式。它的效果或许不够稳定,但效率足够高。其中Threshold是灰度阈值参数,通过估计图片平均灰度来计算对比度。
O
u
t
=
I
n
+
k
⋅
(
I
n
−
T
h
r
e
s
h
o
l
d
)
Out = In + k \cdot (In-Threshold)
Out=In+k⋅(In−Threshold)
而如果不考虑GPU的并行性,我们可以使用直方图均衡化实现效果更好且鲁棒性更强的对比度增强算法:
我们通过统计学的方法得到某图像的灰度分布直方图,在绝大部分自然图像中,灰度都聚集在一个很小的范围内,一般在[90,180]的范围左右,我们设原图A的灰度分布函数为HA(D)。我们希望对每个像素上的颜色进行变换,使其最终整个图像的灰度能在伽马色彩空间的[0,255]线性均匀分布,换句话说,就是进行一个f : R->R映射DB=f(DA),对每个像素点施加一个变换,将图像A变为图像B,新的灰度分布函数为HB(D)。现在我们尝试求函数f:
由于DB与DA满足函数关系,显然有:
D
B
=
f
(
D
A
)
D
B
+
Δ
D
B
=
f
(
D
A
+
Δ
D
A
)
D_B=f(D_A)\\D_B+\Delta D_B=f(D_A+\Delta D_A)
DB=f(DA)DB+ΔDB=f(DA+ΔDA)
为了使DB与DA在对应的区段上包含相同数量的像素,我们给出:
∫
0
D
A
H
A
(
D
)
d
D
=
∫
0
D
B
H
B
(
D
)
d
D
\int_0^{D_A}H_A(D)dD=\int_0^{D_B}H_B(D)dD
∫0DAHA(D)dD=∫0DBHB(D)dD
理想的HB(D)应使像素在不同灰度梯度线性均匀分布,所以有:
H
B
(
D
)
=
A
0
L
H_B(D)=\frac {A_0}{L}
HB(D)=LA0
其中A0为像素点总数,L为灰度梯度,一般为256。将HB(D)代入微分方程得:
∫
0
D
A
H
A
(
D
)
d
D
=
A
0
D
B
L
=
A
0
f
(
D
A
)
L
\int_0^{D_A}H_A(D)dD=\frac{A_0D_B}L=\frac{A_0f(D_A)}L
∫0DAHA(D)dD=LA0DB=LA0f(DA)
轻易解得:
f
(
D
A
)
=
L
A
0
∫
0
D
A
H
A
(
D
)
d
D
f(D_A)=\frac L{A_0}\int_0^{D_A}H_A(D)dD
f(DA)=A0L∫0DAHA(D)dD
将其改写成适合计算机得离散形式得:
f
(
D
A
)
=
L
A
0
∑
i
=
0
D
A
H
A
(
i
)
f(D_A)=\frac L{A_0}\sum_{i=0}^{D_A}H_A(i)
f(DA)=A0Li=0∑DAHA(i)
这个公式已经足以适应绝大部分的图像,但问题也很明显——我们的推导是在HA(D)为连续分布函数的假设基础上进行的,而实际上HA(D)是一个离散函数。在大部分情况下,目标直方图会是近似均匀分布的,但假若A图像的灰度直方图变化剧烈,且某些灰度区间不存在像素点,这会造成B图像的直方图也有较大的不均匀性。
解决A直方图变化剧烈的方法之一是削峰填谷,设定一个阈值,假定直方图某个灰度级的像素数或占总像素比例超过了阈值,就对之进行裁剪,将超出阈值的部分平均分配到各个灰度级。这个思想可以用下图来理解:
将削峰填谷的思想公式化的表达如下:
P
(
D
A
)
=
min
(
0
,
H
A
(
D
A
)
−
T
h
r
e
s
h
o
l
d
)
H
A
′
(
D
A
)
=
max
(
H
A
(
D
A
)
,
T
h
r
e
s
h
o
l
d
)
+
∫
0
L
P
(
D
)
d
D
L
f
(
D
A
)
=
L
A
0
∑
i
=
0
D
A
H
A
′
(
i
)
P(D_A)=\min(0,H_A(D_A)-Threshold)\\H_{A'}(D_A)=\max(H_A(D_A),Threshold)+\frac{\int_0^L P(D)dD}{L}\\f(D_A)=\frac L{A_0}\sum_{i=0}^{D_A}H_{A'}(i)
P(DA)=min(0,HA(DA)−Threshold)HA′(DA)=max(HA(DA),Threshold)+L∫0LP(D)dDf(DA)=A0Li=0∑DAHA′(i)
另一种方法是根据灰度级进行均衡化,将灰度级划分为几个灰度区间,使每个区间内的像素数量大致相等,然后分别在每个区间做直方图均衡化,最后合并。
最后,CLAHE插值法还提出了将图像分割成多个窗口的局部直方图均衡化算法。首先,将图像分块,每块称为窗口,每个窗口计算一个直方图累积分数函数(指前文中的f(DA))。对于图像的每一个像素点,找到其邻近的四个窗口,分别计算四个窗口直方图累积分数函数的映射值,分别记作ful、fur、fbl、fbr,进行双线性插值得到最终该像素点的映射值。**双线性插值(BiLinear)**公式为:
f
(
D
)
=
(
1
−
Δ
y
)
(
(
1
−
Δ
x
)
f
u
l
(
D
)
+
Δ
x
f
b
l
(
D
)
)
+
Δ
y
(
(
1
−
Δ
x
)
f
u
r
(
D
)
+
Δ
x
f
b
r
(
D
)
)
f(D)=(1-\Delta y)((1-\Delta x)f_{ul}(D)+\Delta xf_{bl}(D))+\Delta y((1-\Delta x)f_{ur}(D)+\Delta xf_{br}(D))
f(D)=(1−Δy)((1−Δx)ful(D)+Δxfbl(D))+Δy((1−Δx)fur(D)+Δxfbr(D))
如何进行色调分离?
色调分离就是通过降采样,将接近的颜色转化为同一颜色。这一效果经常用在屏幕后处理中实现非真实渲染效果。
最简单的降采样算法是利用整数除法运算的截断:
R = R / 16 * 16;
G = G / 16 * 16;
B = B / 16 * 16;
这样就可以轻易的将相近的颜色截取到一起。类似的,只需要修改除数的大小就可以修改色阶的大小。
颜色混合
有哪些颜色混合算法?
在本节中出现的A表示下层图层的像素每个通道值(遍历RGB三个通道),B表示上层图层的像素每个通道值,C表示新图像的像素每个通道值。
-
Darken 变暗:
对每个像素的每个通道比较上下两个图层,取通道数值较小的值作为混合像素该通道的值。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.3, 0.5, 0.3)
-
Lighten 变亮:
对每个像素的每个通道比较上下两个图层,取通道数值较大的值作为混合像素该通道的值。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.5, 0.8, 0.8)
-
Multiply 正片叠底:
C=AB。其效果可以形容成:两个幻灯片叠加在一起然后放映,透射光需要分别通过这两个幻灯片,从而被削弱了两次。由于符合美术中色彩混合的特点,是最常用的混合方式。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.15, 0.4, 0.24)
-
Screen 滤色:
对两个RGB向量取反色,相乘,再取反色,即C=1-(1-A)(1-B),其中1-X为X的反相。其效果可以形容成:两台投影机分别对两个图层进行投影后,然后投射到同一个屏幕上。常用于混合多个光照。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.65, 0.9, 0.86)
-
Color Burn 颜色加深:
C = A - (1-A)(1-B) / B。常用于实现阴影。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0, 0.375, 0.333)
-
Color Dodge 颜色减淡:
C = A + (AB)/(1-B)。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.6, 1.0, 1.0)
-
Liner Burn 线性加深:
C = A + B - 1。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0, 0.3, 0.1)
-
Liner Dodge 线性减淡:
C = A + B。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.8, 1.0, 1.0)
-
Overlay 叠加:
A<=0.5,则 C=2AB,否则 C=1-2(1-A)(1-B)。依据下层色彩值的差别,动态的决定使用Multiply或Screen,而上层决定了下层中间色调偏移的强度。如果上层为50%灰,则结果将完全为下层像素的值。如果上层比50%灰暗,则下层的中间色调的将向暗地方偏移,下层中间色调以下的色带变窄,中间色调以上的色带变宽。如果上层比50%灰亮,则下层的中间色调的将向亮地方偏移,下层中间色调以下的色带变宽,中间色调以上的色带变窄。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.15, 0.8, 0.72)
-
Hard Light 强光:
B<=0.5,则 C = 2AB,否则 C = 1-2(1-A)(1-B),相当于叠加模式上下两个图层次序交换的情况。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.3, 0.8, 0.24)
-
Soft Light 柔光:
B<=0.5,则C = 2AB+(A^2)(1-2B),否则C = 2A(1-B)+(A^0.5)(2B-1)。该模式将B作为遮罩,对A进行了Gamma值范围0.5到2.0的近似伽马校正,结果将是非常柔和的色彩组合。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.3, 0.544, 0.736)
-
Vivid Light 亮光:
B<=0.5,则C=A-(1-A)(1-2B)/(2B),否则C=A+A(2B-1)/(2(1-B))。该模式非常强烈的增加了对比度,特别是在高亮和阴暗处。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.3, 1.0, 0.667)
-
Linear Light 线形光:
C=A+2B-1。和Linear Burn类似,只不过是加深了上层的影响力。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.3, 1.0, 0.4)
-
Pin Light 点光:
B<=0.5,则C=min(A,2B),否则C=min(A,2B-1)。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.3, 0.5, 0.6)
-
Differences 差值:
C=|A-B|,用于得出两个像素的差异,差异越大越亮。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.2, 0.3, 0.2)
-
Exclusion 排除:
C=A+B-2AB,亮的图片区域将导致另一层的反相,暗的区域导致另一层完全没有变化。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.5, 0.5, 0.62)
-
Hue 色相:
HCSCVC =HBSAVA,输出图像的饱和度为上层,色调和亮度保持为下层。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.5, 0.8, 0.3)
-
Color 颜色:
HCSCVC =HBSBVA,输出图像的亮度为下层,色调和饱和度保持为上层。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.5, 0.8, 0.3)
-
Luminosity 亮度
HCSCVC=HASAVB,输出图像的亮度为上层,色调和饱和度保持为下层。
A(0.3, 0.5, 0.8) mix B(0.5, 0.8, 0.3) = C(0.3, 0.5, 0.8)
有哪些透明度混合算法?
-
简易透明度混合:
R G B = { R G B 1 + R G B 2 2 α = 0.5 R G B 1 + R G B 2 − R G B 1 α α < 0.5 R G B 2 + R G B 1 − R G B 2 α α > 0.5 (14) RGB =\left \{ \begin{array}{c} \frac{RGB1 + RGB2}2 &\alpha=0.5\\ RGB1+\frac{RGB2-RGB1}\alpha&\alpha<0.5\\ RGB2+\frac{RGB1-RGB2}\alpha&\alpha>0.5 \end{array}\right.\tag{14} RGB=⎩⎨⎧2RGB1+RGB2RGB1+αRGB2−RGB1RGB2+αRGB1−RGB2α=0.5α<0.5α>0.5(14)
这个算法的特点是没有明确的上下层,α值更像是两个图层的混合参数而非某个图层的不透明度。 -
加权透明度混合:
R G B = α 1 ∗ R G B 1 + α 2 ∗ R G B 2 α 1 + α 2 RGB = \frac{\alpha_1 *RGB1+\alpha_2*RGB2}{\alpha_1+\alpha_2} RGB=α1+α2α1∗RGB1+α2∗RGB2
这个混合效果是将RGB1以α的程度混合到RGB2中,于透过RGB1看RGB2效果不同,更像是将两种颜色按比例α调和了起来,属于颜色混合的带参数扩展。 -
双透明度混合:
R G B = ( R G B 1 ∗ α 1 + R G B 2 ∗ α 2 ∗ ( 1 − α 1 ) ) α = 1 − ( 1 − α 1 ) ∗ ( 1 − α 2 ) RGB=(RGB1*\alpha_1+RGB2*\alpha_2*{(1-\alpha_1)})\\ \alpha=1-(1-\alpha_1)*(1-\alpha_2) RGB=(RGB1∗α1+RGB2∗α2∗(1−α1))α=1−(1−α1)∗(1−α2)
其中,RGB1、α1是前景颜色值,RGB2、α2是背景颜色值,α是新图片的不透明度。这个算法可以模拟两个半透明像素混合时生成新的半透明像素的效果。 -
经典透明度混合:
R G B = α ∗ R G B 1 + ( 1 − α ) ∗ R G B 2 RGB=\alpha*RGB1+(1-\alpha)*RGB2 RGB=α∗RGB1+(1−α)∗RGB2
假设RGB1是透明图像,RGB2是不透明图像,α是RGB1某像素的不透明度,这个混合模式可以模拟透过RGB1观察RGB2的效果。由于渲染过程中,透明图像总是叠加在不透明图像前方,所以这个算法是最常用的透明度算法。