Manipulate image pixels very quickly using LockBits wrapped in a class in C#

They Bitmap class's GetPixel and SetPixel methods let you easily get and set individual pixel values. They're easy to use but relatively slow. This program inverts the pixels in an image. When you click the No Lock Bits button, it loops through the pixels using GetPixel and SetPixel to invert the image. When you click the Lock Bits button, the program uses the following code to invert the image.
// Invert the image using Lockbits.The key pieces of code are in the middle. The program creates a Bitmap24 object (described shortly) and calls its LockBitmap method to lock the image in memory so it won't move while the program manipulates it. The code then loops through the image's pixels using the object's ImageBytes array to access the bytes in the image and inverts them. This array contains the red, green, and blue color components of the image's pixel values. It finishes by calling the object's UnlockBitmap method to unlock the bitmap. The following code shows the Bitmap24 class. This class represents a bitmap with 24-bit pixels with 1 byte for each pixel's red, green, and blue color component.
private void btnLockBits_Click(object sender, EventArgs e)
{
const byte BYTE_255 = 255;
Bitmap bm = new Bitmap(picHidden.Image);
this.Cursor = Cursors.WaitCursor;
DateTime start_time = DateTime.Now;
// Make a Bitmap24 object.
Bitmap24 bm24 = new Bitmap24(bm);
// Lock the bitmap.
bm24.LockBitmap();
// Invert the pixels.
for (int i = 0; i < bm.Height * bm24.RowSizeBytes; i++)
{
// bm24.ImageBytes[i] = Convert.ToByte(BYTE_255 - bm24.ImageBytes[i]);
bm24.ImageBytes[i] = (byte)(BYTE_255 - bm24.ImageBytes[i]);
}
// Unlock the bitmap.
bm24.UnlockBitmap();
picVisible.Image = bm;
DateTime stop_time = DateTime.Now;
this.Cursor = Cursors.Default;
TimeSpan elapsed_time = stop_time - start_time;
lblElapsed.Text = elapsed_time.TotalSeconds.ToString("0.000000");
}
public class Bitmap24The class's ImageBytes array provides direct access to the bitmap's pixel data. RowSizeBytes tells you how many bytes are in a row. Note that this may include padding bytes to align the data in memory so some of the bytes at the end of each tow may not actually be part of a pixel's data. The LockBitmap method calls the bitmap's LockBits method to lock the object's data. This prevents the data from moving while the program is manipulating it. It allocates an array big enough to hold the pixel data and then calls Marshal.Copy to copy the data from the bitmap into this array. Subroutine UnlockBitmap calls Marshal.Copy to copy the data from the pixel array back into the Bitmap object. It then calls the object's UnlockBits method. Note: This program takes about 3.5 times as long to invert its image using GetPixel and SetPixel as it does using LockBits. I was surprised to find, however, that a comparable Visual Basic program was about 20 times faster using LockBits! Although C# is not generally faster than Visual Basic, it was surprising that it was this much slower. As far as I can tell, the reason for the difference is in loop that inverts the byte values, specifically this statement:
{
// Provide public access to the picture's byte data.
public byte[] ImageBytes;
public int RowSizeBytes;
public const int PixelDataSize = 24;
// A reference to the Bitmap.
private Bitmap m_Bitmap;
// Save a reference to the bitmap.
public Bitmap24(Bitmap bm)
{
m_Bitmap = bm;
}
// Bitmap data.
private BitmapData m_BitmapData;
// Lock the bitmap's data.
public void LockBitmap()
{
// Lock the bitmap data.
Rectangle bounds = new Rectangle(
0, 0, m_Bitmap.Width, m_Bitmap.Height);
m_BitmapData = m_Bitmap.LockBits(bounds,
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
RowSizeBytes = m_BitmapData.Stride;
// Allocate room for the data.
int total_size = m_BitmapData.Stride * m_BitmapData.Height;
ImageBytes = new byte[total_size];
// Copy the data into the ImageBytes array.
Marshal.Copy(m_BitmapData.Scan0, ImageBytes, 0, total_size);
}
// Copy the data back into the Bitmap
// and release resources.
public void UnlockBitmap()
{
// Copy the data back into the bitmap.
int total_size = m_BitmapData.Stride * m_BitmapData.Height;
Marshal.Copy(ImageBytes, 0, m_BitmapData.Scan0, total_size);
// Unlock the bitmap.
m_Bitmap.UnlockBits(m_BitmapData);
// Release resources.
ImageBytes = null;
m_BitmapData = null;
}
}
bm24.ImageBytes[i] = (byte)(BYTE_255 - bm24.ImageBytes[i]);In C#, operations that involve byte values return an integer so this code needs to use (byte) to cast the result back into a byte. In Visual Basic, subtracting a byte value from another byte value results in a new byte value so no conversion is needed. I think the big difference in time is caused by C# promoting the byte values to integers and then the code converting the result back into a byte. If you discover a reasonable solution to this problem (without using unsafe methods to manipulate the byte array--that's another issue), please let me know.


I have question that how do we change the color using your method live if a pixel has black color, How? then How do I convert that pixel to red color?
Reply to this
The class used by this example isn't really set up to look at pixels easily. Take a look at this example:
Reply to this