#C# Image Processing Performance - Unsafe vs. Safe code, Part II

published mar 14 2008

In Part I of this article, I outlined a method of performing pixel-level operations on an image that required neither unsafe code, nor the use of the GetPixel and SetPixel methods, which are notoriously slow. The one thing I neglected to include was an actual comparison of the IntPtr and pointer methods with the GetPixel\SetPixel methods. As such, I present such a method below. It looks tantalizingly simple, but we’ll soon see that it is basically worthless.

public Image ThresholdGS(float thresh)
	Bitmap b = new Bitmap(_image);

	for (int i = 0; i < b.Height; ++i)
		for (int j = 0; j < b.Width; ++j)
			Color c = b.GetPixel(j, i);

			double magnitude = 1 / 3d * (c.B+c.G+c.R);
			if (magnitude < thresh)
				b.SetPixel(j,i, Color.FromArgb(255,255,255));

	return b;


As outlined in the previous post, I created a form that would allow me to run each threshold method repeatedly to find an average time per threshold operation. The results are given in the table below. For these tests, an arbitrary threshold of 125 was chosen. The image used 24 bits-per-color and had a size of 479 x 700 pixels.

Algorithm Total Time (ms) % Difference
unsafe 194 0
IntPtr 262 35%
GetPixel 238,299 (~4 min.) 122,734%
unsafe 1951 0
IntPtr 2361 21%
unsafe 9783 0
IntPtr 11,499 17%

The first observation is simple: GetPixel\SetPixel SUCKS! It took longer to do 10 operations using that method than it took to do 500 using either of the other two. We also see that the IntPtr method, while not a terrible alternative (if, for some reason, the use of unsafe code was strictly not allowed) is also moderately slower than the pointer method.

Test Program

I wrote up a simple test program to perform these tests. It also includes a subjective test, wherein one can select the threshold method and then move a slider to set different threshold values to see how quickly the image changes. The subjective test agrees with the results above.

If you poke around in the program a bit, you’ll also discover a couple of extras, including some tests of using a square-root-of-squares type method for finding the magnitude of a pixel (applied to thresholding, and grayscaling). There is also a method that allows convolution filters to be applied to the image, though editing the filters is very cumbersome. Expect to see new, more-complete versions this program in the future.

The code is now available on GitHub.

Most of the code from this series is in Process.cs