11,720,785 members (81,831 online)

# Painless yet unsafe grayscale conversion in C#

, 17 Apr 2006 122.9K 41
 Rate this:
This is an article using pointer arithmetic for a quick conversion of an image to grayscale.

## Introduction

I was busy trying to write a motion detection algorithm in C#. To determine motion, I compared change values from a previous image by calculating the absolute value of the pixel difference. For doing this, grayscale is easier and probably faster.

## Background

If MIT thinks using grayscale for motion detection is a good idea, then I am definitely on the right track with my motion detection algorithm. For a reference, see the Cog project.

I got the code for the image loop from here because I originally attempted to use `SetPixel` which is painfully slow.

## Using the code

If you use this code in a multithreaded environment, be aware that the parameter image will be locked until this method unlocks it. This will raise an exception when another code attempts to lock the bitmap, such as a customer `OnPaint`. To avoid this, you can always make a copy of the bitmap.

Here is the entire method:

```public Bitmap processImage(Bitmap image){
Bitmap returnMap = new Bitmap(image.Width, image.Height,
PixelFormat.Format32bppArgb);
BitmapData bitmapData1 = image.LockBits(new Rectangle(0, 0,
image.Width, image.Height),
PixelFormat.Format32bppArgb);
BitmapData bitmapData2 = returnMap.LockBits(new Rectangle(0, 0,
returnMap.Width, returnMap.Height),
PixelFormat.Format32bppArgb);
int a = 0;
unsafe {
byte* imagePointer1 = (byte*)bitmapData1.Scan0;
byte* imagePointer2 = (byte*)bitmapData2.Scan0;
for(int i = 0; i < bitmapData1.Height; i++) {
for(int j = 0; j < bitmapData1.Width; j++) {
// write the logic implementation here
a = (imagePointer1[0] + imagePointer1[1] +
imagePointer1[2])/3;
imagePointer2[0] = (byte)a;
imagePointer2[1] = (byte)a;
imagePointer2[2] = (byte)a;
imagePointer2[3] = imagePointer1[3];
//4 bytes per pixel
imagePointer1 += 4;
imagePointer2 += 4;
}//end for j
//4 bytes per pixel
imagePointer1 += bitmapData1.Stride -
(bitmapData1.Width * 4);
imagePointer2 += bitmapData1.Stride -
(bitmapData1.Width * 4);
}//end for i
}//end unsafe
returnMap.UnlockBits(bitmapData2);
image.UnlockBits(bitmapData1);
return returnMap;
}//end processImage```

The `PixelFormat` is crucial. This format determines the layout of the bitmap data. This is why the literal 4 is hard-coded (actually, it is hard-coded because I am lazy). If you use a different `PixelFormat`, you will need to determine the layout and offset for those formats.

```BitmapData bitmapData1 = image.LockBits(new Rectangle(0, 0,
image.Width, image.Height),
PixelFormat.Format32bppArgb);
BitmapData bitmapData2 = returnMap.LockBits(new Rectangle(0, 0,
returnMap.Width, returnMap.Height),

This code locks the bitmap and returns its image data. Since I am converting the entire image to grayscale, I lock the entire bitmap. It is possible to lock a smaller area.

```byte* imagePointer1 = (byte*)bitmapData1.Scan0;
byte* imagePointer2 = (byte*)bitmapData2.Scan0;```

Pointer arithmetic is unsafe in C# Too bad because it is fast! Although, it is strange getting Access Violation errors in C#. If you change the code, test thoroughly using Mathematics to make sure you don't overwrite any protected memory. `Scan0` is the pointer to the first byte of the bitmap's data.

```for(int i = 0; i < bitmapData1.Height; i++) {
for(int j = 0; j < bitmapData1.Width; j++) {```

The astute will immediately recognize this will only work if the bitmaps are the same size. And fortunately, this always is since this method will create the `returnMap` bitmap based on the input bitmap. An incorrect loop will also cause an Access Violation.

```imagePointer2[0] = (byte)a; //Array index 0 is blue
imagePointer2[1] = (byte)a; //Array index 1 is green
imagePointer2[2] = (byte)a; //Array index 2 is red
imagePointer2[3] = imagePointer1[3]; //Array index 3 is alpha```

See the comments in the code snippet above, to understand the layout of the bitmap data.

`a = (imagePointer1[0] + imagePointer1[1] + imagePointer1[2])/3;`

Average the three color components in the original bitmap. Keep the alpha the same, otherwise your grayscale will also cause undesired blending.

```imagePointer1 += 4;
imagePointer2 += 4;```

Move forward 4 bytes:

```imagePointer1 += bitmapData1.Stride - (bitmapData1.Width * 4);
imagePointer2 += bitmapData1.Stride - (bitmapData1.Width * 4);```

This is definitely the most confusing part. See this for a picture of what is happening. The width of a bitmap is actually the composed width and an unused buffer to pad the bitmap to be a multiple of 4. Width + buffer = stride. It works, so good enough for me.

```returnMap.UnlockBits(bitmapData2);
image.UnlockBits(bitmapData1);
return returnMap;```

`UnlockBits` unlocks the bitmap data, probably a good thing to do.

## Points of Interest

People much smarter than me: MIT Cog Project.

## Possible Errors

I use the bitmap data from `bitmapData1` for both `bitmapData1` and `bitmapData2`. This could lead to unexpected errors if the data were some how different. However, in testing, everything works fine. A lot more prudent coder would verify a lot of things before putting this sort of code into anything critical.

If there are any more errors, I am quite sure that the overly critical among you will gladly point them out as glaring criticisms.

A list of licenses authors might use can be found here

## Share

 Architect ERL GLOBAL, INC United States
My company is ERL GLOBAL, INC. I develop Custom Programming solutions for business of all sizes. I also do Android Programming as I find it a refreshing break from the MS.

## You may also be interested in...

 First PrevNext
 My vote of 5 manoj kumar choubey26-Feb-12 21:17 manoj kumar choubey 26-Feb-12 21:17
 My vote of 5 manuhk15-Dec-10 6:09 manuhk 15-Dec-10 6:09
 Locking a smaller portion of bitmap Al_S23-Feb-09 2:38 Al_S 23-Feb-09 2:38
 Re: Locking a smaller portion of bitmap Ennis Ray Lynch, Jr.23-Feb-09 2:56 Ennis Ray Lynch, Jr. 23-Feb-09 2:56
 Re: Locking a smaller portion of bitmap Al_S23-Feb-09 3:20 Al_S 23-Feb-09 3:20
 Re: Locking a smaller portion of bitmap Ennis Ray Lynch, Jr.23-Feb-09 3:29 Ennis Ray Lynch, Jr. 23-Feb-09 3:29
 Re: Locking a smaller portion of bitmap Al_S23-Feb-09 3:36 Al_S 23-Feb-09 3:36
 Re: Locking a smaller portion of bitmap Ennis Ray Lynch, Jr.23-Feb-09 3:41 Ennis Ray Lynch, Jr. 23-Feb-09 3:41
 Re: Locking a smaller portion of bitmap Al_S23-Feb-09 4:00 Al_S 23-Feb-09 4:00
 Re: Locking a smaller portion of bitmap Al_S23-Feb-09 4:25 Al_S 23-Feb-09 4:25
 Locking a smaller portion of bitmap Al_S23-Feb-09 2:36 Al_S 23-Feb-09 2:36
 accessing a sub-portion of image Al_S20-Feb-09 3:12 Al_S 20-Feb-09 3:12
 pointer indexing kanza azhar22-Apr-08 11:42 kanza azhar 22-Apr-08 11:42
 Re: pointer indexing Ennis Ray Lynch, Jr.22-Apr-08 11:53 Ennis Ray Lynch, Jr. 22-Apr-08 11:53
 Typo in ImageLockMode? dwieringa5-Sep-07 11:46 dwieringa 5-Sep-07 11:46
 You would think Ennis Ray Lynch, Jr.5-Sep-07 12:33 Ennis Ray Lynch, Jr. 5-Sep-07 12:33
 Simple improvement to make it 3 times faster mattrs18-May-07 1:01 mattrs 18-May-07 1:01
 I can believe I missed that Ennis Ray Lynch, Jr.18-May-07 2:42 Ennis Ray Lynch, Jr. 18-May-07 2:42
 Re: Simple improvement to make it 3 times faster Terespl23-Jul-09 22:15 Terespl 23-Jul-09 22:15
 Stride Question and Issue [modified] jouwpaard21-Sep-06 1:03 jouwpaard 21-Sep-06 1:03
 Re: Stride Question and Issue Ennis Ray Lynch, Jr.21-Sep-06 13:54 Ennis Ray Lynch, Jr. 21-Sep-06 13:54
 Cheesy alternative Ravi Bhavnani18-Apr-06 12:58 Ravi Bhavnani 18-Apr-06 12:58
 Re: Cheesy alternative mrBussy25-Apr-06 4:12 mrBussy 25-Apr-06 4:12
 Use ColorMatrix Ray Hayes18-Apr-06 11:00 Ray Hayes 18-Apr-06 11:00
 :( Too Slow elynch18-Apr-06 11:55 elynch 18-Apr-06 11:55
 Last Visit: 31-Dec-99 18:00     Last Update: 4-Sep-15 15:23 Refresh 12 Next »