IMPORTANT:
All samples shown in this article use the new GDIPlus-X library, that is still in ALPHA version, but is already stable and reliable to do the majority of GDI+ tasks. So, before you start, please download the latest stable release from Codeplex: http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX
In that article, I used the example below to apply a color matrix to get the gray scale version of an image:
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) LOCAL loBitmap AS xfcBitmap LOCAL loNewBitmap AS xfcBitmap LOCAL loGfx AS xfcGraphics LOCAL loAttr AS xfcImageAttributes LOCAL loClrMatrix AS xfcColorMatrix LOCAL loRect AS xfcRectangle WITH _SCREEN.System.Drawing loBitmap = .Bitmap.New(GETPICT()) loNewBitmap = .Bitmap.New(loBitmap.Width, loBitmap.Height) * Create a Graphics object to be able to draw in the bitmap loGfx = .Graphics.FromImage(loNewBitmap) loClrMatrix = .Imaging.ColorMatrix.New( ; 0.33, 0.33, 0.33, 0.0, 0.0, ; 0.33, 0.33, 0.33, 0.0, 0.0, ; 0.33, 0.33, 0.33, 0.0, 0.0, ; 0.0 , 0.0 , 0.0 , 1.0, 0.0, ; 0.0 , 0.0 , 0.0 , 0.0, 1.0) loAttr = .Imaging.ImageAttributes.New() loAttr.SetColorMatrix(loClrMatrix) loRect = .Rectangle.New(0,0,loBitmap.Width, loBitmap.Height) loGfx.DrawImage(loBitmap, loRect, loRect, .GraphicsUnit.Pixel, loAttr) loNewBitmap.Save("c:\NewImage.png", .Imaging.ImageFormat.Png) * Show the transformed image RUN /N explorer.exe c:\NewImage.png ENDWITH RETURN
IDENTITY MATRIX
It is very important to understand what each position in the color matrix means. The identity color matrix is a matrix that when applied to any image will return exactly the same original picture. Depending on the desired effect, we will apply changes in some specific positions in the identity matrix. So, if you change the contents of “loClrMatrix” to the matrix below, the resulting image will be exactly the same as the original.
loClrMatrix = .Imaging.ColorMatrix.New( ; 1, 0, 0, 0, 0, ; 0, 1, 0, 0, 0, ; 0, 0, 1, 0, 0, ; 0, 0, 0, 1, 0, ; 0, 0, 0, 0, 1)
GRAYSCALE 1
Conversion to gray scale is another common conversion. Below is the commonest gray scale matrix, which converts each pixel color to the average between the Red, Green and Blue channels.
loClrMatrix = .Imaging.ColorMatrix.New( ; 0.33, 0.33, 0.33, 0.0, 0.0, ; 0.33, 0.33, 0.33, 0.0, 0.0, ; 0.33, 0.33, 0.33, 0.0, 0.0, ; 0.0 , 0.0 , 0.0 , 1.0, 0.0, ; 0.0 , 0.0 , 0.0 , 0.0, 1.0)
GRAYSCALE 2
Grayscale values are determined by calculating the luminosity of a color, which is a weighted average of the R, G and B color components. The average is weighted according to the sensitivity of the human eye to each color component. The weights used here are as given by the NTSC (North America Television Standards Committee) and are widely accepted.
The effective luminance of a pixel is calculated with the following formula: C = 0.3 RED + 0.59 GREEN + 0.11 BLUE. So, using the color matrix below we will obtain another gray scale version of the image, more familiar with NTSC pattern. Bob Powell explains this operation with detail: http://www.bobpowell.net/grayscale.htm
loClrMatrix = .Imaging.ColorMatrix.New( ; 0.30, 0.30, 0.30, 0.0, 0.0, ; 0.59, 0.59, 0.59, 0.0, 0.0, ; 0.11, 0.11, 0.11, 0.0, 0.0, ; 0.0 , 0.0 , 0.0 , 1.0, 0.0, ; 0.0 , 0.0 , 0.0 , 0.0, 1.0)
Note that using this technique the result is slightly different from the one using the prior technique.
CONTRAST
According to Bob Powell, “to get an image with different contrast we just need a method of setting the scale of the red, green and blue elements to the same relative values”. This is accomplished with a color matrix that looks like the one shown below.
loClrMatrix = .Imaging.ColorMatrix.New( ; C, 0, 0, 0, 0, ; 0, C, 0, 0, 0, ; 0, 0, C, 0, 0, ; 0, 0, 0, 1, 0, ; .1, .1, .1, 0, 1)
In the sample below I used 1.40 for the C variable, that means an increase of 40% in contrast.
BRIGHTNESS
That’s very simple to change the brightness too. According to Bob Powell, “to get an image with different contrast we just need a method of setting the scale of the red, green and blue elements to the same relative values”. This is accomplished with a color matrix that looks like the one shown below.
loClrMatrix = .Imaging.ColorMatrix.New( ; 1, 0, 0, 0, 0, ; 0, 1, 0, 0, 0, ; 0, 0, 1, 0, 0, ; 0, 0, 0, 1, 0, ; B, B, B, 0, 1)
NEGATIVE
For a negative image, we just need to set all the contrast main values to -1
loClrMatrix = .Imaging.ColorMatrix.New( ; -1, 0, 0, 0, 0, ; 0 , -1, 0, 0, 0, ; 0 , 0, -1, 0, 0, ; 0 , 0, 0, 1, 0, ; 0 , 0, 0, 0, 1)
There is a known problem related by BobPowell at http://www.bobpowell.net/negativeimage.htm that occurs “when an image contains colors that have zero values in any of the RGB triplet bytes. The matrix multiplies the color triplet byte values by the values in it's matrix and so zero multiplied by -1 is still zero. This makes the inverse of primary colors such as red or blue look black. To correct this problem it is necessary to ensure that none of the triplet values in an image are zero. This can be accomplished using a compression matrix that compresses the gamut very slightly and shifts the whole range up a little. The following code shows such a compression matrix. The values are chosen to shift the value of each byte in the image into the range 1-255 as opposed to the original range 0-255.”
Having this, we need to apply a slight modification in our color matrix, specifically at the positions 40,41 and 42 in order to make a slight compensation on the image.
loClrMatrix = .Imaging.ColorMatrix.New( ; -1, 0, 0, 0, 0, ; 0 , -1, 0, 0, 0, ; 0 , 0, -1, 0, 0, ; 0 , 0, 0, 1, 0, ; 1 , 1, 1, 0, 1)
SATURATION
According to the Wikipedia http://en.wikipedia.org/wiki/Saturation_(color_theory) , the Saturation “is the intensity of a specific hue. A highly saturated hue has a vivid, intense color, while a less saturated hue appears more muted and grey. With no saturation at all, the hue becomes a shade of grey. Saturation is one of three components of the HSL color space and the HSV color space.”
We can also change the saturation of an image applying a specific color matrix.
The “S” variable ranges from 0 to 4, where zero value returns a grayscale version of the image; 1 means no change.
“SCompl” is the saturation complement “SR”, “SG” and “SB” are the saturation complements for the red, green and blue channels
Bob Powell again provides valuable information on this subject. http://www.bobpowell.net/imagesaturation.htm
LOCAL S, SCompl, SR, SG, SB S = 0 && Greyscale S = 4 && Saturated image SCompl = 1 - S SR = 0.3086 * SCompl SG = 0.6094 * SCompl SB = 0.0820 * SCompl loClrMatrix = .Imaging.ColorMatrix.New( ; (SR+S), SR , SR , 0, 0, ; SG , (SG+S), SG , 0, 0, ; SB , SB , (SB+S), 0, 0, ; 0 , 0 , 0 , 1, 0, ; 0 , 0 , 0 , 0, 1)
Using S = 0 we get the gray scale version:
Using S = 2 we obtain a color saturated image of 100%
COLOR SCALE TRANSFORMATIONS
To change a single color is very easy too. If we want to change one specific color channel, we can just change the variables lnRed, lnGreen and lnBlue. These are the same values that we used for the contrast transformation. If you use the same values for the red, green and blue channels you’ll obtain the same results. lnRed, lnGreen and lnBlue usually range from 0 to 1.
loClrMatrix = .Imaging.ColorMatrix.New( ; lnRed, 0 , 0 , 0, 0, ; 0 , lnGreen, 0 , 0, 0, ; 0 , 0 , lnBlue, 0, 0, ; 0 , 0 , 0 , 1, 0, ; 0.1 , 0.1 , 0.1 , 0, 1)
APPLYING ALL EFFECTS AT THE SAME TIME
The color matrix is applied to the image using the Graphics.Drawimage function, with the image attributes object as a parameter. So, if we want to apply more than one effect at the same time to na image, one way would be to call Graphics.DrawImage for every color matrix. That would be a very process and time consuming operation.
The solution is to do a matrix multiply on two matrices (eg. Saturation and brightness) before drawing the image. The matrices multiplication is not so obvious, for this task you can look at some of your mathematics books from school times, or check this link from Mathworld http://mathworld.wolfram.com/MatrixMultiplication.html . The Matrix multiplication is much faster than setting a new color matrix and redrawing the image. Microsoft did not include a Matrix multiply function in gdiplus.dll or in the .NET original classes.
It was very easy to decide to add the Multiply function to the ColorMatrix class. You can use it this way:
loNewMatrix = loMatrix1.Multiply(loMatrix1, loMatrix2)
WITH _SCREEN.System.Drawing lnRed = 1.4 && increase of 40% lnGreen = 1 && keep the same lnBlue = 0.5 && decrease in 50% B = 0.2 && increase brightness in 20% loBitmap = .Bitmap.New(GETPICT()) loNewBitmap = .Bitmap.New(loBitmap.Width, loBitmap.Height) * Create a Graphics object to be able to draw in the bitmap loGfx = .Graphics.FromImage(loNewBitmap) loBright = .Imaging.ColorMatrix.New( ; 1, 0, 0, 0, 0, ; 0, 1, 0, 0, 0, ; 0, 0, 1, 0, 0, ; 0, 0, 0, 1, 0, ; B, B, B, 0, 1) loColors = .Imaging.ColorMatrix.New( ; lnRed, 0 , 0 , 0, 0, ; 0 , lnGreen, 0 , 0, 0, ; 0 , 0 , lnBlue, 0, 0, ; 0 , 0 , 0 , 1, 0, ; 0 , 0 , 0 , 0, 1) loNewMatrix = loBright.Multiply(loBright, loColors) loAttr = .Imaging.ImageAttributes.New() loAttr.SetColorMatrix(loNewMatrix) loRect = .Rectangle.New(0,0,loBitmap.Width, loBitmap.Height) loGfx.DrawImage(loBitmap, loRect, loRect, .GraphicsUnit.Pixel, loAttr) loNewBitmap.Save("c:\NewImage.png", .Imaging.ImageFormat.Png) * Show the transformed image RUN /N explorer.exe c:\NewImage.png ENDWITH
The original picture stays on the left. To obtain the image on the right I first reduced the blue channel of the image to zero. That produced a very cool effect, converting the flowers to yellow. Then I changed the “Hue2” slide bar, to make the flowers orange like.
There are many more cool effects that we can obtain using color matrices. On the web you will find many interesting samples, originally prepared for C or Visual Basic. This is not a problem any more, because the GDIPlus-X classes are compatible with most of those published codes.
Hope you enjoy !
RELATED LINKS
Bob Powell's GDI+ FAQS http://www.bobpowell.net
How to: Use a Color Matrix to Transform a Single Color http://msdn2.microsoft.com/en-us/library/6tf7sa87.aspx
Article: ColorMatrix Basics - Simple Image Color Adjustment by Michael Combs http://www.codeproject.com/vb/net/colormatrix.asp?df=100&forumid=14842&exp=0&select=1072852&tid=1072852#xx1072852xx
Article: GDI+ Graphics Transformation - By Mahesh Chand. http://www.robert.to/csharp/csharp4.html