关于颜色你知道多少?

色彩基础

色彩模型(Color model)是一种抽象数学模型,通过一组数字来描述颜色,常见的有:RGB、CMYK(印刷业)、YUV(视频影像处理)、HSL(HSV)、Lab等。

色域是指一个技术系统能够产生的颜色的总合。色域是颜色的某个完全的子集。

色彩空间(Color space)是对色彩的组织方式。定义颜色的数学表达方式,包含:

  • 色域(可表示的颜色范围)

  • 原色坐标(RGB空间的R/G/B基色位置)

  • 白点(中性白色的定义)

  • Gamma曲线(非线性编码方式)

2.png

定义色彩空间时,通常使用 CIELAB 或者 CIEXYZ 色彩空间作为参考标准。这两个色彩空间在设计时便要求包含普通人眼可见的所有颜色。

色彩空间由色彩模型和色域共同定义。例如Adobe RGB和sRGB都基于RGB颜色模型,但它们是两个不同色彩空间,因为他们的色域不同。

颜色

颜色是在特定颜色空间中的坐标值的映射,用函数表示如下:

颜色 = f(颜色空间,色彩模型的数值输入)

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 表示人眼反应,那么两者之间的对应关系为:s = a * logI其中 a 为常数。大概的函数图如下:

3.png

可以看出人眼对暗部的变化比较敏感。

线性编码

实验表明,人眼对亮度变化的分辨率是有限的,在均匀的背景下,当人眼反应变化超过 1% 时,人眼才能感受到亮度的变化。\Delta s = s_1 - s_2

所以在对数字影像编码的时候,所有相邻的编码值的反应差必须小于 1% 才能保证数字影像的均匀平滑,否则人眼将识别出像素间的亮度突变。

4.png
  • 第一行是人眼所感知到的正常的灰阶,亮度要增加一倍(比如从0.1到0.2)你才会感觉比原来变亮了一倍.

  • 第二行灰阶显示出的才是物理世界真实的亮度,但是由于这与我们的眼睛感知亮度不完全一致(对比较暗的颜色变化更敏感),所以它看起来有差异。

若我们采用 8bit 线性编码,即编码值与其代表的亮度线性对应。那么在 0~255 的编码范围内,由于亮色和暗色均匀分布的话,那亮色部分就会精度过剩而暗色部分就会精度不足。如何解决这个问题?进行 Gamma 矫正。

Gamma校正

从字面上看,Gamma指的是指数运算中的指数。比方说 a^{2.2}Gamma 指的就是2.2这个数字。计算机图形学中颜色值,特别是 RGBA 值都是用0到1的浮点数表示。颜色的 Gamma 矫正本质上就是一系列介于0到1之间的浮点数进行指数运算,通常来说这个数字是2.2。

设编码值为 C,亮度为 I,则两者的关系为:C = I^{\frac{1}{\gamma}}具体见下图,横轴为亮度I,纵轴为编码C。

5.png

上图中的红色虚线就是图像的 Gamma 矫正曲线,一般都是2.2的倒数,0.454545..。观察矫正以后的曲线,原来0到0.5的区间被扩展到了0到~0.73,相应的大于0.5的区间被压缩到只有原来的一半左右。这样通过一次简单的计算,我们达到了不增加数据量的前提下提高可辨识精度的目的。

tip:存储在你硬盘上的图像或视频,其颜色编码都是矫正过的

现在我们知道了对图像进行 Gamma 矫正的本质上的原因——我们的眼睛。那么为什么是2.2的倒数?因为老式的 CRT 显示器(阴极射线管显示器)。

阴极管显示器有一个物理特性——输入值和输出值呈现指数关系,这个指数是2.2。也就是说,当你输入0.5的亮度,在屏幕上得到的不是0.5,而是 0.5^{2.2} \approx 0.218

上图中的红色实现就是CRT 显示器的 Gamma 曲线。当我们在 CRT 显示器上显示一张 Gamma 矫正过的图像,也就是上面的那根曲线,会发生什么事情?我们带入公式计算一下:

