最近买了一台打印机，发现卧槽怎么打出来的效果和屏幕上看到的不太一样。屏幕上看的纹理丰富的暗部，打到纸上就变成了一片黑。有一些艳丽的橙色，在纸上看上去就像黄色一样。调研了一下才知道，要想让两台设备的颜色统一，就需要做一个步骤叫做色彩管理。换言之，色彩管理就是让设备A，比如[数码相机](/yage.ai/lens-chromatic-aberration.html)上拍的图片，和设备B，比如显示器上显示的图片，看起来尽可能的一样。

要想做到这一点首先要理解色彩是什么。从物理的角度来说，光在物体上反射以后进入我们的眼睛/传感器被捕获。不同的单色光会给眼睛不同的刺激，所以就有了不同的色彩。但我们平时的主光源阳光涵盖了整个可见光的波段，被反射以后出来的光可不是单色光，每一个波长的光都有自己的强度。所以从物理的角度来说，要想精确的描述色彩的话，需要用一个无穷维的向量——一个波长-能量直方图才可以。

但从工程的角度来说这显然是不可能的：不可能给每个像素配备无限多个传感器来记录不同波长的亮度，从而精准地还原色彩。相反，彩色数码相机大都只有红绿蓝三种颜色的传感器（准确的说是滤光片）。为什么选这三种颜色呢？这是因为人眼里有三种感光细胞（视锥细胞），每种细胞对整个波段都有不同的响应曲线（见图1）。而这三种细胞的响应峰值就在蓝色，绿色和红色上。所以这其实做了个贪心算法，只记录我们人眼最关注的那部分波长。图2是尼康D80相机色彩滤波片的响应函数，可以看到和人眼的响应有相似之处也有差异（注意横坐标轴的范围不同，所以看上去有点偏移）。

![Human eyes' response curve.](https://yage.ai/images/color-management-1.jpg)

![Camera's response curve.](https://yage.ai/images/color-management-2.png)

所以人眼和彩色相机对色彩的记录本质上是把一个无穷维的向量在三个无穷维的基（数码相机的颜色滤波片的响应函数）上做了内积，然后把这个三维的结果记录下来而已。所以很显然，这种表示方法无法表达所有可能的颜色。虽然人类视觉细胞的响应函数不是正交的，但我们强行用这三个响应函数做基张成了一个三维线性空间。因为光的能量一定是非负数，所以这个空间只有第一象限的部分。这就是LMS色彩空间（LMS分别对应Long, Medium和Short wavelength视锥细胞）。可能是为了表达红绿蓝三个基彼此并非正交，在色彩空间的图示中，我们很少把坐标轴画成一个立方体的角，而是更像一个三角锥形。

这种把整个无穷维的色彩的空间投影到几个响应函数上，所得到的线性空间，就叫色彩空间。那么除了用视锥细胞的响应函数以外，这些基向量也有其他的选择。比如XYZ色彩空间。这是让人类被试员在一个2度的视角上测量出来的对不同色彩的敏感性。XYZ色彩空间的基如图3所示，因为原理类似，所以它和LMS相当接近。

![XYZ color space.](https://yage.ai/images/color-management-3.png)

上述的色彩空间的优势主要是贴近人眼的原理，但也有劣势，就是和人类对色彩的认知联系不太直观。比如我们平时说的"你这个照片太艳了"，很难直观地在LMS/XYZ色彩空间上进行调整。所以人们又引入了另一种色彩空间叫HSV。这个空间也有三组基，分别是色调(Hue)，饱和度(Saturation)和亮度(Value)。这下就好办了，如果照片太亮，就只要缩小亮度。照片不够艳丽，就调高饱和度就好。更重要的一点是，这个色彩空间把亮度和色彩给隔离开了，这和设备的特性也是相符的。比如一个显示器，想提高亮度直接把灯管弄亮点就好，但要想显示一个之前没有的颜色可就难了。所以这个色彩空间也更多地用在对设备能力的描述上。

![HSV color space.](https://yage.ai/images/color-management-4.png)

Hue的意思是色调，就像我们平时说的红橙黄绿青蓝紫，转了一圈紫色和红色看上去很接近又接上了。因为它的这个周期性的特性，HSV空间里的Hue和Saturation一般做成极坐标的形式。Hue表示角度，Saturation表示距离。第三维亮度在设备相关的探讨中就暂不考虑了。图4就是对把HSV空间的Hue和Saturation画出来的形式。注意这不是一个正圆。为什么呢？因为第一前面说过，光的能量不可能是负数，所以整个有意义的坐标会被单色光给框住，就是图中380, 460一直到700这坨。就是单色光在这个坐标系中的位置。第二人类毕竟看不见红外线紫外线。所以这个单色光圈成在图形的下部就像被把刀砍了一样——波长到此截止。中间围着的就是所有可能的（亮度无关的）颜色了。

所以到此为止设备之间的校色好像就齐活了。A设备把你能显示的所有颜色在HS空间上的投影画出来，B设备也画出来。两边把不能都显示的颜色做一个映射。搞定。。可是等等，有个很严重的问题。LMS，XYZ之类的坐标原点非常容易确定，就是纯黑色，不同显示设备之间可以通用——大不了把灯一关肯定是黑色。可是HSV坐标的原点是饱和度为0的颜色，也就是白色。这个就不能通用了。所以在不同设备间还需要做一个额外的校准，叫做白点校准。保证设备A的白色和设备B的白色是一个色彩。而这个校准就需要依赖XYZ这样的绝对坐标系来完成了。所以一般色彩的校准需要一个在色彩空间上平移的白点校准，再加上一个放缩的色彩校准。这样就可以把设备A的一种颜色的HSV表示转化成设备B的HSV表示，从而在设备B上也能准确的渲染出来了。而每种具体色彩可以用一个射影变换来描述。
