Introduction
This article explains how to manipulate the individual
pixels of an image to achieve some image processing functionalities. Using the
techniques one can easily develop/enhance on functionalities like brightening
an Image, changing the contrast of an image, applying gamma, colorizing an
image etc...
Background
The
reader must be reasonably familiar with Silverlight and its associated controls.
Using the Code
The Application consists of a Main Page with a
slider for each image processing routine. The code behind the MouseLeftButtonUp
event
executes the said functionality. The code is developed in C# with Visual studio
2010 express edition.
A Bit About Initializing
To get hold of the individual pixels of an image
the application first supports a Silverlight image control whose source is set
to a relative path namely (Images/2.jpg). Once the image is loaded a WriteableBitmap
object is created as shown below.
wbOriginal = new WriteableBitmap((UIElement)sender,null);
Note: Sender is an image control.
WriteableBitmap
objects can also be
constructed from an BitmapImage
object as is done in the code.
Loading an image into BitmapImage
object is pretty straight forward.
Once loaded into BitmapImage
object you just need to assign the
source property of the Image Silverlight control to this BitmapImage
object. Whenever the source of the Image control changes the
associated WriteableBitmap
object automatically gets updated to the new Image.
Let us
start with the change-brightness functionality.
Brightness
Changing the brightness is just adding a fixed
number(<255) to the individual pixels of an Image. First a temporary copy of
the associated WriteableBitmap object is made and the individual
pixel's Red, Green and Blue color is extracted into pixel as shown below:
int pixel = wb.Pixel[i];
int B = (int)(pixel & 0xFF); pixel >>= 8;
int G = (int)(pixel & 0xFF); pixel >>= 8;
int R = (int)(pixel & 0xFF); pixel >>= 8;
int A = (int)(pixel);
Every pixel is represented by a 32 bit integer,
where the Blue, Green, Red occupy the first second and third positions in the
pixel. Each of Red, Green and Blue can take a value starting from 0 to 255, a
byte value to be precise inside the int pixel varaible.
The
amount of brightness is determined by how far the Brightness slider has moved
from its previous position. That value is added to the R,G,B of every
individual pixel and normalized to 255
as shown below.
B += (int)sliderBrightness.Value;
R += (int)sliderBrightness.Value;
G += int)sliderBrightness.Value;
if (R > 255) R = 255; if (G > 255) G = 255; if (B > 255) B = 255;
if (R < 0) R = 0; if (G < 0) G = 0; if (B < 0) B = 0;
Now that
this is done it is time to put back these values first into wb (the new WriteableBitmap object) and then
back into Silverlight image control.
wb.Pixels[i] = B | (G << 8) | (R << 16) | (A << 24);
wb.Invalidate();
image1.Source = wb;
Let us
move on to another imaging processing routine namely the Power law.
Power Law
The
basics of getting hold of the individual pixels remain the same. The power law
actually darkens the dark region and brightens the bright regions of an image.
for this the power law first defines an array of doubles
double[] dPowerLawFactorArray = { .6, 1.7, .8, .9, 1.02, 1.03, 1.05, 1.07, 1.09, 1.1};
based on
the power law slider movement a value from this array is chosen and applied to
the R,G, and B values of individual pixels as shown below.
double dPowerlawfactor = dPowerLawFactorArray[nIndex];
R = (int)Math.Pow(R, dPowerlawfactor);
G = (int)Math.Pow(G, dPowerlawfactor);
B = (int)Math.Pow(B, dPowerlawfactor);
Changing
the contrast of an Image
double[] contrastArray = { 1, 1.2, 1.3, 1.6, 1.7, 1.9, 2.1, 2.4, 2.6, 2.9 };
double CFactor = contrastArray[nIndex];
R = (int) Math.Max(0, Math.Min(255, (((R - 128) * CFactor) + 128)));
G = (int)Math.Max(0, Math.Min(255, (((G - 128) * CFactor) + 128)));
B = (int)Math.Max(0, Math.Min(255, (((B - 128) * CFactor) + 128)));
Gamma Equations
double[] dGammaArray = new double[19];
dGammaArray[0] = .1;
dGammaArray[1] = .2;
dGammaArray[2] = .3;
dGammaArray[3] = .4;
dGammaArray[4] = .5;
dGammaArray[5] = .6;
dGammaArray[6] = .7;
dGammaArray[7] = .8;
dGammaArray[8] = .9;
dGammaArray[9] = 1;
dGammaArray[10] = 1.1;
dGammaArray[11] = 1.2;
dGammaArray[12]=1.3;
dGammaArray[13]=1.4;
dGammaArray[14] = 1.5;
dGammaArray[15] = 1.6;
dGammaArray[16] = 1.7;
dGammaArray[17] = 1.8;
dGammaArray[18] = 1.9;
double dGamma = dGammaArray[nIndex];
double dDinvgamma = 1.0 / dGamma;
double dMax = Math.Pow(255.0, dDinvgamma) / 255.0;
R = (int)Math.Max(0, Math.Min(255, (Math.Pow(R, dDinvgamma) / dMax)));
G = (int)Math.Max(0, Math.Min(255, (Math.Pow(G, dDinvgamma) / dMax)));
B = (int)Math.Max(0, Math.Min(255, (Math.Pow(B, dDinvgamma) / dMax)));
Colorizing an Image
In this
the user first sets the amount of Blue, Red, Green to apply on the chosen image
with the help of the given sliders. Once a choice is made the "Color
Image" Button can be clicked. This functionality applies the chosen colors
to the image as shown below:
R += (int)sliderRedNess.Value;
G += (int)sliderGreenNess.Value;
B += (int)sliderBlueNess.Value;
Grey Scale
This is just
the average of the R,G,B values applied to every pixel
R = G = B = (R + G + B) / 3;
Just to
make the application interesting some StoryBoard animations and easing
functions are applied to the image control. This animation rotates the image in
X,Y and Z axis before settling down to a normal view.