从 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 和其他

...支持 SIMD 的向量类型,包括 Vector2、Vector3、Vector4、Matrix3x2、Matrix4x4、Plane 和 Quaternion。

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 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 中获取色调的最快公式的主要内容,如果未能解决你的问题,请参考以下文章

RGB 转飞利浦色调 (HSB)

如何仅从单面绘制带有色调颜色和圆角的 UIButton

从代码中获取全局色调颜色

GMail API:获取收件箱中最早的电子邮件的最快方式

Scss中的颜色函数

在 Python 或 C++ 中获取像素颜色的最快方法?