If we need image processing in c#, the fastest way for a programmer to access the pixels of a Bitmap is to use the SetPixel() and GetPixel() .NET methods. Unfortunately the fastest way for a programmer, often does’nt correspond to the fastest way for the program: These functions are REALLY slow, so if your objective is to analyze all pixels of an image, or, still worse, to make an elaboration of many images, you are not adviced to use them. Fortunately the c# is a very language flexible: It allows to make things at high levels, but often gives the opportunity to do low-level programming when is necessary.

For semplicity, in this article i will deal only the images at 8bpp (8 bit per pixel): every byte will be a pixel.

The only way to work fast with an image is to use the pointer to the image data. Work with the pointers is like working with the char * in c++: Initialliy, the pointer will point to the memory address of the first pixel of the bitmap. Increasing the pointer we would gradually obtain the values of the next pixels.
Pointer to image example
A great advantages when using pointers when we have large data, is the fact that the processor is not forced to make mathematical operation in order to read the next element. I will explain myself with an example:

for (int x = 0; x < width; x++)
{
	for (int y = 0; y < height; y++)
	{
		byte b = bmp.GetPixel(x,y);
	}
}

The GetPixel method in order to return the x,y pixel, will take the pointer of the image (the pointer to the first pixel) and will add (width * y) + x. Our instruction is within a double for cycle in which we slide all pixels of the images, so if our image is 1024*768 pixel, the previous example will make more than 3 million useless multiplications and sums in order to calculate every time the x,y offset. It’s clear now that we are not speaking about little numbers, but of really large values, able to slow down a fast computer, so we must always take care to the optimization of our c# code when we are doing image processing.
When we are reading an image from a pointer, we must always take care of the stride of an image, that is the number of byte occupied from one line of pixel. This is not equivalent to the width of the image multipied by its bpp: the byte of every rows is rounded off to groups of 4 bytes, so if we have an image of 30 pixels, the stride of every row will be 32 pixels, so for every row we will have 2 unused byte!

Bitmap bmp = new Bitmap("Dzamir.bmp");

Is the bitmap that we want to analyze.
By the code:

BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
System.IntPtr bmpScan0 = bmpData.Scan0;

We take the pointer of the image. The method LockBits() locks the bitmap in the system memory and we can use it the BitmapData returned from it to access the image data, while the Scan0 method returns the pointer to the first row of the image.

Now it’s necessary to begin an unsafe code block, in order to inform the c# that we are working with pointers.

unsafe
{
	// insert here dungerous code :) }

In the unsafe block now we can start writing the code we need to initialize the pointers:

 byte * bmpPtr = (byte *)(void *)bmpScan0;
int nOffset = stride - bData.Width;

In the nOffset variable we saved the difference between the stride and the number of pixel of the row, and we will sum this value at the end of a line. Now we can insert the double for cycle to cycle all pixels of the image:

for (int x = 0; x < width; x++)
{
	for (int y = 0; y < height; y++)
	{
		// Read the pixel and increase the pointer
		byte pixelXY = *(bmpPtr++);
	}
	bmpPtr += nOffset;
}

Now the code is really elegant and also much more optimized! Of cource this example does’nt do nothing, but this piece of code can be used as a stub from which creating other functions for image processing.
At the end do not forget to invoke the method:

bmp.UnlockBits(bData);

To unlock the Bitmap from the system memory.

digg_url =”http://digg.com/programming/Optimized_access_to_Bitmaps”;

kick it on DotNetKicks.com


Se sei interessato a questo post, potresti anche provare a leggere:

  • No related posts