将 RGB 值转换为 0-1 范围内的值,可以通过将值除以 255(对于 8 位色深)来实现(其中 r、g、b 为给定值):
R = r / 255 = 0.09
G = g / 255 = 0.38
B = b / 255 = 0.46
找到R、G和B的最小和最大值。
根据哪个RGB颜色通道具有最大值,有三个不同的公式:
Hue = (G-B)/(max-min)
Hue = 2.0 + (B-R)/(max-min)
Hue = 4.0 + (R-G)/(max-min)
你得到的Hue值需要乘以60,将其转换为色彩圆上的角度。 如果Hue变为负数,则需要加上360,因为圆有360度。
这是完整文章。
x - y
可能是正数或负数,因此 (x - y) / (max - min)
的范围是从 -1 到 1,而不是从 0 到 1。 - Théophile除了Umriyaev的回答之外:
如果只需要色调,不需要将0-255范围内的颜色值除以255。
例如(绿色 - 蓝色) / (最大值 - 最小值)
的结果在任何范围内都是相同的(当然,前提是颜色在相同的范围内)。
以下是获取色调的Java示例:
public int getHue(int red, int green, int blue) {
float min = Math.min(Math.min(red, green), blue);
float max = Math.max(Math.max(red, green), blue);
if (min == max) {
return 0;
}
float hue = 0f;
if (max == red) {
hue = (green - blue) / (max - min);
} else if (max == green) {
hue = 2f + (blue - red) / (max - min);
} else {
hue = 4f + (red - green) / (max - min);
}
hue = hue * 60;
if (hue < 0) hue = hue + 360;
return Math.round(hue);
}
编辑:增加了检查最小值和最大值是否相同的步骤,因为在这种情况下不需要执行其余计算,并且避免了除以0的情况(见评论)
编辑:修复了Java错误
这可能不是最快的,但这是一个 JavaScript 函数,您可以通过单击下面的“运行代码片段”按钮直接在浏览器中尝试
function rgbToHue(r, g, b) {
// convert rgb values to the range of 0-1
var h;
r /= 255, g /= 255, b /= 255;
// find min and max values out of r,g,b components
var max = Math.max(r, g, b), min = Math.min(r, g, b);
// all greyscale colors have hue of 0deg
if(max-min == 0){
return 0;
}
if(max == r){
// if red is the predominent color
h = (g-b)/(max-min);
}
else if(max == g){
// if green is the predominent color
h = 2 +(b-r)/(max-min);
}
else if(max == b){
// if blue is the predominent color
h = 4 + (r-g)/(max-min);
}
h = h*60; // find the sector of 60 degrees to which the color belongs
// https://www.pathofexile.com/forum/view-thread/1246208/page/45 - hsl color wheel
// make sure h is a positive angle on the color wheel between 0 and 360
h %= 360;
if(h < 0){
h += 360;
}
return Math.round(h);
}
let gethue = document.getElementById('gethue');
let r = document.getElementById('r');
let g = document.getElementById('g');
let b = document.getElementById('b');
r.value = Math.floor(Math.random() * 256);
g.value = Math.floor(Math.random() * 256);
b.value = Math.floor(Math.random() * 256);
gethue.addEventListener('click', function(event) {
let R = parseInt(r.value)
let G = parseInt(g.value)
let B = parseInt(b.value)
let hue = rgbToHue(R, G, B)
console.log(`Hue(${R}, ${G}, ${B}) = ${hue}`);
});
<table>
<tr><td>R = </td><td><input id="r"></td></tr>
<tr><td>G = </td><td><input id="g"></td></tr>
<tr><td>B = </td><td><input id="b"></td></tr>
<tr><td colspan="2"><input id="gethue" type="button" value="Get Hue"></td></tr>
</table>
应该这样:
If Red is max, then Hue = (G-B)/(max-min)
If Green is max, then Hue = 2.0 + (B-R)/(max-min)
If Blue is max, then Hue = 4.0 + (R-G)/(max-min)
我建议这样做:
If Red is max, then Hue = (G-B)*(max-min)
If Green is max, then Hue = 2.0 + (B-R)*(max-min)
If Blue is max, then Hue = 4.0 + (R-G)*(max-min)
你必须指定你正在使用的编程语言和平台,因为C#、Java和C是非常不同的语言,它们的性能也在它们和平台之间有所不同。目前这个问题太过广泛了!!!
与当前常见分辨率相比,640×480并不算很大,但是“最快”是主观的,您需要进行仔细的基准测试来选择适合您的用例的最佳选项。一个看起来更长、步骤更多的算法并不一定比一个短一些的算法慢,因为指令周期不是固定的,还有许多其他因素会影响性能,例如缓存一致性和分支(预测)错误。
对于Umriyaev上面提到的算法,你可以用1.0/255
乘以像素值代替除以255,这样可以在可接受的误差范围内提高性能。
但是最好的方法将涉及向量化和并行化,因为现代CPU具有多个核心,还有SIMD单元来加速数学和多媒体操作,例如x86具有SSE / AVX / AVX-512...可以一次处理8/16/32个通道。结合多线程、硬件加速、GPU计算等,它将比本问题中的任何答案都要好得多。
在过去的C#和Java中,向量化选项并不多,因此在旧版.NET和JVM版本中,您需要在C#中运行不安全代码。在Java中,您可以通过JNI运行本地代码。但现在,它们全部都支持矢量化数学运算。Java有一个新的Vector API,即JEP-338。在Mono中,您可以使用Mono.Simd
命名空间中的向量类型。在RyuJIT中,有Microsoft.Bcl.Simd
。在.NET 1.6+中,有System.Numerics
,其中包括Vector和其他内容。