1 顔色模型
1.1 RGB
根據三原色原理,技術人員創造了RGB模式(R:Red G:Green B:Blue),并用三原色按不同比例混合形成高達1600萬種顔色。在RGB 顔色模式,顔色由紅色,綠色,和藍色各成分強度的三個數值表示。從極小值0到最大值255,當所有顔色,都在最低值被顯示的顔色将是黑色,當所有顔色都在他們的最大值被顯示的顔色将是白色。
1.2 HSV和HSL
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 傳統上更常用。
1.3 CIE xyY模型
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刺激值一緻。
2 方案選擇及轉換公式
2.1 xyY RGB
當在光照條件下判斷不同顔色的相對亮度(亮度)時,人們傾向于将光譜的綠色部分内的光感覺為比相等功率的紅色或藍色光更亮。是以,描述不同波長的感覺亮度的發光度函數大緻類似于M錐的光譜靈敏度。
CIE模型通過将Y設定為亮度來利用這一事實。 Z是準等于藍色或S錐響應,X是選擇為非負的響應曲線的混合。是以,XYZ三刺激值類似于但不同于人眼的LMS錐形響應。将Y設定為亮度具有有用的結果,對于任何給定的Y值,XZ平面将包含該亮度處的所有可能的色度。
三刺激值X,Y和Z的機關通常是任意選擇的,是以Y = 1或Y = 100是彩色顯示器支援的最亮的白色。然後可以使用标準光源推斷X和Z的相應白點值。
轉換公式如下:
2.2 HSL RGB
1.HSL→RGB
Hue參數
,飽和度
,亮度
首先确定色相:
然後 我們可以在RGB立方體的底部三個面上找到一個點(R1,G1,B1),具有與我們的顔色相同的色調和色度(使用中間值X作為該顔色的第二大元件):
最後,我們可以通過向每個元件添加相同的量來找到R,G和B,以比對亮度:
2. RGB→HSL
2.3 HSV RGB
1. HSV→RGB
Hue參數,飽和度 ,明度值,HSV與HSL原理相同。
(1)
(2)
(3)
2. RGB→HSV
方法同HSL,見RGB→HSL。
2.4 xyY HSV(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
附件1
Silabs 燈裝置顔色顯示參考代碼(可反向去寫APP顯示的代碼):
- XY→RGB
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);
}
- HSL→RGB
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);
}
附件2
HSV、HSL參考色闆:
HSL:
HSV: