Resumé

Hue Rotation with F# and GDI+

Cristian Merighi () 5.00

First article in F#. Topic, useful and pretextuous at the same time, is how to hue rotate an image using GDI+ libraries.
This article is obsolete. Some functionalities might not work anymore. Comments are disabled.

I'm not that into those techno-philosophical disputes pro or contra a particular type of programming language. The thing I know is that I've never tried a functional language before F#.
I've been missing something.

I approached this new technology to experiment asynchronous and parallelized functionalities (waiting for PLinq and the CLR 4), but I suddenly fell in love with its fluent and natural syntax and patterns.

I just want to subscribe this first experience of mine signing a bunch of code lines relevant to GDI+ image management, specifically Hue rotation.

Brief theory excursus: most known and used among the color spaces is without doubt the so named RGB (from Red, Greeen, Blue).
This space can be sketched as a 3D environment generated by the three axes identified by the base colors.
Each color channel has available 1 byte (8 bits) of informations (in base-10, 28 values: from 0 to 255).

The imaginary line which connects the black point (R = 0, G = 0, B = 0) and the white one (255, 255, 255) is the gray axis.
Hue rotation is all about moving (rotating) colors around the gray axis (see figure).

RGB Space

To handle this kind of rotation let us disturb our fellows 3D vectors and quaternions. Quaternions are very useful in this scenario:

  1. General reason: they obviate the gimbal lock problem.
  2. Specific reason: they easily permits to get a correspondent 3D rotation matrix, ideal for us: we in fact need to generate a ColorMatrix and apply it to the original image.

About quaternions' theory, I'd address you to resources and literature aailable on the web or in the libraries, the following code will be commented specifying the goal of the line more than its algebraic meaning.

#light

 

namespace Pacem.Drawing

 

open System.Drawing

 

module internal ColorMatrix =

 

    let internal FromAngleAxisRotation(i, j, k, theta) =

        // quaternion-like

        let angleInRadians = (theta % 360.0) * 0.017453292519943295

        let length = sqrt (i*i + j*j + k*k)

        // love the pipeline pattern ;)

        if (length = 0.0) then System.InvalidOperationException() |> raise

        let sinTheta = 0.5 * angleInRadians |> sin

        // preparing the 4 parameters of the quaternion (x, y, x, w)

        let x = i * sinTheta / length

        let y = j * sinTheta / length

        let z = k * sinTheta / length

        let w = 0.5 * angleInRadians |> cos

        // quaternion to rotation matrix

        let m11 = 1.0 - 2.0 * y * y - 2.0 * z * z |> float32

        let m12 = 2.0 * x * y + 2.0 * w * z |> float32

        let m13 = 2.0 * x * z - 2.0 * w * y |> float32

        let m21 = 2.0 * x * y - 2.0 * w * z |> float32

        let m22 = 1.0 - 2.0 * x * x - 2.0 * z * z |> float32

        let m23 = 2.0 * y * z + 2.0 * w * x |> float32

        let m31 = 2.0 * w * y + 2.0 * x * z |> float32

        let m32 = 2.0 * y * z - 2.0 * w * x |> float32

        let m33 = 1.0 - 2.0 * x * x - 2.0 * y * y |> float32

        // notice array ctor: [| item1; item2; ... itemN |]

        let source = [|

            [| m11; m12; m13; 0.0f; 0.0f |];

            [| m21; m22; m23; 0.0f; 0.0f |];

            [| m31; m32; m33; 0.0f; 0.0f |];

            [| 0.0f; 0.0f; 0.0f; 1.0f; 0.0f |];

            [| 0.0f; 0.0f; 0.0f; 0.0f; 1.0f |]

            |]

        // result to return

        System.Drawing.Imaging.ColorMatrix(source)

I love duck-typing!

Visual Studio support

...I still love duck-typing (now rotated by 123°)!

Visual Studio support (hue-rotated ;))

Now how to use it to load, manipulate and save images.
Warning: this is decontextualized code, outside any module or type (= won't compile).

use attr = new ImageAttributes()

// degrees is the variable that carries the angle to rotate by.

// (1.0, 1.0, 1.0) has obviously the same direction of (255.0, 255.0, 255.0).

attr.SetColorMatrix(Pacem.Drawing.ColorMatrix.FromAngleAxisRotation(1.0, 1.0, 1.0, degrees))

use bmp = new Bitmap("c:\\myOriginalImage.jpg")

let w = bmp.Width

let h = bmp.Height

let rc = Rectangle(0, 0, w, h)

use graphics = bmp |> Graphics.FromImage

graphics.DrawImage(bmp, rc, 0, 0, w, h, GraphicsUnit.Pixel, attr)

bmp.Save("c:\\myHueRotatedImage.jpg")

Maybe this is not the more enlightning example about F# potentials, but it's a start.
About me, I've already rewritten my GDI+ libraries asynchronizing and parallelizing wherever possible, and I was stunned: on my Quad core, even the heavier cycles solve in one or two tenths of second for pretty huge images (1024x768).

Take care. Bye.

Feedbacks

  • Re: Hue Rotation with F# and GDI+

    Carlos Alloatti Sunday, June 13, 2010 5.00

    Excellent article thanks! What if we did not want to rotate, but apply an specific hue value?

feedback
 

Syndicate

Author

Cristian Merighi facebook twitter google+ youtube

Latest articles

Top rated

Archive

Where am I?

Author

Cristian Merighi facebook twitter google+ youtube

I'm now reading

Feeds