改变 RGB 颜色的色调 [英] Shift hue of an RGB Color

查看:47
本文介绍了改变 RGB 颜色的色调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个函数来改变 RGB 颜色的色调.具体来说,我在 iOS 应用中使用它,但数学是通用的.

I'm trying to write a function to shift the hue of an RGB color. Specifically I'm using it in an iOS app, but the math is universal.

下图显示了 R、G 和 B 值如何随色调变化.

The graph below shows how the R, G, and B values change with respect to the hue.

看起来似乎编写一个函数来改变色调而不对不同的颜色格式进行任何令人讨厌的转换,这会引入更多错误(如果继续应用小变化,这可能是一个问题)一种颜色),我怀疑计算成本会更高.

Looking at that it seems like it should be a relatively simple to write a function to shift the hue without doing any nasty conversions to a different color format which would introduce more error (which could be an issue if continue applying small shifts to a color), and I suspect would be more computationally expensive.

这是我到目前为止所拥有的作品.如果您从纯黄色、青色或洋红色转变为纯黄色、青色或洋红色,它会非常有效,但否则在某些地方它会变得有点松散.

Here is what I have so far which sort of works. It works perfectly if you're shifting from pure yellow or cyan or magenta but otherwise it gets a little squiffy in some places.

Color4f ShiftHue(Color4f c, float d) {
    if (d==0) {
        return c;
    }
    while (d<0) {
        d+=1;
    }

    d *= 3;

    float original[] = {c.red, c.green, c.blue};
    float returned[] = {c.red, c.green, c.blue};

    // big shifts
    for (int i=0; i<3; i++) {
        returned[i] = original[(i+((int) d))%3];
    }
    d -= (float) ((int) d);
    original[0] = returned[0];
    original[1] = returned[1];
    original[2] = returned[2];

    float lower = MIN(MIN(c.red, c.green), c.blue);
    float upper = MAX(MAX(c.red, c.green), c.blue);

    float spread = upper - lower;
    float shift  = spread * d * 2;

    // little shift
    for (int i = 0; i < 3; ++i) {
        // if middle value
        if (original[(i+2)%3]==upper && original[(i+1)%3]==lower) {
            returned[i] -= shift;
            if (returned[i]<lower) {
                returned[(i+1)%3] += lower - returned[i];
                returned[i]=lower;
            } else
                if (returned[i]>upper) {
                    returned[(i+2)%3] -= returned[i] - upper;
                    returned[i]=upper;
                }
            break;
        }
    }

    return Color4fMake(returned[0], returned[1], returned[2], c.alpha);
}

我知道你可以用 UIColors 做到这一点,并用这样的东西改变色调:

I know you can do this with UIColors and shift the hue with something like this:

CGFloat hue;
CGFloat sat;
CGFloat bri;
[[UIColor colorWithRed:parent.color.red green:parent.color.green blue:parent.color.blue alpha:1] getHue:&hue saturation:&sat brightness:&bri alpha:nil];
hue -= .03;
if (hue<0) {
    hue+=1;
}
UIColor *tempColor = [UIColor colorWithHue:hue saturation:sat brightness:bri alpha:1];
const float* components= CGColorGetComponents(tempColor.CGColor);
color = Color4fMake(components[0], components[1], components[2], 1);

但我并不为此感到疯狂,因为它仅适用于 iOS 5,并且在分配多个颜色对象和从 RGB 转换为 HSB 然后再返回之间似乎有点矫枉过正.

but I'm not crazy about that as It only works in iOS 5, and between allocating a number of color objects and converting from RGB to HSB and then back it seems pretty overkill.

我最终可能会在我的应用程序中使用查找表或预先计算颜色,但我真的很好奇是否有办法让我的代码工作.谢谢!

I might end up using a lookup table or pre-calculate the colors in my application, but I'm really curious if there's a way to make my code work. Thanks!

推荐答案

Edit 每条评论将全部"更改为可以线性近似于".
编辑 2 添加偏移量.

Edit per comment changed "are all" to "can be linearly approximated by".
Edit 2 adding offsets.

基本上,您想要的步骤是

Essentially, the steps you want are

RBG->HSV->Update hue->RGB

由于这些可以通过线性矩阵变换来近似(即它们是关联的),您可以在一个步骤中执行它,而不会发生任何令人讨厌的转换或精度损失.您只需将变换矩阵彼此相乘,然后使用它来变换颜色.

Since these can be approximated by linear matrix transforms (i.e. they are associative), you can perform it in a single step without any nasty conversion or loss of precision. You just multiple the transform matrices with each other, and use that to transform your colors.

这里有一个快速的步骤http://beesbuzz.biz/code/hsv_color_transforms.php

这是 C++ 代码(删除了饱和度和值转换):

Here's the C++ code (With the saturation and value transforms removed):

Color TransformH(
    const Color &in,  // color to transform
    float H
)
{
  float U = cos(H*M_PI/180);
  float W = sin(H*M_PI/180);

  Color ret;
  ret.r = (.299+.701*U+.168*W)*in.r
    + (.587-.587*U+.330*W)*in.g
    + (.114-.114*U-.497*W)*in.b;
  ret.g = (.299-.299*U-.328*W)*in.r
    + (.587+.413*U+.035*W)*in.g
    + (.114-.114*U+.292*W)*in.b;
  ret.b = (.299-.3*U+1.25*W)*in.r
    + (.587-.588*U-1.05*W)*in.g
    + (.114+.886*U-.203*W)*in.b;
  return ret;
}

这篇关于改变 RGB 颜色的色调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