Article Purpose
Adjusting the contrast of an Image is a fairly common task in image processing. This article explores the steps involved in adjusting image contrast by directly manipulating image pixels.
Sample source code
This article is accompanied by a sample source code Visual Studio project which is available for download here.
What is Image Contrast?
Contrast within an image results in differences in colour and brightness being perceived. The greater the difference between colours and brightness in an image results in a greater chance of being perceived as different.
From Wikipedia we learn the following quote:
Contrast is the difference in luminance and/or color that makes an object (or its representation in an image or display) distinguishable. In visual perception of the real world, contrast is determined by the difference in the color and brightness of the object and other objects within the same field of view. Because the human visual system is more sensitive to contrast than absolute luminance, we can perceive the world similarly regardless of the huge changes in illumination over the day or from place to place.
Using the sample Application
The sample source code that accompanies this article includes a sample application, which can be used to implement, test and illustrate the concept of Image Contrast.
The Image Contrast sample application enables the user to load a source image from the local file system. Once a source image has been loaded the contrast can adjusted by dragging the contrast threshold trackbar control. Threshold values range from 100 to –100 inclusive, where positive values increase image contrast and negative values decrease image contrast. A threshold value of 0 results in no change.
The following image is a screenshot of the Image Contrast sample application in action:
The Contrast Extension Method
The sample source code provides the definition for the Contrast extension method. The method has been defined as an extension method targeting the Bitmap class.
The following code snippet details the implementation of the Contrast extension method:
public static Bitmap Contrast(this Bitmap sourceBitmap, int threshold) { BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte [sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
double contrastLevel = Math.Pow((100.0 + threshold) / 100.0, 2);
double blue = 0; double green = 0; double red = 0;
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { blue = ((((pixelBuffer[k] / 255.0) - 0.5) * contrastLevel) + 0.5) * 255.0;
green = ((((pixelBuffer[k + 1] / 255.0) - 0.5) * contrastLevel) + 0.5) * 255.0;
red = ((((pixelBuffer[k + 2] / 255.0) - 0.5) * contrastLevel) + 0.5) * 255.0;
if (blue > 255) { blue = 255; } else if (blue < 0) { blue = 0; }
if (green > 255) { green = 255; } else if (green < 0) { green = 0; }
if (red > 255) { red = 255; } else if (red < 0) { red = 0; }
pixelBuffer[k] = (byte)blue; pixelBuffer[k + 1] = (byte)green; pixelBuffer[k + 2] = (byte)red; }
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(pixelBuffer, 0, resultData.Scan0, pixelBuffer.Length); resultBitmap.UnlockBits(resultData);
return resultBitmap; }
In order to manipulate pixel colour component values directly we first need to lock the source Bitmap into memory by invoking the Bitmap.LockBits method. Once the source Bitmap is locked into memory we can copy the underlying pixel buffer using the Marshal.Copy method.
Based on the value of the threshold method parameter we calculate a contrast level. The formula implemented can be expressed as:
C = ((100.0 + T) / 100.0)2
Where C represents the calculated Contrast and T represents the variable threshold.
The next step involves iterating through the byte buffer of colour components. Notice how each iteration modifies an entire pixel by iterating by 4. The formula used in adjusting the contrast of a pixel’s colour components can be expressed as:
B = ( ( ( (B1 / 255.0) – 0.5) * C) + 0.5) * 255.0
G = ( ( ( (G1 / 255.0) – 0.5) * C) + 0.5) * 255.0
R = ( ( ( (R1 / 255.0) – 0.5) * C) + 0.5) * 255.0
In the formula the symbols B, G and R represent the contrast adjusted colour components Blue, Green and Red. B1, G1 and R1 represents the original values of the colour components Blue, Green and Red prior to being updated. The symbol C represents the contrast level calculated earlier.
Blue, Green and Red colour component values may only from 0 to 255 inclusive. We therefore need to test if the newly calculated values fall within the valid range of values.
The final operation performed by the Contrast method involves copying the modified pixel buffer into a newly created Bitmap object which will be returned to the calling code.
Sample Images
The original source Image used to create the sample images in this article has been licensed under the Creative Commons Attribution 2.0 Generic license. The original image is attributed to Luc Viatour and can be downloaded from Wikipedia. Luc Viatour’s website can be viewed at: http://www.lucnix.be
The Original Image |
|
Contrasted Images |
|
Related Articles
- C# How to: Image filtering by directly manipulating Pixel ARGB values
- C# How to: Image filtering implemented using a ColorMatrix
- C# How to: Blending Bitmap images using colour filters
- C# How to: Bitmap Colour Substitution implementing thresholds
- C# How to: Generating Icons from Images
- C# How to: Swapping Bitmap ARGB Colour Channels
- C# How to: Bitmap Pixel manipulation using LINQ Queries
- C# How to: Linq to Bitmaps – Partial Colour Inversion
- C# How to: Bitmap Colour Balance
- C# How to: Bi-tonal Bitmaps
- C# How to: Bitmap Colour Tint
- C# How to: Bitmap Colour Shading
- C# How to: Image Solarise
- C# How to: Image Contrast
- C# How to: Bitwise Bitmap Blending
Hello,
I thing that there are mistake in code. Insteda of
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
we need :
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format24bppRgb);
For to have correct result.
Thank You
Jiri
Hi Jiri,
Notice the next line locks the bits using PixelFormat.Format32bppArgb and then copies the byte array, which is formatted as 32 Bpp Argb.
Although, I would agree that the initial declaration is somewhat ambiguous.
Well spotted :)
Thanks,
Dewald
nic code !! thanks