根据三原色原理,技术人员创造了RGB模式(R:Red G:Green B:Blue),并用三原色按不同比例混合形成高达1600万种颜色。在RGB 颜色模式,颜色由红色,绿色,和蓝色各成分强度的三个数值表示。从极小值0到最大值255,当所有颜色,都在最低值被显示的颜色将是黑色,当所有颜色都在他们的最大值被显示的颜色将是白色。
1.2.1 HSV
HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型。这个模型中颜色的参数分别是:色调(H)、饱和度(S)、明度(V)。
(1)色调Hue用角度度量,取值范围为0°~360°;
(2)饱和度S表示颜色接近光谱色的程度,一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0~1,值越大,颜色越饱和;
(3)明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0(黑)到1(白)。
1.2.2 HSL
HSL(Hue,Saturation,Lightness)色彩模式是工业界的一种颜色标准,是通过对色相[0°,360°]、饱和度[0,1]、亮度[0,1]三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,这个标准几乎包括了人类视力所能感知的所有颜色,是迄今运用最广的颜色系统之一。它的三维表示为一双棱锥。
1.2.3 比较
在软件中,通常以一个线性或圆形色相选择器和在其中为选定的色相选取饱和度和明度/亮度的一个二维区域(通常为方形或三角形)形式提供给用户基于色相的颜色模型(HSV 或 HSL)。通过这种表示,在 HSV 和 HSL 之间的区别就无关紧要了。但是很多程序还允许你通过线性滑块或数值录入框来选择颜色的明度/亮度,而对于这些控件通常使用要么 HSL 要么 HSV(而非二者)。HSV 传统上更常用。
CIE选择的X,Y和Z基色具有如下性质:
1、 所有的X,Y和Z值都是正的,匹配光谱颜色时不需要一种负值的基色;
2、用Y值表示人眼对亮度(luminance)的响应;
3、 如同RGB模型,X,Y和Z是相加基色。因此,每一种颜色都可以表示成X,Y和Z的混合。
定义CIE xyY颜色空间的根据是,对于一种给定的颜色,如果增加它的明度,每一种基色的光通量也要按比例增加,这样才能匹配这种颜色。因此,当颜色点离开原点(X=0,Y=0,Z=0)时,X:Y:Z的比值保持不变。此外,由于色度值仅与波长(色调)和纯度有关,而与总的辐射能量无关,因此在计算颜色的色度时,把X,Y和Z值相对于总的辐射能量=(X+Y+Z)进行归一化,并只需考虑它们的相对比例,因此,x,y,z称为三基色相对系数,于是配色方程可规格化为x+y+z=1。由于三个相对系数x,y,z之和恒为1,这就相当于把XYZ颜色锥体投影到X+Y+Z=1的平面上。
由于z可以从x+y+z=1导出,因此通常不考虑z,而用另外两个系数x和y表示颜色,并绘制以x和y为坐标的二维图形。这就相当于把X+Y+Z=1平面投射到(X,Y)平面,也就是Z=0的平面,这就是CIE xyY色度图。
在CIE xyY系统中,根据颜色坐标(x,y)可确定z,但不能仅从x和y导出三种基色刺激值X,Y和Z,还需要使用携带亮度信息的Y,其值与XYZ中的Y刺激值一致。
当在光照条件下判断不同颜色的相对亮度(亮度)时,人们倾向于将光谱的绿色部分内的光感知为比相等功率的红色或蓝色光更亮。因此,描述不同波长的感知亮度的发光度函数大致类似于M锥的光谱灵敏度。
CIE模型通过将Y设置为亮度来利用这一事实。 Z是准等于蓝色或S锥响应,X是选择为非负的响应曲线的混合。因此,XYZ三刺激值类似于但不同于人眼的LMS锥形响应。将Y设置为亮度具有有用的结果,对于任何给定的Y值,XZ平面将包含该亮度处的所有可能的色度。
三刺激值X,Y和Z的单位通常是任意选择的,因此Y = 1或Y = 100是彩色显示器支持的最亮的白色。然后可以使用标准光源推断X和Z的相应白点值。
转换公式如下:
1.HSL→RGB
Hue参数,饱和度
,亮度
首先确定色相:
然后 我们可以在RGB立方体的底部三个面上找到一个点(R1,G1,B1),具有与我们的颜色相同的色调和色度(使用中间值X作为该颜色的第二大组件):
最后,我们可以通过向每个组件添加相同的量来找到R,G和B,以匹配亮度:
2. RGB→HSL
1. HSV→RGB
Hue参数,饱和度 ,明度值,HSV与HSL原理相同。
(1)
2. RGB→HSV
方法同HSL,见RGB→HSL。
目前没有查到直接转换的公式,通常运用的是以上三种转换,可通过RGB作为中间者进行链式转换。
[1]https://en.wikipedia.org/wiki/HSL_and_HSV#HSL
[2]https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space
[3]《Gernot Hoffmann CIE Color Space》下载地址:http://docs-hoffmann.de/ciexyz29082000.pdf
Silabs 灯设备颜色显示参考代码(可反向去写APP显示的代码):
void emberAfPluginColorControlServerComputePwmFromXyCallback(uint8_t endpoint)
{
uint16_t currentX, currentY;
uint8_t onOff, currentLevel;
uint32_t scratch;
uint32_t X32, Y32, Z32;
int32_t R32, G32, B32;
uint16_t rDrive, gDrive, bDrive;
// read the attributes from the attribute table.
emberAfReadServerAttribute(endpoint,
ZCL_COLOR_CONTROL_CLUSTER_ID,
ZCL_COLOR_CONTROL_CURRENT_X_ATTRIBUTE_ID,
(uint8_t *)¤tX,
sizeof(currentX));
emberAfReadServerAttribute(endpoint,
ZCL_COLOR_CONTROL_CLUSTER_ID,
ZCL_COLOR_CONTROL_CURRENT_Y_ATTRIBUTE_ID,
(uint8_t *)¤tY,
sizeof(currentY));
readFilteredOnOffAndLevel(&onOff, ¤tLevel);
if (onOff == 0 || currentLevel == 0) {
driveWRGB(0, 0, 0, 0);
return;
}
// compute x, y, z
X32 = currentX;
Y32 = currentY;
scratch = X32 + Y32;
if (scratch > 65536l) {
emberAfAppPrintln("X and Y are too big");
return;
}
Z32 = 65536l - (X32 + Y32);
// now we can compute the RGB values in 65,536,000
// these are well-known constants but are documented at:
// http://docs-hoffmann.de/ciexyz29082000.pdf which came from
// "Digital Color Management, Giorgianni+Madden
R32 = (X32 * 2365) - (Y32 * 897) - (Z32 * 468);
G32 = ((Y32 * 1426) + (Z32 * 89)) - (X32 * 515);
B32 = (X32 * 5) + (Z32 * 1009) - (Y32 * 14);
// Note: it is possible the above algorithm will create a negative drive
// value. We need to check for that and set it to zero.
if (R32 < 0) {
R32 = 0;
}
if (G32 < 0) {
G32 = 0;
}
if (B32 < 0) {
B32 = 0;
}
R32 = R32 / 65536;
R32 = R32 * maxPwmDrive;
R32 = R32 / 1000;
G32 = G32 / 65536;
G32 = G32 * maxPwmDrive;
G32 = G32 / 1000;
B32 = B32 / 65536;
B32 = B32 * maxPwmDrive;
B32 = B32 / 1000;
// limits checking. Also, handle level.
R32 *= currentLevel;
R32 /= 256;
rDrive = (uint16_t) R32;
if (rDrive > maxPwmDrive) {
rDrive = maxPwmDrive;
}
G32 *= currentLevel;
G32 /= 256;
gDrive = (uint16_t) G32;
if (gDrive > maxPwmDrive) {
gDrive = maxPwmDrive;
}
B32 *= currentLevel;
B32 /= 256;
bDrive = (uint16_t) B32;
if (bDrive > maxPwmDrive) {
bDrive = maxPwmDrive;
}
driveWRGB(0, rDrive, gDrive, bDrive);
}
void emberAfPluginColorControlServerComputePwmFromHsvCallback(uint8_t endpoint)
{
uint8_t hue, saturation;
uint8_t onOff, currentLevel;
uint32_t min32, hue32, delta32, sat32, level32;
uint32_t R32, G32, B32;
uint16_t rDrive, gDrive, bDrive;
emberAfReadServerAttribute(endpoint,
ZCL_COLOR_CONTROL_CLUSTER_ID,
ZCL_COLOR_CONTROL_CURRENT_HUE_ATTRIBUTE_ID,
(uint8_t *)&hue,
sizeof(hue));
emberAfReadServerAttribute(endpoint,
ZCL_COLOR_CONTROL_CLUSTER_ID,
ZCL_COLOR_CONTROL_CURRENT_SATURATION_ATTRIBUTE_ID,
(uint8_t *)&saturation,
sizeof(saturation));
readFilteredOnOffAndLevel(&onOff, ¤tLevel);
if (onOff == 0 || currentLevel == 0) {
driveWRGB(0, 0, 0, 0);
return;
}
// algorithm taken from wikipedia
// http://en.wikipedia.org/wiki/CIE_1931_color_space for more details
// note: hue and saturation are 0..254. level (i.e. value for HSV) is
// 0..255. But most of these computations assume 0..1 for saturation and
// value, and 0..360 for hue. This will be a little tricky to compute RGB
// using fixed point math and not lose any bits of significance.
// first switch to 32 bit mode.
level32 = (uint32_t) currentLevel;
sat32 = (uint32_t) saturation;
min32 = level32 * (254 - sat32);
min32 = min32 / 254;
delta32 = level32 - min32;
hue32 = ((uint32_t) hue); // need to map it to 0..6. really is 0..254.
// formula is X = C * { 1 - ( H mod2 - 1) }. Becuase the nubmers don't line
// up, we need to do this with if's.
// The ranges are 0, 42, 84, 127, 169, 211, 254.
if (hue < 43) {
R32 = level32; // 0..254
// convert 0..42 to 0..delta32
G32 = hue32 * delta32;
G32 = G32 / 42;
G32 = G32 + min32;
B32 = min32;
} else if (hue < 85) {
hue32 -= 42;
// convert 0..42 to delta32..0
R32 = (42 - hue32) * delta32;
R32 = R32 / 42;
R32 = R32 + min32;
G32 = level32;
B32 = min32;
} else if (hue < 128) {
hue32 -= 84;
R32 = min32;
G32 = level32;
// convert 0..43 to 0..delta32
B32 = hue32 * delta32;
B32 = B32 / 43;
B32 = B32 + min32;
} else if (hue < 170) {
hue32 -= 127;
R32 = min32;
// convert 0..42 to delta32..0
G32 = (42 - hue32) * delta32;
G32 = G32 / 42;
G32 = G32 + min32;
B32 = level32;
} else if (hue < 212) {
hue32 -= 169;
// convert 0..42 to 0..delta32
R32 = hue32 * delta32;
R32 = R32 / 42;
R32 = R32 + min32;
G32 = min32;
B32 = level32;
} else { //hue is 212..254
hue32 -= 211;
R32 = level32;
G32 = min32;
// convert 0..43 to delta32..0
B32 = (43 - hue32) * delta32;
B32 = B32 / 42;
B32 = B32 + min32;
}
R32 = R32 * maxPwmDrive;
G32 = G32 * maxPwmDrive;
B32 = B32 * maxPwmDrive;
R32 = R32 / 254;
G32 = G32 / 254;
B32 = B32 / 254;
rDrive = (uint16_t) R32;
gDrive = (uint16_t) G32;
bDrive = (uint16_t) B32;
driveWRGB(0, rDrive, gDrive, bDrive);
}
HSV、HSL参考色板:
HSL:
HSV: