Article Purpose
The purpose of this article is to explore and illustrate the concept of creating bi-tonal bitmap images. Colour images are manipulated in such a fashion to only express two colours. The colours expressed are configurable. A threshold value determines which of the two configured colours will be applied to a pixel.
Sample source code
This article is accompanied by a sample source code Visual Studio project which is available for download here.
Using the sample Application
The concepts explored in this article are easily illustrated using the Sample Application provided with the sample source code. The sample application is implemented as a Windows Forms application.
The Bi-tonal Bitmap application enables the user to load an input image file from the local file system. The user interface defines two panels representing the two colours used when creating the resulting bi-tonal Bitmap. When clicking on either panel the user will be presented with a colour dialog, allowing the user to change the colour of the specific panel.
The user interface also provides a trackbar which allows the user to set the threshold used to calculate if a pixel colour should be set to the dark colour or light colour value.
If the user desires to save resulting bi-tonal images to the local file system the sample application makes provision through the Save Button.
The following image provides a screenshot of the Bi-tonal Bitmap application in action:
The Bitonal Extension Method
The Bitonal Extension method defines all of the operations involved in creating Bi-tonal images. This method is an extension method targeting the Bitmap class. Note that the Bitonal extension method manipulates pixel colour components directly, in other words updating a pixel’s Alpha, Red, Green and Blue values directly.
The following code snippet provides the definition of the Bitonal method:
public static Bitmap Bitonal(this Bitmap sourceBitmap, Color darkColor, Color lightColor, 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);
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { if (pixelBuffer[k] + pixelBuffer[k + 1] + pixelBuffer[k + 2] <= threshold) { pixelBuffer[k] = darkColor.B; pixelBuffer[k + 1] = darkColor.G; pixelBuffer[k + 2] = darkColor.R; } else { pixelBuffer[k] = lightColor.B; pixelBuffer[k + 1] = lightColor.G; pixelBuffer[k + 2] = lightColor.R; } }
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.
The next step involves iterating through the byte buffer of colour components. Notice how each iteration modifies an entire pixel by iterating by 4. In order to determine to which colour a pixel should be set, the sum of Red, Green and Blue colour components is to the threshold parameter.
The last step performed is to copy the modified pixel buffer into a new Bitmap object.
Sample Images
The original source image used to create all of the bi-tonal sample images in this article 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
Bi-tonal 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