Posts Tagged 'Open source'

C# How to: Image Colour Average

Article purpose

This article’s intension is focussed on providing a discussion on the tasks involved in implementing Image Colour Averaging. Pixel colour averages are calculated from neighbouring pixels.

Sample source code

This article is accompanied by a sample source code Visual Studio project which is available for download .

Using the Sample Application

The sample source code associated with this article includes a based sample application. The sample application is provided with the intention of illustrating the concepts explored in this article. In addition the sample application serves as a means of testing and replicating results.

By clicking the Load Image button users are able to select input/source from the local system. On the right hand side of the screen various controls enable the user to control the implementation of colour averaging. The three labelled Red, Green and Blue relates to whether an individual colour component is to be included in calculating colour averages.

The filter intensity can be specified through selecting a filter size from the dropdown , specifying higher values will result in output images expressing more colour averaging intensity.

Additional image filter effects can be achieved through implementing colour component shifting/swapping. When colour components are shifted left the result will be:

  • Blue is set to the original value of the Red component.
  • Red is set to the original value of the Green component.
  • Green is set to the original value of the Blue component.

When colour components are shifted right the result will be:

  • Red is set to the original value of the Blue component
  • Blue is set to the original value of the Green component
  • Green is set to the original value of the Red Component

Resulting can be saved by the user to the local file system by clicking the Save Image button. The following image is a screenshot of the Image Colour Average sample application in action:

Image Colour Average Sample Application

Averaging Colours

In this article and the accompanying sample source code colour averaging is implemented on a per pixel basis. An average colour value is calculated based on a pixel’s neighbouring pixels’ colour. Determining neighbouring pixels in the sample source code has been implemented in much the same method as . The major difference to is the absence of a fixed /.

Additional resulting visual effects can be achieved through various options/settings implemented whilst calculating colour averages. Additional options include being able to specify which colour component averages to implement. Furthermore colour components can be swapped/shifted around.

The sample source code implements the AverageColoursFilter , targeting the class. The extent or degree to which colour averaging will be evident in resulting can be controlled through specifying different values set to the matrixSize parameter. The matrixSize parameter in essence determines the number of neighbouring pixels involved in calculating an average colour.

The individual pixel colour components Red, Green and Blue can either be included or excluded in calculating averages. The three method boolean parameters applyBlue, applyGreen and applyRed will determine an individual colour components inclusion in averaging calculations. If a colour component is to be excluded from averaging the resulting will instead express the original source/input image’s colour component.

The intensity of a specific colour component average can be applied to another colour component by means of swapping/shifting colour components, which is indicated through the shiftType method parameter.

The following code snippet provides the implementation of the AverageColoursFilter :

public static Bitmap AverageColoursFilter(
                            this Bitmap sourceBitmap,  
                            int matrixSize,   
                            bool applyBlue = true, 
                            bool applyGreen = true, 
                            bool applyRed = true, 
                            ColorShiftType shiftType = 
                            ColorShiftType.None)  
{ 
    BitmapData sourceData =  
               sourceBitmap.LockBits(new Rectangle(0, 0, 
               sourceBitmap.Width, sourceBitmap.Height), 
               ImageLockMode.ReadOnly,  
               PixelFormat.Format32bppArgb); 

byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
int filterOffset = (matrixSize - 1) / 2; int calcOffset = 0;
int byteOffset = 0;
int blue = 0; int green = 0; int red = 0;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++) { for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++) { byteOffset = offsetY * sourceData.Stride + offsetX * 4;
blue = 0; green = 0; red = 0;
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
blue += pixelBuffer[calcOffset]; green += pixelBuffer[calcOffset + 1]; red += pixelBuffer[calcOffset + 2]; } }
blue = blue / matrixSize; green = green / matrixSize; red = red / matrixSize;
if (applyBlue == false) { blue = pixelBuffer[byteOffset]; }
if (applyGreen == false) { green = pixelBuffer[byteOffset + 1]; }
if (applyRed == false) { red = pixelBuffer[byteOffset + 2]; }
if (shiftType == ColorShiftType.None) { resultBuffer[byteOffset] = (byte)blue; resultBuffer[byteOffset + 1] = (byte)green; resultBuffer[byteOffset + 2] = (byte)red; resultBuffer[byteOffset + 3] = 255; } else if (shiftType == ColorShiftType.ShiftLeft) { resultBuffer[byteOffset] = (byte)green; resultBuffer[byteOffset + 1] = (byte)red; resultBuffer[byteOffset + 2] = (byte)blue; resultBuffer[byteOffset + 3] = 255; } else if (shiftType == ColorShiftType.ShiftRight) { resultBuffer[byteOffset] = (byte)red; resultBuffer[byteOffset + 1] = (byte)blue; resultBuffer[byteOffset + 2] = (byte)green; resultBuffer[byteOffset + 3] = 255; } } }
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(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap; }

