none
How can i change the method that compare two images to be faster? RRS feed

  • Question

  • Today im using GetPixes and SetPixel and its very slow.

    I searched in google and found that if im scanning the whole images i better use LockBits (Unsafe or Marshal ).

    But all the codes i found are compating the images as if the size is true or false.

    My method take two images scan the pixels and create new image with the different pixels on black background.

    And for example when im running my porogram or when im using a numericupdown to change ion the method the variable tolerancenumeric its doing all the method again and it take time alomst 2-3 seconds each time i change the numericupdown value or when im running the program first time and its doing the method for the first time it take like 3 second for the program to load.

    Can someone show m maybe how my code can be using the LockBits so it will make it faster ? In the end i need that the method will do what it does now but faster.

    This is the method:

    public static Bitmap CloudsOnly(Bitmap bitmapwithclouds, Bitmap bitmapwithoutclouds)
            {
                Color color;
                Color backgroundColor = Color.Black;
    
                CreateErrorsArray(bitmapwithclouds, bitmapwithoutclouds);
    
                int tolerance = tolerancenum * tolerancenumeric + tolerancenumeric * tolerancenumeric + tolerancenumeric * tolerancenumeric;
                Bitmap newbitmap = new Bitmap(512, 512);
                for (int x = 0; x < bitmapwithclouds.Width; x++)
                {
                    for (int y = 0; y < bitmapwithclouds.Height; y++)
                    {
                        int error = errorsArray[x, y];
    
                        if (error < tolerance)
                        {
                            color = backgroundColor;
                        }
                        else
                        {
                            color = bitmapwithclouds.GetPixel(x, y);
                        }
                        newbitmap.SetPixel(x, y, color);
                    }
                }
                newbitmap.Save(@"d:\test\newbitmap.jpg");
                return newbitmap;
            }

    I could use a backgroundworker maybe but it wont make it faster .

    I need somehow to convert my method to use LockBits i think but im not sure how to do it.

     
    Thursday, December 19, 2013 3:10 PM

Answers

  • Hi Chocolade,

    Here is a good article about Fast Bitmap comparison:

    http://www.dotnetexamples.com/2012/07/fast-bitmap-comparison-c.html 

    The author have done bitmap comparison using Bitmap.GetPixel() method to check pixel by pixel, he is able to reduce this comparison time to almost 40 ms using Bitmap.LockBits() and Bitmap.UnlockBits() using the following code. 

    private bool CompareBitmaps(Image left, Image right)
    {
        if (object.Equals(left, right))
            return true;
        if (left == null || right == null)
            return false;
        if (!left.Size.Equals(right.Size) || !left.PixelFormat.Equals(right.PixelFormat))
            return false;
     
        Bitmap leftBitmap = left as Bitmap;
        Bitmap rightBitmap = right as Bitmap;
        if (leftBitmap == null || rightBitmap == null)
            return true;
     
        #region Optimized code for performance
     
        int bytes = left.Width * left.Height * (Image.GetPixelFormatSize(left.PixelFormat) / 8);
     
        bool result = true;
        byte[] b1bytes = new byte[bytes];
        byte[] b2bytes = new byte[bytes];
     
        BitmapData bmd1 = leftBitmap.LockBits(new Rectangle(0, 0, leftBitmap.Width - 1, leftBitmap.Height - 1), ImageLockMode.ReadOnly, leftBitmap.PixelFormat);
        BitmapData bmd2 = rightBitmap.LockBits(new Rectangle(0, 0, rightBitmap.Width - 1, rightBitmap.Height - 1), ImageLockMode.ReadOnly, rightBitmap.PixelFormat);
     
        Marshal.Copy(bmd1.Scan0, b1bytes, 0, bytes);
        Marshal.Copy(bmd2.Scan0, b2bytes, 0, bytes);
     
        for (int n = 0; n <= bytes - 1; n++)
        {
            if (b1bytes[n] != b2bytes[n])
            {
                result = false;
                break;
            }
        }
     
        leftBitmap.UnlockBits(bmd1);
        rightBitmap.UnlockBits(bmd2);
     
        #endregion
     
        return result;
    }
    


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    • Marked as answer by Chocolade1972 Saturday, December 21, 2013 2:19 AM
    Friday, December 20, 2013 8:45 AM

