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