Image processing with lockbits, alternative to getpixel?

14,500

Solution 1

You wouldn't want to rely on getPixel() for image processing; it's okay to make an occasional call to get a point value (e.g. on mouseover), but in general it's preferable to do image processing in image memory or in some 2D array that you can convert to a Bitmap when necessary.

To start, you might try writing a method that using LockBits/UnlockBits to extract an array that is convenient to manipulate. Once you're done manipulating the array, you can write it back to a bitmap using a different LockBits/UnlockBits function.

Here's some sample code I've used in the past. The first function returns a 1D array of values from a Bitmap. Since you know the bitmap's width, you can convert this 1D array to a 2D array for further processing. Once you're done processing, you can call the second function to convert the (modified) 1D array into a bitmap again.

public static byte[] Array1DFromBitmap(Bitmap bmp){
    if (bmp == null) throw new NullReferenceException("Bitmap is null");

    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
    IntPtr ptr = data.Scan0;

    //declare an array to hold the bytes of the bitmap
    int numBytes = data.Stride * bmp.Height;
    byte[] bytes = new byte[numBytes];

    //copy the RGB values into the array
    System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, numBytes);

    bmp.UnlockBits(data);

    return bytes;           
}

public static Bitmap BitmapFromArray1D(byte[] bytes, int width, int height)
{
    Bitmap grayBmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
    Rectangle grayRect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height);
    BitmapData grayData = grayBmp.LockBits(grayRect, ImageLockMode.ReadWrite, grayBmp.PixelFormat);
    IntPtr grayPtr = grayData.Scan0;

    int grayBytes = grayData.Stride * grayBmp.Height;
    ColorPalette pal = grayBmp.Palette;

    for (int g = 0; g < 256; g++){
        pal.Entries[g] = Color.FromArgb(g, g, g);
    }

    grayBmp.Palette = pal;

    System.Runtime.InteropServices.Marshal.Copy(bytes, 0, grayPtr, grayBytes);

    grayBmp.UnlockBits(grayData);
    return grayBmp;
}

These methods makes assumptions about the Bitmap pixel format that may not work for you, but I hope the general idea is clear: use LockBits/UnlockBits to extract an array of bytes from a Bitmap so that you can write and debug algorithms most easily, and then use LockBits/UnlockBits again to write the array to a Bitmap again.

For portability, I would recommend that your methods return the desired data types rather than manipulating global variables within the methods themselves.

If you've been using getPixel(), then converting to/from arrays as described above could speed up your code considerably for a small investment of coding effort.

Solution 2

Ok where to start. Better you understand what you are doing with lockBits. First of all make sure, that you dont overwrite your byte array with.

LockBits(small);              
LockBits(large);

due to the second call all the first call does is locking your image and that is not good since you doesn't unlock it again. So add another byte array that represents the image. You can do something like this

LockBits(small, true);              
LockBits(large, false);

and change your Lockbits method

static public void LockBits(Bitmap source, bool flag)                        
{   
...
Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);

if(flag)
   PixelsSmall=Pixels;
else
   PixelsLarge=Pixels;
}

where PixelsLarge and PixelsSmall are globals and Pixels isn't Those 2 contain your image. Now you have to compare it. Now you have to compare each "set of bytes" therefore you have to know the Pixelformat. Is it 32b/pix 24 or only 8 (ARGB,RGB,grayscale) Let's take ARGB images. In this case a set would consist of 4 bytes (=32/8) I am not sure about the order but I think the order of one set is ABGR or BGRA.

Hope this may help you. If you don't figure out how to compare the right pixels then ask again. Ah and dont forget to use the UnlockBits command.

Share:
14,500
m.k.
Author by

m.k.

Python and Javascript.

Updated on June 18, 2022

Comments

  • m.k.
    m.k. almost 2 years

    I am trying to increase my image detection class using lockbits, yet this cause problems with the code and thus it does not run. How can i go about using lockbits and getpixel at the same time in order to speed up image detection, or can someone show me an alternative which is just as fast?

    code:

    static IntPtr Iptr = IntPtr.Zero;
        static BitmapData bitmapData = null;
        static public byte[] Pixels { get; set; }
        static public int Depth { get; private set; }
        static public int Width { get; private set; }
        static public int Height { get; private set; }
    
        static public void LockBits(Bitmap source)
    
        {
                // Get width and height of bitmap
                Width = source.Width;
                Height = source.Height;
    
                // get total locked pixels count
                int PixelCount = Width * Height;
    
                // Create rectangle to lock
                Rectangle rect = new Rectangle(0, 0, Width, Height);
    
                // get source bitmap pixel format size
                Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
    
    
                // Lock bitmap and return bitmap data
                bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                             source.PixelFormat);
    
                // create byte array to copy pixel values
                int step = Depth / 8;
                Pixels = new byte[PixelCount * step];
                Iptr = bitmapData.Scan0;
    
                // Copy data from pointer to array
                Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
    
        }
    
    
         static public bool SimilarColors(int R1, int G1, int B1, int R2, int G2, int B2, int Tolerance)
        {
            bool returnValue = true;
            if (Math.Abs(R1 - R2) > Tolerance || Math.Abs(G1 - G2) > Tolerance || Math.Abs(B1 - B2) > Tolerance)
            {
                returnValue = false;
            }
            return returnValue;
        }
    
    
         public bool findImage(Bitmap small, Bitmap large, out Point location)
         {
             unsafe
             {
                 LockBits(small);
                 LockBits(large);
                 //Loop through large images width
                 for (int largeX = 0; largeX < large.Width; largeX++)
                 {
                     //And height
                     for (int largeY = 0; largeY < large.Height; largeY++)
                     {
                         //Loop through the small width
                         for (int smallX = 0; smallX < small.Width; smallX++)
                         {
                             //And height
                             for (int smallY = 0; smallY < small.Height; smallY++)
                             {
                                 //Get current pixels for both image
                                 Color currentSmall = small.GetPixel(smallX, smallY);
                                 Color currentLarge = large.GetPixel(largeX + smallX, largeY + smallY);
                                 //If they dont match (i.e. the image is not there)
    
                                 if (!colorsMatch(currentSmall, currentLarge))
                                     //Goto the next pixel in the large image
    
                                     goto nextLoop;
                             }
                         }
                         //If all the pixels match up, then return true and change Point location to the top left co-ordinates where it was found
                         location = new Point(largeX, largeY);
                         return true;
                     //Go to next pixel on large image
                     nextLoop:
                         continue;
                     }
                 }
                 //Return false if image is not found, and set an empty point
                 location = Point.Empty;
                 return false;
             }
         }
    
  • Ken
    Ken almost 10 years
    You saved me at least several headaches, thanks a ton.
  • Nyerguds
    Nyerguds over 6 years
    You should check the stride of the target bytes though, and copy per line. After all, on a new object that may differ from the 8-bit data you got. And if Array1DFromBitmap doesn't compact the data to exactly the width, it should definitely output that stride, or the data can't be processed correctly.