C++ 将 RGB 转换为 HSV 并将 HSV 转换为 RGB 的算法,范围为 0-255
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3018313/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both
提问by jmasterx
I am looking for color space converter from RGB to HSV, specifically for the range 0 to 255 for both color spaces.
我正在寻找从 RGB 到 HSV 的颜色空间转换器,特别是对于两个颜色空间的 0 到 255 范围。
回答by David H
I've used these for a long time - no idea where they came from at this point... Note that the inputs and outputs, except for the angle in degrees, are in the range of 0 to 1.0.
我已经使用这些很长时间了 - 现在不知道它们来自哪里......请注意,输入和输出,除了以度为单位的角度,都在 0 到 1.0 的范围内。
NOTE: this code does no real sanity checking on inputs. Proceed with caution!
注意:此代码不会对输入进行真正的健全性检查。谨慎行事!
typedef struct {
double r; // a fraction between 0 and 1
double g; // a fraction between 0 and 1
double b; // a fraction between 0 and 1
} rgb;
typedef struct {
double h; // angle in degrees
double s; // a fraction between 0 and 1
double v; // a fraction between 0 and 1
} hsv;
static hsv rgb2hsv(rgb in);
static rgb hsv2rgb(hsv in);
hsv rgb2hsv(rgb in)
{
hsv out;
double min, max, delta;
min = in.r < in.g ? in.r : in.g;
min = min < in.b ? min : in.b;
max = in.r > in.g ? in.r : in.g;
max = max > in.b ? max : in.b;
out.v = max; // v
delta = max - min;
if (delta < 0.00001)
{
out.s = 0;
out.h = 0; // undefined, maybe nan?
return out;
}
if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
out.s = (delta / max); // s
} else {
// if max is 0, then r = g = b = 0
// s = 0, h is undefined
out.s = 0.0;
out.h = NAN; // its now undefined
return out;
}
if( in.r >= max ) // > is bogus, just keeps compilor happy
out.h = ( in.g - in.b ) / delta; // between yellow & magenta
else
if( in.g >= max )
out.h = 2.0 + ( in.b - in.r ) / delta; // between cyan & yellow
else
out.h = 4.0 + ( in.r - in.g ) / delta; // between magenta & cyan
out.h *= 60.0; // degrees
if( out.h < 0.0 )
out.h += 360.0;
return out;
}
rgb hsv2rgb(hsv in)
{
double hh, p, q, t, ff;
long i;
rgb out;
if(in.s <= 0.0) { // < is bogus, just shuts up warnings
out.r = in.v;
out.g = in.v;
out.b = in.v;
return out;
}
hh = in.h;
if(hh >= 360.0) hh = 0.0;
hh /= 60.0;
i = (long)hh;
ff = hh - i;
p = in.v * (1.0 - in.s);
q = in.v * (1.0 - (in.s * ff));
t = in.v * (1.0 - (in.s * (1.0 - ff)));
switch(i) {
case 0:
out.r = in.v;
out.g = t;
out.b = p;
break;
case 1:
out.r = q;
out.g = in.v;
out.b = p;
break;
case 2:
out.r = p;
out.g = in.v;
out.b = t;
break;
case 3:
out.r = p;
out.g = q;
out.b = in.v;
break;
case 4:
out.r = t;
out.g = p;
out.b = in.v;
break;
case 5:
default:
out.r = in.v;
out.g = p;
out.b = q;
break;
}
return out;
}
回答by Leszek Szary
You can also try this code without floats (faster but less accurate):
您也可以尝试不使用浮点数的代码(更快但不太准确):
typedef struct RgbColor
{
unsigned char r;
unsigned char g;
unsigned char b;
} RgbColor;
typedef struct HsvColor
{
unsigned char h;
unsigned char s;
unsigned char v;
} HsvColor;
RgbColor HsvToRgb(HsvColor hsv)
{
RgbColor rgb;
unsigned char region, remainder, p, q, t;
if (hsv.s == 0)
{
rgb.r = hsv.v;
rgb.g = hsv.v;
rgb.b = hsv.v;
return rgb;
}
region = hsv.h / 43;
remainder = (hsv.h - (region * 43)) * 6;
p = (hsv.v * (255 - hsv.s)) >> 8;
q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
rgb.r = hsv.v; rgb.g = t; rgb.b = p;
break;
case 1:
rgb.r = q; rgb.g = hsv.v; rgb.b = p;
break;
case 2:
rgb.r = p; rgb.g = hsv.v; rgb.b = t;
break;
case 3:
rgb.r = p; rgb.g = q; rgb.b = hsv.v;
break;
case 4:
rgb.r = t; rgb.g = p; rgb.b = hsv.v;
break;
default:
rgb.r = hsv.v; rgb.g = p; rgb.b = q;
break;
}
return rgb;
}
HsvColor RgbToHsv(RgbColor rgb)
{
HsvColor hsv;
unsigned char rgbMin, rgbMax;
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
hsv.v = rgbMax;
if (hsv.v == 0)
{
hsv.h = 0;
hsv.s = 0;
return hsv;
}
hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
if (hsv.s == 0)
{
hsv.h = 0;
return hsv;
}
if (rgbMax == rgb.r)
hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
else if (rgbMax == rgb.g)
hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
else
hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
return hsv;
}
Note that this algorithm uses 0-255
as it's range (not 0-360
) as that was requested by the author of this question.
请注意,该算法使用0-255
的0-360
是本问题作者所要求的范围(不是)。
回答by Patapom
I wrote this in HLSL for our rendering engine, it has no conditions in it:
我在 HLSL 中为我们的渲染引擎编写了这个,它没有条件:
float3 HSV2RGB( float3 _HSV )
{
_HSV.x = fmod( 100.0 + _HSV.x, 1.0 ); // Ensure [0,1[
float HueSlice = 6.0 * _HSV.x; // In [0,6[
float HueSliceInteger = floor( HueSlice );
float HueSliceInterpolant = HueSlice - HueSliceInteger; // In [0,1[ for each hue slice
float3 TempRGB = float3( _HSV.z * (1.0 - _HSV.y),
_HSV.z * (1.0 - _HSV.y * HueSliceInterpolant),
_HSV.z * (1.0 - _HSV.y * (1.0 - HueSliceInterpolant)) );
// The idea here to avoid conditions is to notice that the conversion code can be rewritten:
// if ( var_i == 0 ) { R = V ; G = TempRGB.z ; B = TempRGB.x }
// else if ( var_i == 2 ) { R = TempRGB.x ; G = V ; B = TempRGB.z }
// else if ( var_i == 4 ) { R = TempRGB.z ; G = TempRGB.x ; B = V }
//
// else if ( var_i == 1 ) { R = TempRGB.y ; G = V ; B = TempRGB.x }
// else if ( var_i == 3 ) { R = TempRGB.x ; G = TempRGB.y ; B = V }
// else if ( var_i == 5 ) { R = V ; G = TempRGB.x ; B = TempRGB.y }
//
// This shows several things:
// . A separation between even and odd slices
// . If slices (0,2,4) and (1,3,5) can be rewritten as basically being slices (0,1,2) then
// the operation simply amounts to performing a "rotate right" on the RGB components
// . The base value to rotate is either (V, B, R) for even slices or (G, V, R) for odd slices
//
float IsOddSlice = fmod( HueSliceInteger, 2.0 ); // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
float ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
float3 ScrollingRGBForEvenSlices = float3( _HSV.z, TempRGB.zx ); // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
float3 ScrollingRGBForOddSlices = float3( TempRGB.y, _HSV.z, TempRGB.x ); // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
float3 ScrollingRGB = lerp( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
float IsNotFirstSlice = saturate( ThreeSliceSelector ); // 1 if NOT the first slice (true for slices 1 and 2)
float IsNotSecondSlice = saturate( ThreeSliceSelector-1.0 ); // 1 if NOT the first or second slice (true only for slice 2)
return lerp( ScrollingRGB.xyz, lerp( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice ); // Make the RGB rotate right depending on final slice index
}
回答by com.prehensible
this should be on here: it works anyway. And it looks good compared to the above ones.
这应该在这里:无论如何它都有效。与上面的相比,它看起来不错。
hlsl code
hlsl代码
float3 Hue(float H)
{
half R = abs(H * 6 - 3) - 1;
half G = 2 - abs(H * 6 - 2);
half B = 2 - abs(H * 6 - 4);
return saturate(half3(R,G,B));
}
half4 HSVtoRGB(in half3 HSV)
{
return half4(((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z,1);
}
float3 is 16 bit precision vector3 data type, i.e. float3 hue() is returns a data type (x,y,z) e.g. (r,g,b), half is same with half precision, 8bit, a float4 is (r,g,b,a) 4 values.
float3 是 16 位精度 vector3 数据类型,即 float3 Hue() 返回数据类型 (x,y,z) 例如 (r,g,b),一半与一半精度相同,8 位,float4 是 (r, g,b,a) 4 个值。
回答by Geremia
Here's a C implementation based on Agoston's Computer Graphics and Geometric Modeling: Implementation and Algorithmsp. 304, with H∈ [0, 360] and S,V∈ [0, 1].
这是基于 Agoston 的计算机图形和几何建模的 C 实现:实现和算法p。304,H∈ [0, 360] 和S, V∈ [0, 1]。
#include <math.h>
typedef struct {
double r; // ∈ [0, 1]
double g; // ∈ [0, 1]
double b; // ∈ [0, 1]
} rgb;
typedef struct {
double h; // ∈ [0, 360]
double s; // ∈ [0, 1]
double v; // ∈ [0, 1]
} hsv;
rgb hsv2rgb(hsv HSV)
{
rgb RGB;
double H = HSV.h, S = HSV.s, V = HSV.v,
P, Q, T,
fract;
(H == 360.)?(H = 0.):(H /= 60.);
fract = H - floor(H);
P = V*(1. - S);
Q = V*(1. - S*fract);
T = V*(1. - S*(1. - fract));
if (0. <= H && H < 1.)
RGB = (rgb){.r = V, .g = T, .b = P};
else if (1. <= H && H < 2.)
RGB = (rgb){.r = Q, .g = V, .b = P};
else if (2. <= H && H < 3.)
RGB = (rgb){.r = P, .g = V, .b = T};
else if (3. <= H && H < 4.)
RGB = (rgb){.r = P, .g = Q, .b = V};
else if (4. <= H && H < 5.)
RGB = (rgb){.r = T, .g = P, .b = V};
else if (5. <= H && H < 6.)
RGB = (rgb){.r = V, .g = P, .b = Q};
else
RGB = (rgb){.r = 0., .g = 0., .b = 0.};
return RGB;
}
回答by Shonn
@fins's answer has an overflow issue on Arduio as you turn the saturation down. Here it is with some values converted to int to prevent that.
当您调低饱和度时,@fins 的回答在 Arduio 上存在溢出问题。这里有一些值转换为 int 以防止这种情况。
typedef struct RgbColor
{
unsigned char r;
unsigned char g;
unsigned char b;
} RgbColor;
typedef struct HsvColor
{
unsigned char h;
unsigned char s;
unsigned char v;
} HsvColor;
RgbColor HsvToRgb(HsvColor hsv)
{
RgbColor rgb;
unsigned char region, p, q, t;
unsigned int h, s, v, remainder;
if (hsv.s == 0)
{
rgb.r = hsv.v;
rgb.g = hsv.v;
rgb.b = hsv.v;
return rgb;
}
// converting to 16 bit to prevent overflow
h = hsv.h;
s = hsv.s;
v = hsv.v;
region = h / 43;
remainder = (h - (region * 43)) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
rgb.r = v;
rgb.g = t;
rgb.b = p;
break;
case 1:
rgb.r = q;
rgb.g = v;
rgb.b = p;
break;
case 2:
rgb.r = p;
rgb.g = v;
rgb.b = t;
break;
case 3:
rgb.r = p;
rgb.g = q;
rgb.b = v;
break;
case 4:
rgb.r = t;
rgb.g = p;
rgb.b = v;
break;
default:
rgb.r = v;
rgb.g = p;
rgb.b = q;
break;
}
return rgb;
}
HsvColor RgbToHsv(RgbColor rgb)
{
HsvColor hsv;
unsigned char rgbMin, rgbMax;
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
hsv.v = rgbMax;
if (hsv.v == 0)
{
hsv.h = 0;
hsv.s = 0;
return hsv;
}
hsv.s = 255 * ((long)(rgbMax - rgbMin)) / hsv.v;
if (hsv.s == 0)
{
hsv.h = 0;
return hsv;
}
if (rgbMax == rgb.r)
hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
else if (rgbMax == rgb.g)
hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
else
hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
return hsv;
}
回答by Trey Reynolds
This isn't C, but it's certainly does work. All the other methods I see here work by casing everything into parts of a hexagon, and approximating "angles" from that. By instead starting with a different equation using cosines, and solving for h s and v, you get a lot nicer relationship between hsv and rgb, and tweening becomes smoother (at the cost of it being way slower).
这不是 C,但它确实有效。我在这里看到的所有其他方法都是将所有东西都封装成六边形的一部分,然后从中估算出“角度”。通过使用余弦从不同的方程开始,并求解 hs 和 v,您可以在 hsv 和 rgb 之间获得更好的关系,并且补间变得更平滑(代价是速度变慢)。
Assume everything is floating point. If r g and b go from 0 to 1, h goes from 0 to 2pi, v goes from 0 to 4/3, and s goes from 0 to 2/3.
假设一切都是浮点数。如果rg和b从0到1,h从0到2pi,v从0到4/3,s从0到2/3。
The following code is written in Lua. It's easily translatable into anything else.
下面的代码是用 Lua 编写的。它很容易翻译成其他任何东西。
local hsv do
hsv ={}
local atan2 =math.atan2
local cos =math.cos
local sin =math.sin
function hsv.fromrgb(r,b,g)
local c=r+g+b
if c<1e-4 then
return 0,2/3,0
else
local p=2*(b*b+g*g+r*r-g*r-b*g-b*r)^0.5
local h=atan2(b-g,(2*r-b-g)/3^0.5)
local s=p/(c+p)
local v=(c+p)/3
return h,s,v
end
end
function hsv.torgb(h,s,v)
local r=v*(1+s*(cos(h)-1))
local g=v*(1+s*(cos(h-2.09439)-1))
local b=v*(1+s*(cos(h+2.09439)-1))
return r,g,b
end
function hsv.tween(h0,s0,v0,h1,s1,v1,t)
local dh=(h1-h0+3.14159)%6.28318-3.14159
local h=h0+t*dh
local s=s0+t*(s1-s0)
local v=v0+t*(v1-v0)
return h,s,v
end
end
回答by Ray Hulha
GLSL Shader version based on Patapoms answer:
基于 Patapoms 的 GLSL 着色器版本回答:
vec3 HSV2RGB( vec3 hsv )
{
hsv.x = mod( 100.0 + hsv.x, 1.0 ); // Ensure [0,1[
float HueSlice = 6.0 * hsv.x; // In [0,6[
float HueSliceInteger = floor( HueSlice );
float HueSliceInterpolant = HueSlice - HueSliceInteger; // In [0,1[ for each hue slice
vec3 TempRGB = vec3( hsv.z * (1.0 - hsv.y), hsv.z * (1.0 - hsv.y * HueSliceInterpolant), hsv.z * (1.0 - hsv.y * (1.0 - HueSliceInterpolant)) );
float IsOddSlice = mod( HueSliceInteger, 2.0 ); // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
float ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
vec3 ScrollingRGBForEvenSlices = vec3( hsv.z, TempRGB.zx ); // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
vec3 ScrollingRGBForOddSlices = vec3( TempRGB.y, hsv.z, TempRGB.x ); // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
vec3 ScrollingRGB = mix( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
float IsNotFirstSlice = clamp( ThreeSliceSelector, 0.0,1.0 ); // 1 if NOT the first slice (true for slices 1 and 2)
float IsNotSecondSlice = clamp( ThreeSliceSelector-1.0, 0.0,1. ); // 1 if NOT the first or second slice (true only for slice 2)
return mix( ScrollingRGB.xyz, mix( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice ); // Make the RGB rotate right depending on final slice index
}
回答by Kamil Kie?czewski
I'm not C++ developer so I will not provide code. But I can provide simple hsv2rgbalgorithm (rgb2hsv here) which I currently discover - I update wiki with description: HSVand HLS. Main improvement is that I carefully observe r,g,b as hue functions and introduce simpler shape function to describe them (without loosing accuracy). The Algorithm- on input we have: h (0-255), s (0-255), v(0-255)
我不是 C++ 开发人员,所以我不会提供代码。但是我可以提供我目前发现的简单hsv2rgb算法(此处为rgb2hsv) - 我用描述更新维基:HSV和HLS。主要改进是我仔细观察 r,g,b 作为色调函数并引入更简单的形状函数来描述它们(不损失准确性)。算法- 在输入上我们有:h (0-255), s (0-255), v(0-255)
r = 255*f(5), g = 255*f(3), b = 255*f(1)
We use function f described as follows
我们使用函数 f 描述如下
f(n) = v/255 - (v/255)*(s/255)*max(min(k,4-k,1),0)
where (mod can return fraction part; k is floating point number)
其中(mod 可以返回小数部分;k 是浮点数)
k = (n+h*360/(255*60)) mod 6;
回答by CRISTIAN
// This pair of functions convert HSL to RGB and vice-versa.
// It's pretty optimized for execution speed
typedef unsigned char BYTE
typedef struct _RGB
{
BYTE R;
BYTE G;
BYTE B;
} RGB, *pRGB;
typedef struct _HSL
{
float H; // color Hue (0.0 to 360.0 degrees)
float S; // color Saturation (0.0 to 1.0)
float L; // Luminance (0.0 to 1.0)
float V; // Value (0.0 to 1.0)
} HSL, *pHSL;
float *fMin (float *a, float *b)
{
return *a <= *b? a : b;
}
float *fMax (float *a, float *b)
{
return *a >= *b? a : b;
}
void RGBtoHSL (pRGB rgb, pHSL hsl)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
// rgb->R, rgb->G, rgb->B: [0 to 255]
float r = (float) rgb->R / 255;
float g = (float) rgb->G / 255;
float b = (float) rgb->B / 255;
float *min = fMin(fMin(&r, &g), &b);
float *max = fMax(fMax(&r, &g), &b);
float delta = *max - *min;
// L, V [0.0 to 1.0]
hsl->L = (*max + *min)/2;
hsl->V = *max;
// Special case for H and S
if (delta == 0)
{
hsl->H = 0.0f;
hsl->S = 0.0f;
}
else
{
// Special case for S
if((*max == 0) || (*min == 1))
hsl->S = 0;
else
// S [0.0 to 1.0]
hsl->S = (2 * *max - 2*hsl->L)/(1 - fabsf(2*hsl->L - 1));
// H [0.0 to 360.0]
if (max == &r) hsl->H = fmod((g - b)/delta, 6); // max is R
else if (max == &g) hsl->H = (b - r)/delta + 2; // max is G
else hsl->H = (r - g)/delta + 4; // max is B
hsl->H *= 60;
}
}
void HSLtoRGB (pHSL hsl, pRGB rgb)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
float a, k, fm1, fp1, f1, f2, *f3;
// L, V, S: [0.0 to 1.0]
// rgb->R, rgb->G, rgb->B: [0 to 255]
fm1 = -1;
fp1 = 1;
f1 = 1-hsl->L;
a = hsl->S * *fMin(&hsl->L, &f1);
k = fmod(0 + hsl->H/30, 12);
f1 = k - 3;
f2 = 9 - k;
f3 = fMin(fMin(&f1, &f2), &fp1) ;
rgb->R = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
k = fmod(8 + hsl->H/30, 12);
f1 = k - 3;
f2 = 9 - k;
f3 = fMin(fMin(&f1, &f2), &fp1) ;
rgb->G = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
k = fmod(4 + hsl->H/30, 12);
f1 = k - 3;
f2 = 9 - k;
f3 = fMin(fMin(&f1, &f2), &fp1) ;
rgb->B = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
}