This is an excellent example of rotating the hue using only matrices. It is way better transformation.
;;
http://stackoverflow.com/questions/8507885/shift-hue-of-an-rgb-color
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)
;;
[RotationMatrix:
RotationMatrix.Data0_0: R$ 0
RotationMatrix.Data0_1: R$ 0
RotationMatrix.Data0_2: R$ 0
RotationMatrix.Data1_0: R$ 0
RotationMatrix.Data1_1: R$ 0
RotationMatrix.Data1_2: R$ 0
RotationMatrix.Data2_0: R$ 0
RotationMatrix.Data2_1: R$ 0
RotationMatrix.Data2_2: R$ 0]
[Float_SquareRoot_OneThird: R$ 0.577350269189625764509148780501957455647601751270126876018602] ; sqrt(1/3)
[TmpRotate.Red: R$ 0]
[TmpRotate.Green: R$ 0]
[TmpRotate.Blue: R$ 0]
[TmpAngle: R$ 0]
[TmpRotateRatio: R$ 0]
Proc RGBRotate:
Arguments @pColorData, @Angle
Local @TmpStorage, @TmpAngle, @TmpColorRed, @TmpColorGreen, @TmpColorBlue
Uses ebx, esi
mov esi D@pColorData
lea ebx D@TmpColorRed | move D$ebx D$esi
lea ebx D@TmpColorGreen | move D$ebx D$esi
lea ebx D@TmpColorBlue | move D$ebx D$esi
lea ebx D@TmpStorage
movzx eax B$esi+RGBTRIPLE.rgbtRedDis | mov D$ebx eax | fild F$ebx | fstp R$TmpRotate.Red
movzx eax B$esi+RGBTRIPLE.rgbtGreenDis | mov D$ebx eax | fild F$ebx | fstp R$TmpRotate.Green
movzx eax B$esi+RGBTRIPLE.rgbtBlueDis | mov D$ebx eax | fild F$ebx | fstp R$TmpRotate.Blue
lea ebx D@TmpAngle | move D$ebx D@Angle | fild F$ebx | fmul R$Degree_Radian | fstp R$TmpAngle
R$TmpRotateRatio = (1 - cos(R$TmpAngle)) * R$Float_OneThird
R$RotationMatrix.Data0_0 = (cos(R$TmpAngle) + R$TmpRotateRatio); * R$GrayRedFactor_Rec709 ; seting the gray factor keep luma while not touching saturtion
R$RotationMatrix.Data0_1 = (R$TmpRotateRatio - (R$Float_SquareRoot_OneThird*sin(R$TmpAngle))); * R$GrayGreenFactor_Rec709
R$RotationMatrix.Data0_2 = (R$TmpRotateRatio + (R$Float_SquareRoot_OneThird*sin(R$TmpAngle))); * R$GrayBlueFactor_Rec709
R$RotationMatrix.Data1_0 = R$RotationMatrix.Data0_2
R$RotationMatrix.Data1_1 = R$RotationMatrix.Data0_0
R$RotationMatrix.Data1_2 = R$RotationMatrix.Data0_1
R$RotationMatrix.Data2_0 = R$RotationMatrix.Data0_1
R$RotationMatrix.Data2_1 = R$RotationMatrix.Data0_2
R$RotationMatrix.Data2_2 = R$RotationMatrix.Data0_0
lea ebx D@TmpStorage
fld R$TmpRotate.Red | fmul R$RotationMatrix.Data0_0 | fld R$TmpRotate.Green | fmul R$RotationMatrix.Data0_1 | faddp ST1 ST0
fld R$TmpRotate.Blue | fmul R$RotationMatrix.Data0_2 | faddp ST1 ST0 | fistp F$ebx | On D$ebx <s 0, mov D$ebx 0 | On D$ebx > 255, mov D$ebx 255 | mov eax D$ebx | mov B$esi+RGBTRIPLE.rgbtRedDis al
fld R$TmpRotate.Red | fmul R$RotationMatrix.Data1_0 | fld R$TmpRotate.Green | fmul R$RotationMatrix.Data1_1 | faddp ST1 ST0
fld R$TmpRotate.Blue | fmul R$RotationMatrix.Data1_2 | faddp ST1 ST0 | fistp F$ebx | On D$ebx <s 0, mov D$ebx 0 | On D$ebx > 255, mov D$ebx 255 | mov eax D$ebx | mov B$esi+RGBTRIPLE.rgbtGreenDis al
fld R$TmpRotate.Red | fmul R$RotationMatrix.Data2_0 | fld R$TmpRotate.Green | fmul R$RotationMatrix.Data2_1 | faddp ST1 ST0
fld R$TmpRotate.Blue | fmul R$RotationMatrix.Data2_2 | faddp ST1 ST0 | fistp F$ebx | On D$ebx <s 0, mov D$ebx 0 | On D$ebx > 255, mov D$ebx 255 | mov eax D$ebx | mov B$esi+RGBTRIPLE.rgbtBlueDis al
EndP
Example of usage:
[hBitsInMemory: D$ 0] ; a variable to store the pixel in an ARGB form
mov esi D$hBitsInMemory
call RGBRotate esi, 260 ;<--- Angle always in degrees
Btw: SOmeone knows how to achieve the hue angle using this sort of matrix ? For the reverted operation
Christian Graus wrote several articles
whether or not you find exactly what you're looking for, you'll enjoy his articles :t
http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=6556 (http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=6556)
another one, i haven't had time to look at it closely
http://www.codeproject.com/Articles/426172/My-Photo-Editor (http://www.codeproject.com/Articles/426172/My-Photo-Editor)