Click here to Skip to main content
13,407,694 members (50,811 online)
Click here to Skip to main content
Add your own
alternative version

Stats

53.8K views
13 bookmarked
Posted 18 Jun 2012

Why the use of GetPixel and SetPixel is so inefficient!

, 8 Jan 2018
Rate this:
Please Sign up or sign in to vote.
Couple of seconds to process a one megapixel picture… what the hell?

The Bitmap class provides two simple methods: GetPixel and SetPixel used respectively to retrieve a point of image (as the Color structure) and set a point of image. The following code illustrates how to retrieve/set all the pixels in a bitmap:

private void GetSetPixel(Bitmap image) {
   for (int x = 0; x < image.Width; x++) {
      for (int y = 0; y < image.Height; y++) {
         Color pixel = image.GetPixel(x, y);
         image.SetPixel(x, y, Color.Black);
      }
   } 
}

As shown, review and modification of pixels is extremely simple. Unfortunately, behind the simplicity of the code lies a serious performance trap. While for a small number of references to image points, the speed at which GetPixel and SetPixel work is good enough, for larger images it is not the case. The graph presented below can serve as a proof of that. It shows the results of 10 tests* which consisted of 10-fold invocation of previously shown GetSetPixel method for images 100x100 and 1000x1000 pixels in size.

Results of speed testing Bitmap's GetPixel and SetPixel methods

 

The average test time for an image measuring 100 by 100 pixels was 543 milliseconds. This speed is acceptable if the image processing is not done frequently. The performance problem is, however, clearly visible when you try to use an image of size 1000 per 1000 pixels. The execution of the test in this case takes an average of more than 41 seconds - more than 4 sec. on a single call to GetSetPixel (seriously!).

Why so slow?

The low efficiency is due to the fact that access to the pixel is not a simple reference to a memory area. Each getting or setting of color is associated with the invocation of a .NET Framework method, which is a wrapper for a native function contained in gdiplus.dll. This call is through the mechanism of P/Invoke (Platform Invocation), which is used to communicate from managed code to unmanaged API (an API outside of the .NET Framework). So for a bitmap of 1000x1000 pixels, there will be 1 million calls to the GetPixel method that besides the validation of parameters uses the native GdipBitmapGetPixel function. Before returning the color information, the GDI+ function has to perform such operations as calculating the position of bytes responsible for the description of the desired pixel… A similar situation occurs in the case of the SetPixel method.

Look at the following code of the Bitmap.GetPixel method obtained with the .NET Reflector (System.Drawing.dll, .NET Framework 2.0):

public Color GetPixel(int x, int y) {
   int argb = 0;
   if ((x < 0) || (x >= base.Width)) {
      throw new ArgumentOutOfRangeException("x", SR.GetString("ValidRangeX"));
   }
   if ((y < 0) || (y >= base.Height)) {
      throw new ArgumentOutOfRangeException("y", SR.GetString("ValidRangeY"));
   }
   
   int status = SafeNativeMethods.Gdip.GdipBitmapGetPixel
    (new HandleRef(this, base.nativeImage), x, y, out argb);
   if (status != 0) {
      throw SafeNativeMethods.Gdip.StatusException(status);
   }
   return Color.FromArgb(argb);
}

Here is the import of the GDI + function:

[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, SetLastError=true, 
ExactSpelling=true)]
internal static extern int GdipBitmapGetPixel(HandleRef bitmap, int x, int y, out int argb);

* I have tested on this laptop: HP Pavilion dv5, AMD Turion X2 Dual-Core Mobile RM-70, 3 GB RAM, Vista Home Premium

Update (2013-07-10): Unfortunately I haven't found time to write an article about a solution to this performance problem but there are some useful hints in my comment.

Update (2013-11-07): I've written an article (...finally) about fast pixel operations. No need to use crappy Get/SetPixel anymore :) Click here.

Update (2018-01-08): If you really want to do some complex and efficient image processing then you should use specialized library like OpenCV. Few months ago I've written "Detecting a Drone - OpenCV in .NET for Beginners (Emgu CV 3.2, Visual Studio 2017)" blog post series that will help you do it...

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author


You may also be interested in...

Pro
Pro

Comments and Discussions

 
PraiseThanks for good post! Pin
Pioner_hero14-Feb-18 3:07
memberPioner_hero14-Feb-18 3:07 
GeneralRe: Thanks for good post! Pin
morzel14-Feb-18 5:33
membermorzel14-Feb-18 5:33 
Praisemy rating 5 out of 5 Pin
Erasmus218-Jan-18 19:28
memberErasmus218-Jan-18 19:28 
GeneralMy vote of 5 Pin
GregoryW11-Jul-13 3:40
memberGregoryW11-Jul-13 3:40 
GeneralMy vote of 5 Pin
Volynsky Alex10-Jun-13 2:30
memberVolynsky Alex10-Jun-13 2:30 
QuestionFast access Pin
YvesDaoust10-Jun-13 0:34
memberYvesDaoust10-Jun-13 0:34 
AnswerRe: Fast access Pin
morzel10-Jun-13 1:21
membermorzel10-Jun-13 1:21 
Yup, you are right!

Here is an example of getting and setting of pixels (C# with unsafe, very efficient):

private void Locked() { 
   BitmapData imageData = image.LockBits(new Rectangle(0, 0, image.Width, 
   image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 
   int bytesForPixel = 3; 
   
   unsafe
   {
       for (int x = 0; x < imageData.Width; x++)
       {
           int x0 = x * bytesForPixel;
           int x1 = x0 + 1;
           int x2 = x1 + 1;

           for (int y = 0; y < imageData.Height; y++)
           {
               byte* row = (byte*)imageData.Scan0 + (y * imageData.Stride);

               // Get color components (watch out for order!)
               byte pixelB = row[x0];
               byte pixelG = row[x1];
               byte pixelR = row[x2];

               // Set pixel to black
               row[x0] = 0;
               row[x1] = 0;
               row[x2] = 0;
           }
       }
   }

   image.UnlockBits(imageData); 
} 


If you can't use unsafe, check out System.Runtime.InteropServices.Marshal class. You may also want to look at System.Drawing.Imaging.ColorMatrix!

Update: This code is fast but its not optimal - check out this article!
GeneralRe: Fast access Pin
KP Lee19-Jun-13 20:11
memberKP Lee19-Jun-13 20:11 
GeneralRe: Fast access Pin
morzel20-Jun-13 2:26
membermorzel20-Jun-13 2:26 
GeneralRe: Fast access Pin
KP Lee20-Jun-13 21:28
memberKP Lee20-Jun-13 21:28 
GeneralRe: Fast access Pin
Klaus-Werner Konrad11-Jul-13 4:16
memberKlaus-Werner Konrad11-Jul-13 4:16 
QuestionWorkaround would be useful Pin
MR_SAM_PIPER18-Jun-12 14:43
memberMR_SAM_PIPER18-Jun-12 14:43 
AnswerRe: Workaround would be useful Pin
Chona117119-Jun-12 4:17
memberChona117119-Jun-12 4:17 
AnswerRe: Workaround would be useful - code sample Pin
morzel20-Jun-12 5:47
membermorzel20-Jun-12 5:47 
GeneralMy vote of 5 Pin
SledgeHammer0118-Jun-12 11:21
memberSledgeHammer0118-Jun-12 11:21 
GeneralRe: My vote of 5 Pin
WiStRoM11-Jul-13 5:49
memberWiStRoM11-Jul-13 5:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.180221.1 | Last Updated 8 Jan 2018
Article Copyright 2012 by morzel
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid