Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotate Hue in C#

Tags:

c#

gdi+

I'm looking to replicate the CSS3 hue rotation behaviour found here

original image

original image

image with hue rotated 180deg

image with hue rotated 180deg

I can already accurately convert an RGB value to a HSL value and back again but I'm not sure what the mathematical function to apply to the hue component to replicate the output is.

like image 836
James South Avatar asked Sep 15 '14 20:09

James South


2 Answers

Addition.

It's that simple, just add 180 to the hue value, then make sure that it wraps around at 360:

hue = (hue + 180) % 360;
like image 79
Guffa Avatar answered Oct 09 '22 22:10

Guffa


I wanted to point out how to actually do this. This was the exact question, but no good answer.

Given a GDI+ Image, i want to apply a hue shift and return a new image. In practice it will return a new Bitmap. I'll use C# style pseudo-code.

First is the basic guts to clone a GDI+ image (but without the hue shift yet):

Bitmap CloneImage(Image sourceImage, Single hueShiftAngleDegrees)
{
   Int32 width  = sourceImage.GetWidth();
   Int32 height = sourceImage.Getheight();

   //Create destination bitmap
   Bitmap bmpDest = new Bitmap(width, height, PixelFormat32bppARGB);
   
   //Create a Graphics that will draw onto our destination bitmap
   Graphics g = new Graphics(destinationBitmap);

   //Draw the source image into the destination
   g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 
         0, 0, width, height, UnitPixel);

   return bmpDest;
}

Next is the idea that when we use the Graphics.DrawImage method, we can supply an ImageAttributes class.

ImageAttributes attributes = new ImageAttributes();
g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 
      0, 0, width, height, UnitPixel, attributes);

One of these attributes can be a 5x5 ColorMatrix:

ColorMatrix cm = (
   ( rr, gr, br, ar, 0 ),
   ( rg, gg, bg, ag, 0 ),
   ( rb, gb, bb, ab, 0 ),
   ( ra, ga, ba, aa, 0 ),
   ( r1, g1, b1, a1, 1 )
);

ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(cm);
g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 
      0, 0, width, height, UnitPixel, attributes);

The magic comes from the color matrix that can perform a hue shift. I create a function that can return the ColorMatrix that performs the desired hue shift:

ColorMatrix GetHueShiftColorMax(Single hueShiftDegrees)
{
    /* Return the matrix
    
        A00  A01  A02  0  0
        A10  A11  A12  0  0
        A20  A21  A22  0  0
          0    0    0  1  0
          0    0    0  0  1
    */
    Single theta = hueShiftDegrees/360 * 2*pi; //Degrees --> Radians
    Single c = cos(theta);
    Single s = sin(theta);

    Single A00 = 0.213 + 0.787*c - 0.213*s;
    Single A01 = 0.213 - 0.213*c + 0.413*s;
    Single A02 = 0.213 - 0.213*c - 0.787*s;

    Single A10 = 0.715 - 0.715*c - 0.715*s;
    Single A11 = 0.715 + 0.285*c + 0.140*s;
    Single A12 = 0.715 - 0.715*c + 0.715*s;

    Single A20 = 0.072 - 0.072*c + 0.928*s;
    Single A21 = 0.072 - 0.072*c - 0.283*s;
    Single A22 = 0.072 + 0.928*c + 0.072*s;

    ColorMatrix cm = new ColorMatrix(
          ( A00, A01, A02,  0,  0 ),
          ( A10, A11, A12,  0,  0 ),
          ( A20, A21, A22,  0,  0 ),
          (   0,   0,   0,  0,  0 ),
          (   0,   0,   0,  0,  1 )
    )

    return cm;
}

So i'll create a new kind of function, one that makes a copy of an image and applies a ColorMatrix to it:

Bitmap Multiply(Image sourceImage, ColorMatrix cm)
{
   Int32 width  = sourceImage.GetWidth();
   Int32 height = sourceImage.Getheight();

   //Create destination bitmap
   Bitmap bmpDest = new Bitmap(width, height, PixelFormat32bppARGB);
   
   //Create a Graphics that will draw onto our destination bitmap
   Graphics g = new Graphics(destinationBitmap);

   //Draw the source image into the destination
   ImageAttributes attributes = new ImageAttributes();
   attributes.SetColorMatrix(cm);
   g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 
         0, 0, width, height, UnitPixel, attributes);

   return bmpDest;
}

And our hue shift algorithm becomes:

Bitmap ApplyHueShift(Image sourceImage, Single hueShiftAngleDegrees)
{
   ColorMatrix cm = GetHueShiftColorMatrix(hueShiftAngleDegrees);

   return Multiply(sourceImage, cm);
}

I have no idea where the hue shift color matrix comes from. It just exists on MSDN page Hue rotation effect archive:

enter image description here

like image 29
Ian Boyd Avatar answered Oct 09 '22 23:10

Ian Boyd