从 RGB 中获取色调的最快公式
Posted
技术标签:
【中文标题】从 RGB 中获取色调的最快公式【英文标题】:Fastest formula to get Hue from RGB 【发布时间】:2014-05-30 04:18:25 【问题描述】:如果给定范围为 0-255 的红色、绿色和蓝色值,那么仅获得色调值的最快计算是什么?此公式将用于 30fps(每秒 920 万次)的 640x480 图像的每个像素,因此一点点速度优化都会有所帮助。
我见过其他公式,但我对它们涉及的步骤数不满意。我正在寻找一个实际的公式,而不是一个内置的库函数。
【问题讨论】:
如果你唯一要做的就是计算色调,那么这将是内存绑定的。您需要与色调一起进行一些其他计算才能成为计算(例如高斯模糊),以证明优化色调计算是合理的。 如果你有内存的话,这可以通过查找表快速完成。我认为这就是@Zboson 所暗示的。 【参考方案1】:将 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)
您得到的色调值需要乘以 60 才能将其转换为色环上的度数。如果 Hue 变为负数,则需要添加 360,因为一个圆有 360 度。
这里是full article。
【讨论】:
我看过那篇文章,这是我目前使用的方法。我希望有更快的东西.. 我看到了另一种使用 tan 的方法,但我想目前的方法比那更快 虽然应该是 256 而不是 255,但我想我现在明白了。假设只有两种颜色 0 和 1。如果我除以 0 和 0.5,我应该除以颜色数减 1。 谁能解释2.0 +
和4.0 +
部分实现了什么,因为(x - y) / (max - min)
产生的最大值是1,而不是2?
@Sphynx 注意x - y
可以是正数或负数,所以(x - y) / (max - min)
的范围是-1 到1,而不是0 到1。【参考方案2】:
除了乌姆里亚耶夫的回答:
如果只需要色调,则不需要将0-255范围的颜色除以255。
e.x. 的结果(green - blue) / (max - min)
在任何范围内都是相同的(当然,只要颜色在同一范围内)。
这是获取 Hue 的 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);
编辑:添加检查 min 和 max 是否相同,因为在这种情况下不需要其余的计算,并避免除以 0(参见 cmets)
编辑:修复 java 错误
【讨论】:
【参考方案3】: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);
【讨论】:
【参考方案4】:网页Math behind colorspace conversions, RGB-HSL 涵盖了这一点,但它包含我认为是错误的内容。它规定色调计算除以 max-min 但是如果除以这个小数,值会增加并且很容易超过 -1 到 5 的完整预期范围。我发现乘以 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)
我建议这样做:
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)
【讨论】:
你能在这里解释一下你的想法吗?我正在努力了解任何分子如何具有比分母更大的绝对值,这将导致分数的极限接近 1。每个函数确保分子中使用较小的两个数字,因此可能是最大的分子是三个变量中第二大的值。【参考方案5】:您必须指定您正在使用的语言和平台,因为C#、Java 和C 是非常不同的语言,而且它们之间和平台之间的性能也各不相同。这个问题目前太宽泛了!!!
640×480 与当前常见的分辨率相比不是很大,但“最快”是主观的,您需要仔细进行基准测试以选择最适合您的用例的分辨率。 看起来更多步骤的算法不一定比较短的算法慢,因为指令周期不是固定的,还有许多其他因素会影响性能,例如缓存一致性和分支(错误)预测.
对于上面提到的 Umriyaev 算法,您可以replace the division by 255 with a multiplication by 1.0/255
,这将提高性能,但可接受的误差很小。
但最好的方法将涉及 vectorization 和 parallelization 在某种程度上,因为现代 CPU 具有多个内核以及 SIMD units 来加速这样的数学和多媒体运算。例如 x86 具有 SSE/AVX/AVX-512... 可以同时在 8/16/32 通道上执行操作。结合多线程、硬件加速、GPU 计算......它会比这个问题的任何答案都要好。
在 C# 和 Java 中,过去没有很多矢量化选项,因此对于较旧的 .NET 和 JVM 版本,您需要在 C# 中运行不安全的代码。在 Java 中,您可以通过 JNI 运行 本机代码。但是现在他们都支持矢量化数学。 Java 在 JEP-338 中有一个新的 Vector API。在 Mono 中,您可以使用 Mono.Simd
namespace 中的矢量类型。在 RyuJIT 中有Microsoft.Bcl.Simd
。在 .NET 1.6+ 中有 System.Numerics
,其中包括 Vector 和其他
How to use the Intel AVX in Java? Parallelism on a Single Core - SIMD with C# SIMD in Depth - Performance and Cost in C# and C++ Will .NET ever do intelligent SIMD? Using...支持 SIMD 的向量类型,包括 Vector2、Vector3、Vector4、Matrix3x2、Matrix4x4、Plane 和 Quaternion。
System.Numerics.Vector
for Graphics Programming
System.Numerics.Vectors 'Vector<T>': is it basically just System.UInt128?
Performance Gains with Data Parallelism: Using SIMD Instructions from C#
【讨论】:
【参考方案6】:您可以使用此处建议的一种数学技术,但不是在每个像素上进行,而是在大约 10% 像素的随机样本上进行。这仍然很可能具有很高的准确性,并且速度会快 10 倍。
【讨论】:
以上是关于从 RGB 中获取色调的最快公式的主要内容,如果未能解决你的问题,请参考以下文章