实际显示亮度 = 编码值C^{2.2}

编码值C = 亮度I^{\frac{1}{\gamma}}

化简:

实际显示亮度 = 亮度I^{\frac{2.2}{\gamma}}

带入\gamma = 2.2

可得

实际显示亮度 = I

就会获得所期望的线性图像——中间那根灰色的直线。

事实上我们之所以选择2.2的倒数进行图像矫正,那是为了迎合 CRT 显示器的特性。如果显示器的 Gamma 值不是2.2,那么图像矫正 Gamma 值也需要相应变更,比方说早年的 Mac 操作系统都是使用1.8的 Gamma 值。

现代的 LCD 显示器已经不再具有这个特性,但是生产厂商仍旧会加入模拟 Gamma 曲线的硬件功能,也就是说所有显示器都期待软件输出 Gamma 矫正过的图像。

下图是一张完整的照片颜色处理的过程:

6.png

Gamma 矫正的确是一个隐藏的比较深的问题,别说普通人,长久以来游戏界也一直没有重视。计算机图形学中的算法普遍需要输入值处于线性空间,而读入的图像却都是矫正过的——处于 Gamma 空间。最终得到的计算结果自然也是不正确的。

由于亮度取值范围为0~1,经过显示器2.2次幂计算后值会变小,会显示的更暗一些。出于这个原因,美术师通常将光照值设置得比本来更亮一些,如果不是这样,在线性空间里计算出来的光照就会不正确。

因为所有中间亮度都是线性空间计算出来的(计算的时候假设Gamma为1)监视器显以后,实际上都会不正确。当使用更高级的光照算法时,这个问题会变得越来越明显,你可以看看下图:

7.png

设备颜色空间

通常来说,颜色受限于显示设备的显示性能,无法覆盖所有人眼可视的色域,所以设备会有自己可显示的颜色空间。拿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];
image.png

上面的例子,在iphone14真机上可以看到两种颜色是不同的,但是在模拟器上,则两种颜色是相同的。同时,如果在真机上进行截屏或屏幕录制,无法录制出不同的颜色。

这是因为iPhone14屏幕支持的色域更广,设备颜色空间支持展示这种颜色。而模拟器上,使用的显示器的颜色空间为sRGB,不支持展示更广的颜色空间。同时,当我们截图或屏幕录制的时候,相应格式也会限制颜色空间,这里可以看到,我们的截图有一个颜色描述文件 sRGB IEC61966-2.1,即描述我们当前图片中的颜色空间是哪个,显示器要应该要如何显示图片。

颜色差异和对比度

颜色差异(英语:Color difference),亦称颜色距离,是色彩学上的一个关注点。它量化了一个概念。在未量化之前,人们只能用形容词来大概描述这个概念,这使得对颜色要求严格的工作者们很不方便。颜色差异可以通过色彩空间内的欧氏距离简单计算得出,也可以使用国际照明委员会较为复杂、均匀的人类知觉公式计算。

欧式距离

很多日常使用的“颜色差异”,是直接通过在一个“设备无关”的色彩空间里,进行欧氏距离的计算得到的。给定一个RGB(红绿蓝)的色彩空间,最简单的差异计算方式就是在这个三维空间里求两个点间的距离:

\Delta E = \sqrt[2]{{(R_2-R_1)}^2 + {(G_2-G_1)}^2 + {(B_2-B_1)}^2}

有不少人尝试将RGB三值加上权重,希望可以让得到的结果更加符合人类感官。一种做法是使用2、4、3:

\Delta E = \sqrt[2]{{(R_2-R_1)}^2*2 + {(G_2-G_1)}^2*4 + {(B_2-B_1)}^2*3}

CIEDE2000

后续由于考虑到人眼对颜色的感知非均匀,国际照明委员会不断更新更符合人员的色差计算公式,通过添加各种权重去拟合人眼感知。

image.png

对比度(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高但实际不可读)
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容