When using GDI+ method you can optimize the process of bitmap manipulation  seven  times.I am implementing an image scaling algorithm at my University Seam Carving . While working on this project I realized that simply accessing pixels by the GetPixel and SetPixel methods is too slow.

GetPixel , SetPixel

This is a really easy solution. We have two functions GetPixel and SetPixel both  have x and y coordinate as argument. GetPixel returns a color and SetPixel sets a color on the coordinate.

GetPixel(int x,int y)
SetPixel(int x,int y,Color color)

With the use of those functions, we can easily iterate through all the pixels in the image by simply modifying the x and y variable.

for (int y =0; y < _bmp.Height; y++)
{
    for (int x =0; x < _bmp.Width; x++)
    {
        _bmp.SetPixel(x,y,color);
        Color c = _bmp.GetPixel(x,y);
     }
}

Pros

  • Code is more readable.
  • Easy to implement and use.

Cons

-Low efficiency.

This method is really great for simple graphical operations when speed isn't important. In my scenario with the Seam Carving algorithm, I had an efficiency problem using this method. Seam Carving algorithm is making a lot of calculations in order to find seams. The time is growing exponentially with the size of the image.

GDI+ and LockBits.

Bitmap class contains two methods LockBits and UnlockBits , with them, we can get access and work directly on the memory. LockBits method returns BitmapData object, which is used to describe the memory sector. In this method, we have to use the pointers. That's why our class should have an unsafe keyword.

With BitmapData object we need to define some variables.

int _pixelSize =3;
byte* _current =(byte*)(void*)_bmd.Scan0;
int _nWidth = _bmp.Width * _pixelSize;
int _nHeight = _bmp.Height;
  • ScanO memory address which defines the beginning of our Bitmap
  • _nWidth how many bots in one row With this we can iterate through the image.
for (int y =0; y < _nHeight; y++)
{
    for (int x =0; x < _nWidth;x++ )
    {
         if (x % _pixelSize ==0|| x ==0)
        {
             SetColor(new Color.Black);
         }
      _current++;
     }
}

We have to remember that current variable is only a pointer  to a memory address

This condition ensures that the current will always point to the beginning of the next pixel. In my example pixel is represented by  three bytes, this value is stored in the _pixelSize variable.

With _current pointer we can access pixel values by using the indexer.

void SetColor(,Color color)
{
     _current[0]= color.R;
     _current [1] = color.G;
     _current [2] = color.B;
}

Pros:

  • Speed

Cons:

-Code is confusing but we can wrap the logic in readable functions

  • Implementation is complicated in the beginning

- unmanaged code

Comparision between Gdi+ Lockbits and GetPixel SetPixel

GDI+ LockBits (ms) GetPixel SetPixel (ms)
Read Pixel 21 157
Write Pixel 15 153
Iterate Image 226 1496

As you can see the GDI+ Lockbits are almost seven times faster than GetPixel SetPixel method

Link to the project.

 

Share