The definition of the ColorShiftType :

public enum ColorShiftType  
{
    None, 
    ShiftLeft, 
    ShiftRight 
}

Sample

The original image used in generating the sample images that form part of this article, has been licensed under the Attribution-Share Alike , , and license. The can be from .

Original Image

Rose_Amber_Flush_20070601

Colour Average Blue Size 11

Colour Average Blue Size 11

Colour Average Blue Size 11 Shift Left

Colour Average Blue Size 11 Shift Left

Colour Average Blue Size 11 Shift Right

Colour Average Blue Size 11 Shift Right

Colour Average Green Size 11 Shift Right

Colour Average Green Size 11 Shift Right

Colour Average Green, Blue Size 11

Colour Average Green, Blue Size 11

Colour Average Green, Blue Size 11 Shift Left

Colour Average Green, Blue Size 11 Shift Left

Colour Average Green, Blue Size 11 Shift Right

Colour Average Green, Blue Size 11 Shift Right

Colour Average Red Size 11

Colour Average Red Size 11

Colour Average Red Size 11 Shift Left

Colour Average Red Size 11 Shift Left

Colour Average Red, Blue Size 11

Colour Average Red, Blue Size 11

Colour Average Red, Blue Size 11 Shift Left

Colour Average Red, Blue Size 11 Shift Left

Colour Average Red, Green Size 11

Colour Average Red, Green Size 11

Colour Average Red, Green Size 11 Shift Left

Colour Average Red, Green Size 11 Shift Left

Colour Average Red, Green Size 11 Shift Right

Colour Average Red, Green Size 11 Shift Right

Colour Average Red, Green, Blue Size 11

Colour Average Red, Green, Blue Size 11

Colour Average Red, Green, Blue Size 11 Shift Left

Colour Average Red, Green, Blue Size 11 Shift Left

Colour Average Red, Green, Blue Size 11 Shift Right

Colour Average Red, Green, Blue Size 11 Shift Right

Related Articles and Feedback

Feedback and questions are always encouraged. If you know of an alternative implementation or have ideas on a more efficient implementation please share in the comments section.

I’ve published a number of articles related to imaging and images of which you can find URL links here:

C# How to: Bitwise Bitmap Blending

Article Purpose

In this article you’ll find a discussion on the topic of blending  images into a single . Various possible methods can be employed in blending images. In this scenario image blending is achieved through means of bitwise operations, implemented on individual colour components Red, Green and Blue.

Sample source code

This article is accompanied by a sample source code Visual Studio project which is available for download here.

Download Sample Source Code

Bitwise Operations

In this article we will be implementing the following bitwise operators:

  • & Binary And
  • | Binary Or
  • ^ Exclusive Binary Or (XOR)

A good description of how these operators work can be found on MSDN:

The bitwise-AND operator compares each bit of its first operand to the corresponding bit of its second operand. If both bits are 1, the corresponding result bit is set to 1. Otherwise, the corresponding result bit is set to 0.

The bitwise-exclusive-OR operator compares each bit of its first operand to the corresponding bit of its second operand. If one bit is 0 and the other bit is 1, the corresponding result bit is set to 1. Otherwise, the corresponding result bit is set to 0.

The bitwise-inclusive-OR operator compares each bit of its first operand to the corresponding bit of its second operand. If either bit is 1, the corresponding result bit is set to 1. Otherwise, the corresponding result bit is set to 0.

Using the sample Application

Included with this article is a Visual Studio solution containing sample source code and a sample application. The Bitwise Bitmap Blending Sample application allows the user to select two input/source images from the local file system. Selected source images, once specified are displayed as previews with the majority of the application front end being occupied by an output .

The following image is a screenshot of the Bitwise Bitmap Blending application in action:

Bitwise Bitmap Blending

If the user decides to, blended images can be saved to the local file system by clicking the Save button.

