Search code examples
c#.netbitmaplockbits

Locking Bits Fails To Detect Pixels


I am creating a program that scans all the pixels of an image, and whenever it finds a pixel that contains the color pink. It makes the pixel black. But it doesn't seem to find a pink pixel when there is two of them on the image. I do not know if I am using LockBits correctly, maybe I am using it wrong. Can someone please help me solve this I would greatly appreciate it.

Here is the code below:

            Bitmap bitmap = pictureBox1.Image as Bitmap;
            System.Drawing.Imaging.BitmapData d = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
            IntPtr ptr = d.Scan0;
            byte[] rgbs = new byte[Math.Abs(d.Stride) * bitmap.Height];
            Marshal.Copy(ptr, rgbs, 0, rgbs.Length);
            Graphics g = pictureBox1.CreateGraphics();
            for (int index = 2; index < rgbs.Length; index += 3)
            {


                if (rgbs[index] == 255 &&  rgbs[index - 1] == 0 && rgbs[index - 2] == 255) // If color = RGB(255, 0, 255) Then ...
                {
                     // This never gets executed!
                     rgbs[index] = 0;
                     rgbs[index - 1] = 0;
                     rgbs[index - 2] = 0;

                }
            }
            Marshal.Copy(rgbs, 0, ptr, rgbs.Length); // Copy rgb values back to the memory location of the bitmap.
            pictureBox1.Image = bitmap;
            bitmap.UnlockBits(d); 

Solution

  • You don't need to copy the pixel data into an array. The point of LockBits is it gives you direct (unsafe) access the memory. You can just iterate the pixels and change them as you find them. You will need to know the format of the image to do this successfully.

      BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10), 
                           ImageLockMode.ReadOnly, bm.PixelFormat);
      // Blue, Green, Red, Alpha (Format32BppArgb)
      int pixelSize=4;
    
      for(int y=0; y<bmd.Height; y++)
      {
        byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);
        for(int x=0; x<bmd.Width; x++) 
        {
          int offSet = x*pixelSize;
          // read pixels
          byte blue = row[offSet];
          byte green = row[offSet+1];
          byte red = row[offSet+2];
          byte alpha = row[offSet+3];
    
          // set blue pixel
          row[x*pixelSize]=255;
        }
      }
    

    It's a little more tricky in VB than C# as VB has no knowledge of pointers and requires the use of the marshal class to access unmanaged data. Here's some sample code. (For some reason I originally though this was a VB question).

      Dim x As Integer
      Dim y As Integer
      ' Blue, Green, Red, Alpha (Format32BppArgb)
      Dim PixelSize As Integer = 4 
      Dim bmd As BitmapData = bm.LockBits(new Rectangle(0, 0, 10, 10),
                                          ImageLockMode.ReadOnly, bm.PixelFormat)
    
      For y = 0 To bmd.Height - 1
        For x = 0 To bmd.Width - 1
          Dim offSet As Int32 = (bmd.Stride * y) + (4 * x)
          ' read pixel data
          Dim blue As Byte = Marshal.ReadByte(bmd.Scan0, offSet)
          Dim green As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 1)
          Dim red As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 2)
          Dim alpha As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 3)
          ' set blue pixel
          Marshal.WriteByte(bmd.Scan0, offSet , 255)
        Next
      Next