色彩基础
色彩模型(Color model)是一种抽象数学模型,通过一组数字来描述颜色,常见的有:RGB、CMYK(印刷业)、YUV(视频影像处理)、HSL(HSV)、Lab等。
色域是指一个技术系统能够产生的颜色的总合。色域是颜色的某个完全的子集。
色彩空间(Color space)是对色彩的组织方式。定义颜色的数学表达方式,包含:
色域(可表示的颜色范围)
原色坐标(RGB空间的R/G/B基色位置)
白点(中性白色的定义)
Gamma曲线(非线性编码方式)
定义色彩空间时,通常使用 CIELAB 或者 CIEXYZ 色彩空间作为参考标准。这两个色彩空间在设计时便要求包含普通人眼可见的所有颜色。
色彩空间由色彩模型和色域共同定义。例如Adobe RGB和sRGB都基于RGB颜色模型,但它们是两个不同色彩空间,因为他们的色域不同。
颜色
颜色是在特定颜色空间中的坐标值的映射,用函数表示如下:
sRGB空间中的 (1.0, 0.0, 0.0) 对应特定红色。
Adobe RGB空间中的同样数值会显示更饱和的红色。
由于“色彩空间”有着固定的色彩模型和映射函数组合,非正式场合下,这一词汇也被用来指代色彩模型。尽管固定的色彩空间有固定的色彩模型相对应,这样的用法严格意义上是错误的。
当我们在比较UI颜色是否相同时,一定是在同种颜色空间下,否则的话,同样的RGB值并不能表示同样的颜色。
常见的颜色空间
sRGB色彩空间(standard Red Green Blue,标准红绿蓝色彩空间)是惠普与微软于1996年一起开发的用于显示器、打印机以及因特网的一种标准RGB色彩空间。
DCI-P3,或DCI/P3, 美国电影行业推出的一种广色域标准,是目前数位电影回放设备的色彩标准之一。DCI-P3 比 sRGB 色彩空间大了约 25%。
Adobe RGB色彩空间是一种由Adobe Systems于1998年开发的色彩空间。Adobe RGB色彩空间粗略包括了50%的Lab色彩空间中的可视色彩,主要在青绿色(cyan-green)色系上有所提升。
Gamma(伽玛)校正与线性编码
人眼感光特性
人眼对亮度的反应是非线性增长的。由于我们生活的世界中的景物亮度变化范围非常大。比如在晚上微弱的光线下我们要能及时发现危险;在正午的阳光下要能清晰分辨出物体。我们人类必须适应巨大的亮度差才能存活下来。
值得一提的是,人眼的感光能力是随着外界环境光的强弱来自动调节的:比如在昏暗的房间里,我们看到的烛光是明亮的,但是同样的烛光在正午阳光下,我们甚至连火苗都看不见;当晚上突然开灯的时候会觉得灯光刺眼,要隔一阵子才能适应灯光。
这说明人眼对亮度变化的反应随着光的增加而减弱。实验表明,人眼的这种特性近似于 Log 函数,若用 I 表示亮度,S 表示人眼反应,那么两者之间的对应关系为:其中 a 为常数。大概的函数图如下:
可以看出人眼对暗部的变化比较敏感。
线性编码
实验表明,人眼对亮度变化的分辨率是有限的,在均匀的背景下,当人眼反应变化超过 1% 时,人眼才能感受到亮度的变化。
所以在对数字影像编码的时候,所有相邻的编码值的反应差必须小于 1% 才能保证数字影像的均匀平滑,否则人眼将识别出像素间的亮度突变。
第一行是人眼所感知到的正常的灰阶,亮度要增加一倍(比如从0.1到0.2)你才会感觉比原来变亮了一倍.
第二行灰阶显示出的才是物理世界真实的亮度,但是由于这与我们的眼睛感知亮度不完全一致(对比较暗的颜色变化更敏感),所以它看起来有差异。
若我们采用 8bit 线性编码,即编码值与其代表的亮度线性对应。那么在 0~255 的编码范围内,由于亮色和暗色均匀分布的话,那亮色部分就会精度过剩而暗色部分就会精度不足。如何解决这个问题?进行 Gamma 矫正。
Gamma校正
从字面上看,Gamma指的是指数运算中的指数。比方说 Gamma 指的就是2.2这个数字。计算机图形学中颜色值,特别是 RGBA 值都是用0到1的浮点数表示。颜色的 Gamma 矫正本质上就是一系列介于0到1之间的浮点数进行指数运算,通常来说这个数字是2.2。
设编码值为 C,亮度为 I,则两者的关系为:具体见下图,横轴为亮度I,纵轴为编码C。
上图中的红色虚线就是图像的 Gamma 矫正曲线,一般都是2.2的倒数,0.454545..。观察矫正以后的曲线,原来0到0.5的区间被扩展到了0到~0.73,相应的大于0.5的区间被压缩到只有原来的一半左右。这样通过一次简单的计算,我们达到了不增加数据量的前提下提高可辨识精度的目的。
tip:存储在你硬盘上的图像或视频,其颜色编码都是矫正过的。
现在我们知道了对图像进行 Gamma 矫正的本质上的原因——我们的眼睛。那么为什么是2.2的倒数?因为老式的 CRT 显示器(阴极射线管显示器)。
阴极管显示器有一个物理特性——输入值和输出值呈现指数关系,这个指数是2.2。也就是说,当你输入0.5的亮度,在屏幕上得到的不是0.5,而是
上图中的红色实现就是CRT 显示器的 Gamma 曲线。当我们在 CRT 显示器上显示一张 Gamma 矫正过的图像,也就是上面的那根曲线,会发生什么事情?我们带入公式计算一下:
化简:
带入
可得
就会获得所期望的线性图像——中间那根灰色的直线。
事实上我们之所以选择2.2的倒数进行图像矫正,那是为了迎合 CRT 显示器的特性。如果显示器的 Gamma 值不是2.2,那么图像矫正 Gamma 值也需要相应变更,比方说早年的 Mac 操作系统都是使用1.8的 Gamma 值。
现代的 LCD 显示器已经不再具有这个特性,但是生产厂商仍旧会加入模拟 Gamma 曲线的硬件功能,也就是说所有显示器都期待软件输出 Gamma 矫正过的图像。
下图是一张完整的照片颜色处理的过程:
Gamma 矫正的确是一个隐藏的比较深的问题,别说普通人,长久以来游戏界也一直没有重视。计算机图形学中的算法普遍需要输入值处于线性空间,而读入的图像却都是矫正过的——处于 Gamma 空间。最终得到的计算结果自然也是不正确的。
由于亮度取值范围为0~1,经过显示器2.2次幂计算后值会变小,会显示的更暗一些。出于这个原因,美术师通常将光照值设置得比本来更亮一些,如果不是这样,在线性空间里计算出来的光照就会不正确。
因为所有中间亮度都是线性空间计算出来的(计算的时候假设Gamma为1)监视器显以后,实际上都会不正确。当使用更高级的光照算法时,这个问题会变得越来越明显,你可以看看下图:
设备颜色空间
通常来说,颜色受限于显示设备的显示性能,无法覆盖所有人眼可视的色域,所以设备会有自己可显示的颜色空间。拿iphone举例:在iPhone 7之前,苹果一直沿用的sRGB色域。在iPhone 7上,苹果使用了的 Display P3 色域的 LCD 屏幕。
在iOS上,对于颜色是通过UIColor(CGColorRef)来实现的。对于任意一个UIColor对象,我们可以通过CGColorGetColorSpace(color.CGColor)
函数获取当前UIColor颜色对象所在的颜色空间对象,可以使用CGColorSpaceGetName(colorSpace)
函数输出对应颜色空间的名称。
在我们日常基于RGB创建的UIColor中,例如如下的代码:
+ (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
在iOS版本<10.0,默认创建的为设备 RGB 色彩空间。在iOS版本≥10.0,默认创建的为sRGB空间。在iOS版本≥13.0,默认创建扩展 sRGB空间。
这里简单讲几个iOS中可能会用到的颜色空间:
颜色空间 | 特性 |
---|---|
kCGColorSpaceSRGB | 伽马编码:使用sRGB标准的非线性伽马曲线(≈2.2) 色域范围:标准sRGB(0.0-1.0) 通道位深:通常8位/通道 |
kCGColorSpaceLinearSRGB | 伽马编码:线性光值(无伽马校正) 色域范围:标准sRGB(0.0-1.0) 通道位深:通常16位浮点或32位浮点 |
kCGColorSpaceExtendedSRGB | 伽马编码:类似sRGB的非线性扩展 色域范围:超出标准sRGB(支持负数及>1.0的值) 通道位深:16位浮点或更高 |
kCGColorSpaceExtendedLinearSRGB | 伽马编码:线性光值 色域范围:扩展范围(同extendedSRGB) 通道位深:32位浮点 |
同样还有部分创建颜色的方法:
// 创建HSB颜色空间下的颜色
+ (UIColor *)colorWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha;
// 创建P3颜色空间下的颜色
+ (UIColor *)colorWithDisplayP3Red:(CGFloat)displayP3Red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha API_AVAILABLE(ios(10.0), watchos(3.0));
// 使用 Core Graphics(CGColor)创建颜色
#import <CoreGraphics/CoreGraphics.h>
// 1\. 定义颜色空间
CGColorSpaceRef p3ColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
CGColorSpaceRef srgbColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
// 2\. 创建 Display P3 颜色
CGFloat p3BlueComponents[] = {0.0, 0.0, 1.0, 1.0};
CGColorRef p3Blue = CGColorCreate(p3ColorSpace, p3BlueComponents);
// 3\. 创建 sRGB 颜色
CGFloat srgbGreenComponents[] = {0.0, 1.0, 0.0, 1.0};
CGColorRef srgbGreen = CGColorCreate(srgbColorSpace, srgbGreenComponents);
// 释放颜色空间(需手动管理内存)
CGColorSpaceRelease(p3ColorSpace);
CGColorSpaceRelease(srgbColorSpace);
上文介绍了在iOS13以后,通过RGB创建颜色的默认颜色空间是扩展sRGB颜色空间,且扩展颜色sRGB空间取值范围可以是负数、也可以大于1.0。这里使用一个小Demo给大家演示一个好玩的例子:
代码主要就是设置tableView相应行的颜色,一行为(1.0, 0, 0),一行为(2.0, 0 ,0)。
UIColor *color = [UIColor colorWithRed:indexPath.row%2 ? 1.0:2.0 green:0 blue:0 alpha:1];
上面的例子,在iphone14真机上可以看到两种颜色是不同的,但是在模拟器上,则两种颜色是相同的。同时,如果在真机上进行截屏或屏幕录制,无法录制出不同的颜色。
这是因为iPhone14屏幕支持的色域更广,设备颜色空间支持展示这种颜色。而模拟器上,使用的显示器的颜色空间为sRGB,不支持展示更广的颜色空间。同时,当我们截图或屏幕录制的时候,相应格式也会限制颜色空间,这里可以看到,我们的截图有一个颜色描述文件 sRGB IEC61966-2.1,即描述我们当前图片中的颜色空间是哪个,显示器要应该要如何显示图片。
颜色差异和对比度
颜色差异(英语:Color difference),亦称颜色距离,是色彩学上的一个关注点。它量化了一个概念。在未量化之前,人们只能用形容词来大概描述这个概念,这使得对颜色要求严格的工作者们很不方便。颜色差异可以通过色彩空间内的欧氏距离简单计算得出,也可以使用国际照明委员会较为复杂、均匀的人类知觉公式计算。
欧式距离
很多日常使用的“颜色差异”,是直接通过在一个“设备无关”的色彩空间里,进行欧氏距离的计算得到的。给定一个RGB(红绿蓝)的色彩空间,最简单的差异计算方式就是在这个三维空间里求两个点间的距离:
有不少人尝试将RGB三值加上权重,希望可以让得到的结果更加符合人类感官。一种做法是使用2、4、3:
CIEDE2000
后续由于考虑到人眼对颜色的感知非均匀,国际照明委员会不断更新更符合人员的色差计算公式,通过添加各种权重去拟合人眼感知。
对比度(Luminance Contrast)
色彩对比度是指在同一视觉场景中,不同颜色之间明暗、冷暖、纯度等方面的差异。简单来说,如果一幅画中的颜色之间差异较大,我们就说这幅画的色彩对比度高;反之,如果颜色之间差异较小,我们就说这幅画的色彩对比度低。对比度高的画面通常更为引人注目,给人强烈的视觉冲击,而对比度低的画面则给人以和谐、舒适的感觉。
WCAG 2.1公式:计算颜色A与B的 相对亮度差异:
对比度 = (L1 + 0.05) / (L2 + 0.05) ,(L1为较亮颜色亮度,L2为较暗颜色亮度)
L = 0.2126R' + 0.7152G' + 0.0722*B',R'、G'、B'均为去除伽马校正后的线性编码值。
评估可读性
在评估背景色A与文字颜色B的视觉可读性时,使用 对比度(Luminance Contrast) 和 色彩差异(Color Difference, ΔE) 是两种不同的判断逻辑。以下是两者的核心差异、优缺点及适用场景分析:
方案 | 优点 | 缺点 |
---|---|---|
对比度判断法(基于亮度差异) | 标准化:符合WCAG可访问性标准(AA级需≥4.5:1,AAA级需≥7:1) 亮度敏感:直接针对人眼对明暗的感知,对视力障碍者友好 简单易用:无需复杂计算,工具自动生成结果(如Chrome DevTools) |
忽略色相差异:可能通过亮度但色相接近(如深红 vs 暗橙)导致辨识困难 |
色彩差异(Color Difference, ΔE) | 多维感知:同时考虑亮度、色相、饱和度,更贴近人眼真实感受 广色域支持:适用于P3、AdobeRGB等超出sRGB的场景 |
无明确标准:ΔE阈值缺乏统一规范(通常认为ΔE>5可区分,但需场景调整) 计算复杂:需转换RGB到LAB空间,工具依赖性强 过度敏感:可能高估微小差异(如浅灰 vs 冷灰ΔE高但实际不可读) |