Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast work with Bitmaps in C#

I need to access each pixel of a Bitmap, work with them, then save them to a Bitmap.

Using Bitmap.GetPixel() and Bitmap.SetPixel(), my program runs slowly.

How can I quickly convert Bitmap to byte[] and back?

I need a byte[] with length = (4 * width * height), containing RGBA data of each pixel.

like image 721
AndreyAkinshin Avatar asked Oct 13 '09 21:10

AndreyAkinshin


2 Answers

You can do it a couple of different ways. You can use unsafe to get direct access to the data, or you can use marshaling to copy the data back and forth. The unsafe code is faster, but marshaling doesn't require unsafe code. Here's a performance comparison I did a while back.

Here's a complete sample using lockbits:

/*Note unsafe keyword*/ public unsafe Image ThresholdUA(float thresh) {     Bitmap b = new Bitmap(_image);//note this has several overloads, including a path to an image      BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);      byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);      /*This time we convert the IntPtr to a ptr*/     byte* scan0 = (byte*)bData.Scan0.ToPointer();      for (int i = 0; i < bData.Height; ++i)     {         for (int j = 0; j < bData.Width; ++j)         {             byte* data = scan0 + i * bData.Stride + j * bitsPerPixel / 8;              //data is a pointer to the first byte of the 3-byte color data             //data[0] = blueComponent;             //data[1] = greenComponent;             //data[2] = redComponent;         }     }      b.UnlockBits(bData);      return b; } 

Here's the same thing, but with marshaling:

/*No unsafe keyword!*/ public Image ThresholdMA(float thresh) {     Bitmap b = new Bitmap(_image);      BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);      /* GetBitsPerPixel just does a switch on the PixelFormat and returns the number */     byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);      /*the size of the image in bytes */     int size = bData.Stride * bData.Height;      /*Allocate buffer for image*/     byte[] data = new byte[size];      /*This overload copies data of /size/ into /data/ from location specified (/Scan0/)*/     System.Runtime.InteropServices.Marshal.Copy(bData.Scan0, data, 0, size);      for (int i = 0; i < size; i += bitsPerPixel / 8 )     {         double magnitude = 1/3d*(data[i] +data[i + 1] +data[i + 2]);          //data[i] is the first of 3 bytes of color      }      /* This override copies the data back into the location specified */     System.Runtime.InteropServices.Marshal.Copy(data, 0, bData.Scan0, data.Length);      b.UnlockBits(bData);      return b; } 
like image 101
davidtbernal Avatar answered Sep 28 '22 13:09

davidtbernal


You can use Bitmap.LockBits method. Also if you want to use parallel task execution, you can use the Parallel class in System.Threading.Tasks namespace. Following links have some samples and explanations.

  • http://csharpexamples.com/fast-image-processing-c/
  • http://msdn.microsoft.com/en-us/library/dd460713%28v=vs.110%29.aspx
  • http://msdn.microsoft.com/tr-tr/library/system.drawing.imaging.bitmapdata%28v=vs.110%29.aspx
like image 24
turgay Avatar answered Sep 28 '22 12:09

turgay