HSI:色相(hue),饱和度(saturation),亮度(Intensity),这种模型完全对应于RGB模型转化而来,转化也有严格的公式推导得到,网上提供了几种转化公式的表示方法:
HSV:色相(hue),饱和度(saturation),明度(value),也称HSB(brightness)。
HSL:色相(hue),饱和度(saturation),亮度(luminance)。
上面这两种模型各个意义也不完全一样,它们的意义对应于它们各自的建模方式。其中HSV/HSB来自于Lab建模方式并考虑了人眼的影响之后得到的。
通用的RGB转HSV/HSB公式如下:详细的原理还未弄清楚
R,G,B的取值范围是[0, Cmax],未作归一化的Cmax的值为255。Chigh = max(R,G,B),Clow = min(R,G,B),Crng = Chigh - Clow
饱和度:
if(Chigh > 0) S = Crng / Chigh else S = 0
明度:
V = Chigh / Cmax
色调:
if(Crng == 0)此时饱和度S == 0,为灰度图,色调因此无定义。
else
先将各分量归一化:R‘ = (Chigh - R) / Crng G‘ = (Chigh - G) / Crng B‘ = (Chigh - B) / Crng
基于原始颜色分量中的最大值,计算出一个初步的色调H‘:
if(R = Chigh) H‘ = B‘ - G‘ ; else if(G = Chigh) H‘ = R‘ - B‘ + 2; else if(B = Chigh) H‘ = G‘ - R‘ + 4;
由上式可知H‘的取值区间为[-1, 5],通过将其归一化到区间[0, 1]得到最终的色调值:
if(H‘ < 0) H = 1/6 * (H‘ + 6) else H = 1/6 * H‘
三个分量值都位于区间[0, 1]。
代码如下:来自于JDK java.awt.Color类
public static float[] RGBtoHSB(int r, int g, int b, float[] hsbvals) {
float hue, saturation, brightness;
if (hsbvals == null) {
hsbvals = new float[3];
}
int cmax = (r > g) ? r : g;
if (b > cmax) cmax = b;
int cmin = (r < g) ? r : g;
if (b < cmin) cmin = b;
brightness = ((float) cmax) / 255.0f;
if (cmax != 0)
saturation = ((float) (cmax - cmin)) / ((float) cmax);
else
saturation = 0;
if (saturation == 0)
hue = 0;
else {
float redc = ((float) (cmax - r)) / ((float) (cmax - cmin));
float greenc = ((float) (cmax - g)) / ((float) (cmax - cmin));
float bluec = ((float) (cmax - b)) / ((float) (cmax - cmin));
if (r == cmax)
hue = bluec - greenc;
else if (g == cmax)
hue = 2.0f + redc - bluec;
else
hue = 4.0f + greenc - redc;
hue = hue / 6.0f;
if (hue < 0)
hue = hue + 1.0f;
}
hsbvals[0] = hue;
hsbvals[1] = saturation;
hsbvals[2] = brightness;
return hsbvals;
}
HSB/HSV转RGB的介绍不给出,直接给出java.awt.Color类中的转换代码
public static int HSBtoRGB(float hue, float saturation, float brightness) { int r = 0, g = 0, b = 0; if (saturation == 0) { r = g = b = (int) (brightness * 255.0f + 0.5f); } else { float h = (hue - (float)Math.floor(hue)) * 6.0f; float f = h - (float)java.lang.Math.floor(h); float p = brightness * (1.0f - saturation); float q = brightness * (1.0f - saturation * f); float t = brightness * (1.0f - (saturation * (1.0f - f))); switch ((int) h) { case 0: r = (int) (brightness * 255.0f + 0.5f); g = (int) (t * 255.0f + 0.5f); b = (int) (p * 255.0f + 0.5f); break; case 1: r = (int) (q * 255.0f + 0.5f); g = (int) (brightness * 255.0f + 0.5f); b = (int) (p * 255.0f + 0.5f); break; case 2: r = (int) (p * 255.0f + 0.5f); g = (int) (brightness * 255.0f + 0.5f); b = (int) (t * 255.0f + 0.5f); break; case 3: r = (int) (p * 255.0f + 0.5f); g = (int) (q * 255.0f + 0.5f); b = (int) (brightness * 255.0f + 0.5f); break; case 4: r = (int) (t * 255.0f + 0.5f); g = (int) (p * 255.0f + 0.5f); b = (int) (brightness * 255.0f + 0.5f); break; case 5: r = (int) (brightness * 255.0f + 0.5f); g = (int) (p * 255.0f + 0.5f); b = (int) (q * 255.0f + 0.5f); break; } } return 0xff000000 | (r << 16) | (g << 8) | (b << 0); }
RGB转HSL:
色调:
同HSV
亮度:
L = (Chigh + Clow) / 2
饱和度:
if(L == 0) S = 0; else if( 0<L<=0.5 ) S = 0.5 * Crng / L; else if(0.5 < L < 1) S = 0.5 * Crng / (1 - L); else if(L == 1) S = 0;
static float[] RGBtoHLS(float R, float G, float B){ float cHi = Math.max(R, Math.max(G, B)); float cLo = Math.min(R, Math.min(G, B)); float cRng = cHi - cLo; //计算亮度L float L = (cHi + cLo) / 2; //计算饱和度S float S = 0; if(0 < L && L < 1){ float d = (L <= 0.5f) ? L : (1 - L); S = 0.5f * cRng / d; } //计算色调H float H = 0; if(cRng > 0){ float rr = (float)((cHi - R) / cRng); float gg = (float)((cHi - G) / cRng); float bb = (float)((cHi - B) / cRng); float hh = 0; if(R == cHi)hh = bb - gg; else if(G == cHi)hh = rr - bb + 2; else if(B == cHi)hh = gg - rr + 4; if(hh < 0)hh = hh + 6; H = hh / 6; } return new float[]{H, L, S}; }
HSL转RGB代码如下:
static float[] HLStoRGB(float H, float S, float L){ //假设HSL在[0, 1]区间 float R = 0; float G = 0; float B = 0; if(L <= 0)R = G = B = 0; else if(L >= 1) R = G = B = 1; else{ float hh = (6 * H) % 6; int c1 = (int)hh; float c2 = hh - c1; float d = (L <= 0.5f) ? (S * L) : (S * (1 - L)); float w = L + d; float x = L - d; float y = w - (w - x) * c2; float z = w + (w - x) * c2; switch(c1){ case 0: R = w; G = z; B = x; break; case 1: R = y; G = w; B = x; break; case 2: R = x; G = w; B = z; break; case 3: R = x; G = y; B = w; break; case 4: R = z; G = x; B = w; break; case 5: R = w; G = x; B = y; break; } } return new float[]{R, G, B}; }