我正在寻找从RGB到HSV的色彩空间转换器,特别是对于两个色彩空间的0到255范围.
Dav*_*d H 118
我已经使用了很长时间了 - 不知道它们来自何处...请注意,输入和输出除了以度为单位的角度外,都在0到1.0的范围内.
注意:此代码对输入没有真正的健全性检查.谨慎行事!
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;
}
Run Code Online (Sandbox Code Playgroud)
Les*_*ary 36
您也可以在没有浮点数的情况下尝试此代码(更快但不太准确):
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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,此算法使用0-255其范围(不是 0-360).
(来源)
小智 22
我在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
}
Run Code Online (Sandbox Code Playgroud)
这应该在这里:无论如何它都有效.与上述相比,它看起来很好.
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);
}
Run Code Online (Sandbox Code Playgroud)
float3是16位精度vector3数据类型,即float3 hue()返回数据类型(x,y,z),例如(r,g,b),半数相同,半精度,8bit,float4是(r, g,b,a)4个值.
小智 6
当您调低饱和度时,@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;
}
Run Code Online (Sandbox Code Playgroud)
这不是 C,但它确实有效。我在这里看到的所有其他方法都是将所有东西都封装成六边形的一部分,然后从中估算出“角度”。通过使用余弦从不同的方程开始,并求解 hs 和 v,您可以在 hsv 和 rgb 之间获得更好的关系,并且补间变得更平滑(代价是速度变慢)。
假设一切都是浮点数。如果rg和b从0到1,h从0到2pi,v从0到4/3,s从0到2/3。
下面的代码是用 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
Run Code Online (Sandbox Code Playgroud)
这是一个基于Agoston 计算机图形和几何建模的C实现:实现和算法 p.304,具有ħ ∈[0,360]和小号,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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
163612 次 |
| 最近记录: |