Valorem Consulting

| Tags: Evan Lang, Tips

A lot of people are familiar with the WriteableBitmapEx project on Codeplex. I got an opportunity to use it in a recent project of mine, and although many of its functions are very helpful, I noticed a couple common image processing functions it was missing. It supports resizable blits (with bilinear filtering), flips, and rotates, which are nice. More impressively, it supports arbitrary convolutions, which are very powerful and versatile in the image processing world. What it was missing, however, was the ability to perform a linear colorspace transform, and apply a color ramp lookup table.

Linear transforms in an image's color space (RGB in the case of this implementation) can perform a variety of functions. They work by treating each pixel as a vector of color components, <R, G, B, A>. That vector is transformed by a 4x4 matrix, and the output vector represents the new color. To use this function, call

WriteableBitmapColorExtensions.TransformColors(Matrix3D matrix);

The matrix you supply determines how the color is transformed.  The alpha component in most images is usually 1, making the matrix usually affine, though the method does not give it special treatment, so you may get some… interesting… behavior with translucent images, depending on how you put your matrix together.

The WriteableBitmapColorExtensions class defines a few common ones:

   NegativeTransform - Produces an image's film negative

$latex left( begin{matrix}1&0&0&-1 0&1&0&-1 0&0&1&-1 0&0&0&1 end{matrix} right)$

   BlackAndWhiteTransform - Transforms the image into black and white

$latex left( begin{matrix}0.30&0.30&0.30&0 0.59&0.59&0.59&0 0.11&0.11&0.11&0 0&0&0&1 end{matrix} right)$

   SepiaTransform - Transform an image into sepia

$latex left( begin{matrix}0.393&0.349&0.272&0 0.769&0.686&0.534&0 0.189&0.168&0.131&0 0&0&0&1 end{matrix} right)$


What else can you do with it? You can try brightness changes:

$latex Brightness(B) = left( begin{matrix}B&0&0&0 0&B&0&0 0&0&B&0 0&0&0&1 end{matrix} right) $


Note you can vary the brightness of independent color components, if you like.

You can also desaturate an image by interpolating between the identity matrix and the black and white matrix. If your saturation level goes from 0 (black and white) to 1 (normal):

$latex Saturation(S)= Brightness(S) + BlackAndWhiteTransform * (1 - S) $

You can even do a hue shift. A hue shift basically rotates a color vector around the <1, 1, 1> axis:

HueShift(Degrees) = Matrix3D.Identity.Rotate(Degrees, new Vector3D(1,1,1))

The purists among us will note that this isn't exactly right... to do a true hue shift you need to normalize the brightness of the color components first (you can use the Brightness(S) function above), but this gets the idea across. The sample image here uses a subtle rotation of 15 degrees.

Those are the ‘ordinary’ functions that a color transform can do, but you can stylize things as well.  For example, you can swap around or even turn off color components:

There are other interesting things you can do with TransformColors as well, such as skewing colors or projecting them onto a plane (or a line, creating a nice two-tone effect—this is essentially what the black and white matrix does). And remember the most powerful feature of linear transformations: you can take any or all of these effects, combine them into a single matrix, and apply all of them to an image at once without any additional performance hit.

The other function introduced into the library, which I will elaborate more on in my next post, is WriteableBitmapColorExtensions. RampColors. This method replaces all the colors in an image using a lookup table. This is useful for nonlinear things like gamma correction.  In fact, gamma correction is by far the most common application of this function, so I included a special method just for it, WriteableBitmapColorExtensions.AdjustGamma.


2018 IdentityMine, Inc. Privacy Policy