The BitwiseBlend Extension method

The Sample Source provides the definition for the BitwiseBlend extension method. This method’s declaration indicates being an extension method targeting the class.

The BitwiseBlend method requires 4 parameters: the being blended with and three parameters all of type BitwiseBlendType. The enumeration defines the available blending types in regards to bitwise operations. The following code snippet provides the definition of the BitwiseBlendType enum:

public enum BitwiseBlendType  
{
   None,
   Or,
   And,
   Xor
}

The three BitwiseBlendType parameters relate to a pixel’s colour components: Red, Green and Blue.

The code snippet below details the implementation of the BitwiseBlend Extension method:

 public static Bitmap BitwiseBlend(this Bitmap sourceBitmap, Bitmap blendBitmap,  
                                     BitwiseBlendType blendTypeBlue, BitwiseBlendType  
                                     blendTypeGreen, BitwiseBlendType blendTypeRed) 
 { 
     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);
BitmapData blendData = blendBitmap.LockBits(new Rectangle(0, 0, blendBitmap.Width, blendBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] blendBuffer = new byte[blendData.Stride * blendData.Height]; Marshal.Copy(blendData.Scan0, blendBuffer, 0, blendBuffer.Length); blendBitmap.UnlockBits(blendData);
int blue = 0, green = 0, red = 0;
for (int k = 0; (k + 4 < pixelBuffer.Length) && (k + 4 < blendBuffer.Length); k += 4) { if (blendTypeBlue == BitwiseBlendType.And) { blue = pixelBuffer[k] & blendBuffer[k]; } else if (blendTypeBlue == BitwiseBlendType.Or) { blue = pixelBuffer[k] | blendBuffer[k]; } else if (blendTypeBlue == BitwiseBlendType.Xor) { blue = pixelBuffer[k] ^ blendBuffer[k]; }
if (blendTypeGreen == BitwiseBlendType.And) { green = pixelBuffer[k+1] & blendBuffer[k+1]; } else if (blendTypeGreen == BitwiseBlendType.Or) { green = pixelBuffer[k+1] | blendBuffer[k+1]; } else if (blendTypeGreen == BitwiseBlendType.Xor) { green = pixelBuffer[k+1] ^ blendBuffer[k+1]; }
if (blendTypeRed == BitwiseBlendType.And) { red = pixelBuffer[k+2] & blendBuffer[k+2]; } else if (blendTypeRed == BitwiseBlendType.Or) { red = pixelBuffer[k+2] | blendBuffer[k+2]; } else if (blendTypeRed == BitwiseBlendType.Xor) { red = pixelBuffer[k+2] ^ blendBuffer[k+2]; }
if (blue < 0) { blue = 0; } else if (blue > 255) { blue = 255; }
if (green < 0) { green = 0; } else if (green > 255) { green = 255; }
if (red < 0) { red = 0; } else if (red > 255) { red = 255; }
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; }

All image manipulation tasks performed by the BitwiseBlend Extension method are implemented by directly accessing a ’s underlying raw pixel data.

A first needs to be locked in memory by invoking the method. Once the object has been locked in memory the method instantiates a array, representing a pixel data buffer. Each element present in the data buffer reflects an individual colour component: Alpha, Red, Green or Blue.

Take note: Colour component ordering is opposite to the expected ordering. Colour components are ordered: Blue, Green, Red, Alpha. The short explanation for reverse ordering can be attributed to Little Endian CPU architecture and Blue being represented by the least significant bits of a pixel.

In order to perform bitwise operations on each pixel representing the specified the sample source code employs a for loop, iterating both data buffers. The possibility exists that the two s specified might not have the same size dimensions. Notice how the for loop defines two conditional statements, preventing the loop from iterating past the maximum bounds of the smallest .

Did you notice how the for loop increments the defined counter by four at each loop operation? The reasoning being that every four elements of the data buffer represents a pixel, being composed of: Blue, Green, Red and Alpha. Iterating four elements per iteration thus allows us to manipulate all the colour components of a pixel.

The operations performed within the for loop are fairly straight forward. The source code checks to determine which type of bitwise operation to implement per colour component. Colour components can only range from 0 to 255 inclusive, we therefore perform range checking before assigning calculated values back to the data buffer.

The final step performed involves creating a new resulting object and populating the new with the updated pixel data buffer.

Sample Images

In generating the sample images two source images were specified, a sunflower and bouquet of roses. The sunflower image has been released into the public domain and can be downloaded from Wikipedia. The bouquet of roses image has been licensed under the Creative Commons Attribution-Share Alike 3.0 Unported, 2.5 Generic, 2.0 Generic and 1.0 Generic license and can be downloaded from  Wikipedia.

The Original Images
Sunflower_USFWS
Bouquet_de_roses_roses
The Blended Images
SunflowerRoses
SunflowerRoses7
SunflowerRoses4
SunflowerRoses10
SunflowerRoses8
SunflowerRoses9

Related Articles

C# How to: Image Contrast

Article Purpose

Adjusting the contrast of an 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.

Download Sample Source Code

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 we learn the following quote:

Contrast is the difference in and/or that makes an object (or its representation in an image or display) distinguishable. In of the real world, contrast is determined by the difference in the and of the object and other objects within the same . Because the human visual system is more sensitive to contrast than absolute , 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:

ImageContrast

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 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 into memory by invoking the method. Once the source is locked into memory we can copy the underlying pixel buffer using the 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 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 object which will be returned to the calling code.

Sample Images

The original source 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

Ara_ararauna_Luc_Viatour

Contrasted Images

Parrot1

Parrot2

Parrot3

Parrot4

Related Articles

C# How to: Image Solarise

Article Purpose

The focus of this is set on exploring the concept of . In this the tasks required to perform   are entirely implemented on pixel data level. manipulation is only implemented in the form of updating the individual Alpha, Red, Green and Blue colour components expressed by pixels.

Sample source code

This is accompanied by a sample source code Visual Studio project which is available for download here.

Download Sample Source Code

What is Image Solarisation?

can be described as a form of Image inversion/colour inversion. A distinction can be made  between solarisation and colour inversion when taking into regard threshold values implemented when performing image solarisation.

Colour inversion can be implemented by subtracting each colour component from the value 255 and then assigning the relevant colour component to the result of subtraction. Colour inversion in terms of a formula can be expressed as follows:

R = 255 – R1

G = 255 – G1

B = 255 – B1

In this expression R1 G2 and B1 represent the original value of a pixel’s Red, Green and Blue colour components prior to being updated. when expressed as a formula could be represented as follows:

R = R1

If R1 < RT   Then R = 255 – R1

G = G1

If G1 < GT   Then G = 255 – G1

B = B1

If B1 < BT   Then B = 255 – B1

When implementing this expression R1 G1 and B1 represent the original value of a pixel’s Red, Green and Blue colour components prior to being updated. RT GT and BT in this scenario represent configurable threshold values applicable to individual colour components. If a colour component’s original value equates to less than the corresponding threshold value only then should colour inversion be implemented.

Using the sample Application

The concepts discussed in this are implemented/tested by means of a Windows Forms application. When using the sample application a user has the ability to browse the local file system in order to specify a source/input image. The user interface includes three trackbar controls, each related to a colour component threshold value. Threshold values can range from 0 to 255 inclusive. A threshold value of 0 results in no change, whereas a value of 255 equates to regular colour inversion.

Updated/filtered images can be saved to the local file system at any stage by clicking the ‘Save Image’ button.

The following image is a screenshot of the Image Solarise sample application in action:


ImageSolarise_Screenshot


The Solarise Extension Method

The sample source code provides the definition for the Solarise extension method. The method has been defined as an extension method targeting the class.

The following code snippet details the implementation of the Solarise extension method:

 public static Bitmap Solarise(this Bitmap sourceBitmap, byte blueValue, 
                              byte greenValue, byte redValue) 
{ 
    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);
byte byte255 = 255;
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { if (pixelBuffer[k] < blueValue) { pixelBuffer[k] = (byte)(byte255 - pixelBuffer[k]); }
if (pixelBuffer[k + 1] < greenValue) { pixelBuffer[k + 1] = (byte)(byte255 - pixelBuffer[k + 1]); }
if (pixelBuffer[k + 2] < redValue) { pixelBuffer[k + 2] = (byte)(byte255 - pixelBuffer[k + 2]); } }
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 into memory by invoking the Bitmap.LockBits method. Once the source is locked into memory we can copy the underlying pixel buffer using the Marshal.Copy method.

The next step involves iterating through the byte buffer of colour components. Notice how each iteration modifies an entire pixel by iterating by 4. As discussed earlier when implementing image solarisation the associated formula employs threshold comparison, which determines whether or not colour inversion should be applied.

The final operation performed by the Solarise method involves copying the modified pixel buffer into a newly created object which will be returned to the calling code.

Sample Images

The original source image used to create all of the solarised sample images in this has been licensed under the Creative Commons Attribution-Share Alike 3.0 Unported, 2.5 Generic, 2.0 Generic and 1.0 Generic license. The original image is attributed to Kenneth Dwain Harrelson and can be downloaded from Wikipedia.

The Original Image

 

Monarch_In_May

Solarised Images

 

Butterfly1 Butterfly2
Butterfly3 Butterfly4
Butterfly5 Butterfly7
Butterfly8 Butterfly10

Related Articles

C# How to: Bitmap Colour Shading

Article Purpose

The objective of this article is focussed on exploring the concept of applying to images. The various Bitmap manipulation operations detailed in this article are all exclusively implemented by processing raw pixel data. No traditional GDI+ drawing operations are required in implementing .

Processor10 Processor11 Processor12 Processor13

Sample source code

This article is accompanied by a sample source code Visual Studio project which is available for download here.

Download Sample Source Code

Processor5 Processor6 Processor7 Processor9

Using the sample Application

This article has been written from a practical implementation point of view. The concepts detailed throughout this article are all coupled with a corresponding source code implementation. A sample application has been provided with this article. The  sample application has been implemented in the architecture of a , of which a complete source code implementation has also been provided. The sample application facilitates implementing and testing the concepts surrounding as discussed in this article.

Processor31 Processor22 Processor16 Processor47

In using the Bitmap Shading sample application users have the ability to select a source/input from the local file system. The user interface facilitates implementing through three trackbar controls, each associated with a  colour component. The value range of each trackbar control is defined from 0 to 100 inclusive. A value of 100 equates to no change and a value of 0 equating to the most possible change.

Processor51 Processor50 Processor49 Processor57

Users are able to save to the local file system any modified images by clicking the ‘Save Image’ button and providing a file path and file name.

The screenshot below illustrates the Bitmap Shading application in action:


BitmapShading_Screenshot


Processor25 Processor24 Processor23 Processor22

The ColorShade Extension method

The ColorShade extension method forms the crux of this article. The ColorShade method has been defined as an extension method targeting the class. It is within this method that all raw pixel manipulation occurs. In essence each pixel expressed by the specified image will be extracted upon which calculations are performed updating the value of Alpha, Red, Green and Blue colour components.

Processor28 Processor29 Processor30 Processor31

The following code snippet details the implementation of the ColorShade extension method:

 public static Bitmap ColorShade(this Bitmap sourceBitmap, float blueShade, 
                                float greenShade, float redShade) 
{
    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);
float blue = 0; float green = 0; float red = 0;
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { blue = pixelBuffer[k] * blueShade; green = pixelBuffer[k + 1] * greenShade; red = pixelBuffer[k + 2] * redShade;
if (blue < 0) { blue = 0; }
if (green < 0) { green = 0; }
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; }
Processor26 Processor1 Processor51 Processor24

In order to manipulate pixel colour component values directly we first need to lock the source into memory by invoking the method. Once the source is locked into memory we can copy the underlying pixel buffer using the method.

Processor39 Processor13 Processor64 Processor16

The next step involves iterating through the buffer of colour components. Notice how each iteration modifies an entire pixel by iterating by 4. The formula being implemented can be expressed as:

R = R1 * RS

G = G1 * GS

B = B1 * BS

In the stated equation R1 G1 and B1 represents the original value of a colour component and RS GS and BS equates to the percentage of shading expressed by each colour component. Take note that in this scenario shading percentages are defined in terms of a fractional value. As an example 45% would result in a shading factor of 0.45.

Processor59 Processor60 Processor61 Processor62

Sample Images

The original image file used in this article was authored by Konstantin Lanzet and is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license, and can be downloaded from Wikipedia.

The Original Image

KL_Intel_D8086

Colour Shaded Images

Processor6
Processor11
Processor38
Processor20
Processor22
Processor31

Related Articles


Dewald Esterhuizen

Blog Stats

  • 869,866 hits

Enter your email address to follow and receive notifications of new posts by email.

Join 228 other subscribers

Archives

RSS SoftwareByDefault on MSDN

  • An error has occurred; the feed is probably down. Try again later.