All replies

  • Im trying this method:

    private static void GetPixels(Bitmap bmp1, Bitmap bmp2)
            {
                BitmapData data = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height),
       ImageLockMode.ReadOnly, bmp1.PixelFormat);  // make sure you check the pixel format as you will be looking directly at memory
    
                unsafe
                {
                    byte* ptrSrc = (byte*)data.Scan0;
    
                    // example assumes 24bpp image.  You need to verify your pixel depth
                    // loop by row for better data locality
                    for (int y = 0; y < data.Height; ++y)
                    {
                        for (int x = 0; x < data.Width; ++x)
                        {
                            // windows stores images in BGR pixel order
                            byte r = ptrSrc[2];
                            byte g = ptrSrc[1];
                            byte b = ptrSrc[0];
    
                            // next pixel in the row
                            ptrSrc += 3;
                        }
                    }
                }
            }

    But im not sure how to use it with two bitmaps and to do the comparison and get the colors and pixels as i did in my original method.

    Thursday, December 19, 2013 6:41 PM
  • Take a look at this article:

    http://bobpowell.net/lockingbits.aspx

    Thursday, December 19, 2013 11:04 PM
  • Hi Chocolade,

    Here is a good article about Fast Bitmap comparison:

    http://www.dotnetexamples.com/2012/07/fast-bitmap-comparison-c.html 

    The author have done bitmap comparison using Bitmap.GetPixel() method to check pixel by pixel, he is able to reduce this comparison time to almost 40 ms using Bitmap.LockBits() and Bitmap.UnlockBits() using the following code. 

    private bool CompareBitmaps(Image left, Image right)
    {
        if (object.Equals(left, right))
            return true;
        if (left == null || right == null)
            return false;
        if (!left.Size.Equals(right.Size) || !left.PixelFormat.Equals(right.PixelFormat))
            return false;
     
        Bitmap leftBitmap = left as Bitmap;
        Bitmap rightBitmap = right as Bitmap;
        if (leftBitmap == null || rightBitmap == null)
            return true;
     
        #region Optimized code for performance
     
        int bytes = left.Width * left.Height * (Image.GetPixelFormatSize(left.PixelFormat) / 8);
     
        bool result = true;
        byte[] b1bytes = new byte[bytes];
        byte[] b2bytes = new byte[bytes];
     
        BitmapData bmd1 = leftBitmap.LockBits(new Rectangle(0, 0, leftBitmap.Width - 1, leftBitmap.Height - 1), ImageLockMode.ReadOnly, leftBitmap.PixelFormat);
        BitmapData bmd2 = rightBitmap.LockBits(new Rectangle(0, 0, rightBitmap.Width - 1, rightBitmap.Height - 1), ImageLockMode.ReadOnly, rightBitmap.PixelFormat);
     
        Marshal.Copy(bmd1.Scan0, b1bytes, 0, bytes);
        Marshal.Copy(bmd2.Scan0, b2bytes, 0, bytes);
     
        for (int n = 0; n <= bytes - 1; n++)
        {
            if (b1bytes[n] != b2bytes[n])
            {
                result = false;
                break;
            }
        }
     
        leftBitmap.UnlockBits(bmd1);
        rightBitmap.UnlockBits(bmd2);
     
        #endregion
     
        return result;
    }
    


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    • Marked as answer by Chocolade1972 Saturday, December 21, 2013 2:19 AM
    Friday, December 20, 2013 8:45 AM
  • I tried to use the example but i dont understand how do i use it like in my code to get the color of the pixels set the pixels and return a new bitmap ? In the example i will get a true or false. Its not like in my method.

    This is what i did for now:

    private static void GetPixels(Bitmap bmp1, Bitmap bmp2)
            {
                int bytes = bmp1.Width * bmp1.Height * (Image.GetPixelFormatSize(bmp1.PixelFormat) / 8);
                byte[] b1bytes = new byte[bytes];
                byte[] b2bytes = new byte[bytes];
    
                BitmapData bmd1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width - 1, bmp1.Height - 1), ImageLockMode.ReadOnly, bmp1.PixelFormat);
                BitmapData bmd2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width - 1, bmp2.Height - 1), ImageLockMode.ReadOnly, bmp2.PixelFormat);
    
                Marshal.Copy(bmd1.Scan0, b1bytes, 0, bytes);
                Marshal.Copy(bmd2.Scan0, b2bytes, 0, bytes);
    
                for (int n = 0; n <= bytes - 1; n++)
                {
                    if (b1bytes[n] != b2bytes[n])
                    {
                        break;
                    }
                }
    
                bmp1.UnlockBits(bmd1);
                bmp2.UnlockBits(bmd2);
            }

    Friday, December 20, 2013 11:06 AM
  • Im trying to use this code now:

    public static Bitmap LockBits(Bitmap bmp)
            {
                PixelFormat pxf = PixelFormat.Format24bppRgb;
                Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
                BitmapData bmpData =
                bmp.LockBits(rect, ImageLockMode.ReadWrite, pxf);
                IntPtr ptr = bmpData.Scan0;
                int numBytes = bmpData.Stride * bmp.Height;
                byte[] rgbValues = new byte[numBytes];
                Marshal.Copy(ptr, rgbValues, 0, numBytes);
                for (int counter = 0; counter < rgbValues.Length; counter += 6)
                    rgbValues[counter] = (byte)tolerancenumeric;
                Marshal.Copy(rgbValues, 0, ptr, numBytes);
                bmp.UnlockBits(bmpData);
                bmp.Save(@"d:\testbmplockbits.bmp");
                return bmp;
            }

    Instead tolerancenumeric variable there was the value 10.

    In Form1 i use it like this:

    private void numericUpDown1_ValueChanged(object sender, EventArgs e)
            {
                  CloudEnteringAlert.tolerancenum = (int)numericUpDown1.Value;
                  pictureBox1.Image = CloudEnteringAlert.LockBits(bitmapwithclouds);
            }

    And still when im clicking on the numericupdown to change the value its slow. Take about 2-4 seconds to make a change. Why this method is not working fast ?

    Friday, December 20, 2013 10:48 PM