# Shift hue of an RGB Color

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.

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);
}
```

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);
```

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!

### 9 Solutions Collect From Internet About “Shift hue of an RGB Color”

**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.

There’s a quick step by step here http://beesbuzz.biz/code/hsv_color_transforms.php

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;
}
```

The RGB color space describes a cube. It is possible to rotate this cube around the diagonal axis from (0,0,0) to (255,255,255) to effect a change of hue. Note that some of the results will lie outside of the 0 to 255 range and will need to be clipped.

I finally got a chance to code this algorithm. It’s in Python but it should be easy to translate to the language of your choice. The formula for 3D rotation came from http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle

**Edit:** If you saw the code I posted previously, please ignore it. I was so anxious to find a formula for the rotation that I converted a matrix-based solution into a formula, not realizing that the matrix was the best form all along. I’ve still simplified the calculation of the matrix using the constant sqrt(1/3) for axis unit vector values, but this is much closer in spirit to the reference and simpler in the per-pixel calculation `apply`

as well.

```
from math import sqrt,cos,sin,radians
def clamp(v):
if v < 0:
return 0
if v > 255:
return 255
return int(v + 0.5)
class RGBRotate(object):
def __init__(self):
self.matrix = [[1,0,0],[0,1,0],[0,0,1]]
def set_hue_rotation(self, degrees):
cosA = cos(radians(degrees))
sinA = sin(radians(degrees))
self.matrix[0][0] = cosA + (1.0 - cosA) / 3.0
self.matrix[0][1] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA
self.matrix[0][2] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA
self.matrix[1][0] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA
self.matrix[1][1] = cosA + 1./3.*(1.0 - cosA)
self.matrix[1][2] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA
self.matrix[2][0] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA
self.matrix[2][1] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA
self.matrix[2][2] = cosA + 1./3. * (1.0 - cosA)
def apply(self, r, g, b):
rx = r * self.matrix[0][0] + g * self.matrix[0][1] + b * self.matrix[0][2]
gx = r * self.matrix[1][0] + g * self.matrix[1][1] + b * self.matrix[1][2]
bx = r * self.matrix[2][0] + g * self.matrix[2][1] + b * self.matrix[2][2]
return clamp(rx), clamp(gx), clamp(bx)
```

Here are some results from the above:

You can find a different implementation of the same idea at http://www.graficaobscura.com/matrix/index.html

I was disappointed by most answers I found here, some were flawed and basically flat-out wrong. I ended up spending 3+ hours trying to figure this out. The answer by Mark Ransom is correct, but I want to offer a complete C solution that’s also verified with MATLAB. I have tested this thoroughly, and here is the C code:

```
#include <math.h>
typedef unsigned char BYTE; //define an "integer" that only stores 0-255 value
typedef struct _CRGB //Define a struct to store the 3 color values
{
BYTE r;
BYTE g;
BYTE b;
}CRGB;
BYTE clamp(float v) //define a function to bound and round the input float value to 0-255
{
if (v < 0)
return 0;
if (v > 255)
return 255;
return (BYTE)v;
}
CRGB TransformH(const CRGB &in, const float fHue)
{
CRGB out;
const float cosA = cos(fHue*3.14159265f/180); //convert degrees to radians
const float sinA = sin(fHue*3.14159265f/180); //convert degrees to radians
//calculate the rotation matrix, only depends on Hue
float matrix[3][3] = {{cosA + (1.0f - cosA) / 3.0f, 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA},
{1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f*(1.0f - cosA), 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA},
{1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f * (1.0f - cosA)}};
//Use the rotation matrix to convert the RGB directly
out.r = clamp(in.r*matrix[0][0] + in.g*matrix[0][1] + in.b*matrix[0][2]);
out.g = clamp(in.r*matrix[1][0] + in.g*matrix[1][1] + in.b*matrix[1][2]);
out.b = clamp(in.r*matrix[2][0] + in.g*matrix[2][1] + in.b*matrix[2][2]);
return out;
}
```

**NOTE: The rotation matrix only depends on the Hue** (`fHue`

), so once you’ve computed `matrix[3][3]`

, you can **reuse** it for every pixel in the image that is undergoing the same hue transformation! This will improve the efficiency drastically.

Here is a MATLAB code that verifies the results:

```
function out = TransformH(r,g,b,H)
cosA = cos(H * pi/180);
sinA = sin(H * pi/180);
matrix = [cosA + (1-cosA)/3, 1/3 * (1 - cosA) - sqrt(1/3) * sinA, 1/3 * (1 - cosA) + sqrt(1/3) * sinA;
1/3 * (1 - cosA) + sqrt(1/3) * sinA, cosA + 1/3*(1 - cosA), 1/3 * (1 - cosA) - sqrt(1/3) * sinA;
1/3 * (1 - cosA) - sqrt(1/3) * sinA, 1/3 * (1 - cosA) + sqrt(1/3) * sinA, cosA + 1/3 * (1 - cosA)];
in = [r, g, b]';
out = round(matrix*in);
end
```

Here is a sample input/output that was reproduceable by both codes:

```
TransformH(86,52,30,210)
ans =
36
43
88
```

So the input RGB of `[86,52,30]`

was converted to `[36,43,88]`

using a hue of `210`

.

Basically there are two options:

- Convert RGB -> HSV, change hue, convert HSV -> RGB
- Change the hue directly with a linear transformation

I’m not really sure about how to implement 2, but basically you’ll have to create a transformation matrix and filter the image through this matrix. However, this will re-color the image instead of changing only the hue. If this is ok for you, then this could be an option but if not a conversion cannot be avoided.

**Edit**

A little research shows this, which confirms my thoughts. To summarize: The conversion from RGB to HSV should be preferred, if an exact result is desired. Modifying the original RGB image by a linear transform also leads to a result but this rather tints the image. The difference is explained as follows: The conversion from RGB to HSV is non-linear, whereas the transform is linear.

The post is old, and the original poster was looking for ios code – however, I was sent here via a search for visual basic code, so for all those like me, I converted Mark’s code to a vb .net module:

```
Public Module HueAndTry
Public Function ClampIt(ByVal v As Double) As Integer
Return CInt(Math.Max(0F, Math.Min(v + 0.5, 255.0F)))
End Function
Public Function DegreesToRadians(ByVal degrees As Double) As Double
Return degrees * Math.PI / 180
End Function
Public Function RadiansToDegrees(ByVal radians As Double) As Double
Return radians * 180 / Math.PI
End Function
Public Sub HueConvert(ByRef rgb() As Integer, ByVal degrees As Double)
Dim selfMatrix(,) As Double = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
Dim cosA As Double = Math.Cos(DegreesToRadians(degrees))
Dim sinA As Double = Math.Sin(DegreesToRadians(degrees))
Dim sqrtOneThirdTimesSin As Double = Math.Sqrt(1.0 / 3.0) * sinA
Dim oneThirdTimesOneSubCos As Double = 1.0 / 3.0 * (1.0 - cosA)
selfMatrix(0, 0) = cosA + (1.0 - cosA) / 3.0
selfMatrix(0, 1) = oneThirdTimesOneSubCos - sqrtOneThirdTimesSin
selfMatrix(0, 2) = oneThirdTimesOneSubCos + sqrtOneThirdTimesSin
selfMatrix(1, 0) = selfMatrix(0, 2)
selfMatrix(1, 1) = cosA + oneThirdTimesOneSubCos
selfMatrix(1, 2) = selfMatrix(0, 1)
selfMatrix(2, 0) = selfMatrix(0, 1)
selfMatrix(2, 1) = selfMatrix(0, 2)
selfMatrix(2, 2) = cosA + oneThirdTimesOneSubCos
Dim rx As Double = rgb(0) * selfMatrix(0, 0) + rgb(1) * selfMatrix(0, 1) + rgb(2) * selfMatrix(0, 2)
Dim gx As Double = rgb(0) * selfMatrix(1, 0) + rgb(1) * selfMatrix(1, 1) + rgb(2) * selfMatrix(1, 2)
Dim bx As Double = rgb(0) * selfMatrix(2, 0) + rgb(1) * selfMatrix(2, 1) + rgb(2) * selfMatrix(2, 2)
rgb(0) = ClampIt(rx)
rgb(1) = ClampIt(gx)
rgb(2) = ClampIt(bx)
End Sub
End Module
```

I put common terms into (long) variables, but otherwise it’s a straightforward conversion – worked fine for my needs.

By the way, I tried to leave Mark an upvote for his excellent code, but I didn’t have enough votes myself to allow it to be visible (Hint, Hint).

It seems converting to HSV makes the most sense. Sass provides some amazing color helpers. It’s in ruby, but it might provide useful.

Excelent code, but, i wonder that it can be faster if you simply don´t use self.matrix[2][0], self.matrix[2][1], self.matrix[2][1]

Therefore, set_hue_rotation can be written simply as:

```
def set_hue_rotation(self, degrees):
cosA = cos(radians(degrees))
sinA = sin(radians(degrees))
self.matrix[0][0] = cosA + (1.0 - cosA) / 3.0
self.matrix[0][1] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA
self.matrix[0][2] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA
self.matrix[1][0] = self.matrix[0][2] <---Not sure, if this is the right code, but i think you got the idea
self.matrix[1][1] = self.matrix[0][0]
self.matrix[1][2] = self.matrix[0][1]
```

Scott….not exactly. The algo seems to work the same as in HSL/HSV, but faster.

Also, if you simply multiply the 1st 3 elements of the array with the factor for grey, you add/decrease luma.

Example…Greyscale from Rec709 have those values

[GrayRedFactor_Rec709: R$ 0.212671

GrayGreenFactor_Rec709: R$ 0.715160

GrayBlueFactor_Rec709: R$ 0.072169]

When you multply

self.matrix[x][x] with the GreyFactor correspondent you decrease luma without touching saturation

Ex:

```
def set_hue_rotation(self, degrees):
cosA = cos(radians(degrees))
sinA = sin(radians(degrees))
self.matrix[0][0] = (cosA + (1.0 - cosA) / 3.0) * 0.212671
self.matrix[0][1] = (1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA) * 0.715160
self.matrix[0][2] = (1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA) * 0.072169
self.matrix[1][0] = self.matrix[0][2] <---Not sure, if this is the right code, but i think you got the idea
self.matrix[1][1] = self.matrix[0][0]
self.matrix[1][2] = self.matrix[0][1]
```

And the opposite is also true.If you divide instead multiply, the luminosity is increased dramatically.

From what i´m testing this algorithms can be a wonderfull replacement for HSL, as long you don´t need saturation, of course.

Try doing this…rotate the hue to only 1 degree (Just to force the algo to work properly while keeping the same perception sensitivity of the image), and multiply by those factors.

Also, Mark´s algo produces more accurated results.

For instance, if you rotate the hue to 180º using HSV colorspace, the image may result in a reddish tone color.

But on Mark´s algo, the image is properly rotate. Skins tones for example (Hue = 17, Sat = 170, L = 160 in PSP) turns properly to blue that have Hue around 144 in PSP, and all other colors of the image are properly rotate.

The algo makes sense since Hue is nothing more, nothing else then a Logarithm function of a arctan of Red, Green, Blue as defined by this formula:

```
Hue = arctan((logR-logG)/(logR-logG+2*LogB))
```

- How to create atlas for SpriteKit
- Xcode files and folders clean structure and organization
- Is this a bug with MKMapKitDelegate mapView:didUpdateUserLocation?
- Core Data/NSOperation: crash while enumerating through and deleting objects
- How to make a Button continually call a function when held down (SpriteKit)
- Allow “auto lock” while video is being played
- Is there a keyboard shortcut for cycling through pragma marks or methods in Xcode?
- What kind of sarcastic error is this iOS?
- HTTP Status Code 411 – Length Required
- Xib file: Can't drag a View from a xib file to a swift file
- iOS UITableView with dynamic text and images rendered together (NSAttributedString + images)
- How to change color of UITableViewCell when selecting?
- AVAudioPlayer is always nill and its isPlaying property is always false. The files are mixed while playing
- Pass data through view controller, tab bar controller, navigation controller and to view controller
- How to share content on WhatsApp from iOS