Question

i have two images and i want to compare two image and want to get difference. i search google and found a link from where i copy paste the code for image comparison using win32 api. so this is the url http://blog.bobcravens.com/2009/04/create-a-remote-desktop-viewer-using-c-and-wcf/

here i am pasting the code.

        private void button1_Click(object sender, EventArgs e)
        {
        Bitmap _prevBitmap = new Bitmap(@"d:\prev.jpg");
        Bitmap _newBitmap = new Bitmap(@"d:\current.jpg");

        Rectangle bounds = GetBoundingBoxForChanges(_prevBitmap, _newBitmap);
        if (bounds == Rectangle.Empty)
        {
        }

        Bitmap diff = new Bitmap(bounds.Width, bounds.Height);
        Graphics g = Graphics.FromImage(diff);
        g.DrawImage(_newBitmap, 0, 0, bounds, GraphicsUnit.Pixel);
        g.Dispose();

        // Set the current bitmap as the previous to prepare
        //    for the next screen capture.
        //
        diff.Save(@"d:\diff.bmp");

        //return diff;
    }

    private Rectangle GetBoundingBoxForChanges(Bitmap _prevBitmap, Bitmap _newBitmap)
    {
        // The search algorithm starts by looking
        //    for the top and left bounds. The search
        //    starts in the upper-left corner and scans
        //    left to right and then top to bottom. It uses
        //    an adaptive approach on the pixels it
        //    searches. Another pass is looks for the
        //    lower and right bounds. The search starts
        //    in the lower-right corner and scans right
        //    to left and then bottom to top. Again, an
        //    adaptive approach on the search area is used.
        //

        // Note: The GetPixel member of the Bitmap class
        //    is too slow for this purpose. This is a good
        //    case of using unsafe code to access pointers
        //    to increase the speed.
        //

        // Validate the images are the same shape and type.
        //
        if (_prevBitmap.Width != _newBitmap.Width ||
            _prevBitmap.Height != _newBitmap.Height ||
            _prevBitmap.PixelFormat != _newBitmap.PixelFormat)
        {
            // Not the same shape...can't do the search.
            //
            return Rectangle.Empty;
        }

        // Init the search parameters.
        //
        int width = _newBitmap.Width;
        int height = _newBitmap.Height;
        int left = width;
        int right = 0;
        int top = height;
        int bottom = 0;

        BitmapData bmNewData = null;
        BitmapData bmPrevData = null;
        try
        {
            // Lock the bits into memory.
            //
            bmNewData = _newBitmap.LockBits(
                new Rectangle(0, 0, _newBitmap.Width, _newBitmap.Height),
                ImageLockMode.ReadOnly, _newBitmap.PixelFormat);
            bmPrevData = _prevBitmap.LockBits(
                new Rectangle(0, 0, _prevBitmap.Width, _prevBitmap.Height),
                ImageLockMode.ReadOnly, _prevBitmap.PixelFormat);

            // The images are ARGB (4 bytes)
            //
            int numBytesPerPixel = 4;

            // Get the number of integers (4 bytes) in each row
            //    of the image.
            //
            int strideNew = bmNewData.Stride / numBytesPerPixel;
            int stridePrev = bmPrevData.Stride / numBytesPerPixel;

            // Get a pointer to the first pixel.
            //
            // Note: Another speed up implemented is that I don't
            //    need the ARGB elements. I am only trying to detect
            //    change. So this algorithm reads the 4 bytes as an
            //    integer and compares the two numbers.
            //
            System.IntPtr scanNew0 = bmNewData.Scan0;
            System.IntPtr scanPrev0 = bmPrevData.Scan0;

            // Enter the unsafe code.
            //
            unsafe
            {
                // Cast the safe pointers into unsafe pointers.
                //
                int* pNew = (int*)(void*)scanNew0;
                int* pPrev = (int*)(void*)scanPrev0;

                // First Pass - Find the left and top bounds
                //    of the minimum bounding rectangle. Adapt the
                //    number of pixels scanned from left to right so
                //    we only scan up to the current bound. We also
                //    initialize the bottom & right. This helps optimize
                //    the second pass.
                //
                // For all rows of pixels (top to bottom)
                //
                for (int y = 0; y < _newBitmap.Height; ++y)
                {
                    // For pixels up to the current bound (left to right)
                    //
                    for (int x = 0; x < left; ++x)
                    {
                        // Use pointer arithmetic to index the
                        //    next pixel in this row.
                        //
                        if ((pNew + x)[0] != (pPrev + x)[0])
                        {
                            // Found a change.
                            //
                            if (x < left)
                            {
                                left = x;
                            }
                            if (x > right)
                            {
                                right = x;
                            }
                            if (y < top)
                            {
                                top = y;
                            }
                            if (y > bottom)
                            {
                                bottom = y;
                            }
                        }
                    }

                    // Move the pointers to the next row.
                    //
                    pNew += strideNew;
                    pPrev += stridePrev;
                }

                // If we did not find any changed pixels
                //    then no need to do a second pass.
                //
                if (left != width)
                {
                    // Second Pass - The first pass found at
                    //    least one different pixel and has set
                    //    the left & top bounds. In addition, the
                    //    right & bottom bounds have been initialized.
                    //    Adapt the number of pixels scanned from right
                    //    to left so we only scan up to the current bound.
                    //    In addition, there is no need to scan past
                    //    the top bound.
                    //

                    // Set the pointers to the first element of the
                    //    bottom row.
                    //
                    pNew = (int*)(void*)scanNew0;
                    pPrev = (int*)(void*)scanPrev0;
                    pNew += (_newBitmap.Height - 1) * strideNew;
                    pPrev += (_prevBitmap.Height - 1) * stridePrev;

                    // For each row (bottom to top)
                    //
                    for (int y = _newBitmap.Height - 1; y > top; y--)
                    {
                        // For each column (right to left)
                        //
                        for (int x = _newBitmap.Width - 1; x > right; x--)
                        {
                            // Use pointer arithmetic to index the
                            //    next pixel in this row.
                            //
                            if ((pNew + x)[0] != (pPrev + x)[0])
                            {
                                // Found a change.
                                //
                                if (x > right)
                                {
                                    right = x;
                                }
                                if (y > bottom)
                                {
                                    bottom = y;
                                }
                            }
                        }

                        // Move up one row.
                        //
                        pNew -= strideNew;
                        pPrev -= stridePrev;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            int xxx = 0;
        }
        finally
        {
            // Unlock the bits of the image.
            //
            if (bmNewData != null)
            {
                _newBitmap.UnlockBits(bmNewData);
            }
            if (bmPrevData != null)
            {
                _prevBitmap.UnlockBits(bmPrevData);
            }
        }

        // Validate we found a bounding box. If not
        //    return an empty rectangle.
        //
        int diffImgWidth = right - left + 1;
        int diffImgHeight = bottom - top + 1;
        if (diffImgHeight < 0 || diffImgWidth < 0)
        {
            // Nothing changed
            return Rectangle.Empty;
        }

        // Return the bounding box.
        //
        return new Rectangle(left, top, diffImgWidth, diffImgHeight);
    }

when GetBoundingBoxForChanges() call then i am getting error and error message is Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

error occur at this code if ((pNew + x)[0] != (pPrev + x)[0])

so i am not being able to find out the reason. how to fix this error. please guide. thanks

Was it helpful?

Solution

  bmNewData = _newBitmap.LockBits(...., _newBitmap.PixelFormat);

This algorithm implicitly assumes that a pixel has 4 bytes and can be addressed with an int*. It however fails to provide that guarantee. Asking for _newBitmap.PixelFormat in LockBits() is not sufficient, that just asks for the same format that the original image used. You'll get a hard crash if the images are 24bpp for example, very common.

Explicitly ask for 32bppArgb instead.

OTHER TIPS

Instead of the Win32 API you could use a managed image processing library such as AForge.NET. In the documentation look for the AForge.Imaging.Filters.Difference class. It works with Bitmap objects so you will have to make minimal changes to your program.

Bitmap overlayImage;
Bitmap sourceImage;

//ToDo: Load the two images.        

// Create filter.
Difference filter = new Difference(overlayImage);
// Apply the filter and return a new bitmap that is the difference between the source and overlay images.
Bitmap resultImage = filter.Apply(sourceImage);

// If you don't want a new image the you can apply the filter directly to the source image.
filter.ApplyInPlace(sourceImage);

This is the mechanism that I use to calculate image differences in C#. Note that it need to be compiled with the unsafe directive. Hope it helps:

using System;
using System.Collections.Generic;
using System.Text;

using System.Drawing;
using System.Drawing.Imaging;
using log4net;

namespace ImageDiff
{
    public class ImageDifferences
    {
        private static ILog mLog = LogManager.GetLogger("ImageDifferences");

        public static unsafe Bitmap PixelDiff(Image a, Image b)
        {
            if (!a.Size.Equals(b.Size)) return null;
            if (!(a is Bitmap) || !(b is Bitmap)) return null;

            return PixelDiff(a as Bitmap, b as Bitmap);
        }

        public static unsafe Bitmap PixelDiff(Bitmap a, Bitmap b)
        {
            Bitmap output = new Bitmap(
                Math.Max(a.Width, b.Width),
                Math.Max(a.Height, b.Height),
                PixelFormat.Format32bppArgb);

            Rectangle recta = new Rectangle(Point.Empty, a.Size);
            Rectangle rectb = new Rectangle(Point.Empty, b.Size);
            Rectangle rectOutput = new Rectangle(Point.Empty, output.Size);

            BitmapData aData = a.LockBits(recta, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            BitmapData bData = b.LockBits(rectb, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            BitmapData outputData = output.LockBits(rectOutput, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

            try
            {
                byte* aPtr = (byte*)aData.Scan0;
                byte* bPtr = (byte*)bData.Scan0;
                byte* outputPtr = (byte*)outputData.Scan0;
                int len = aData.Stride * aData.Height;

                for (int i = 0; i < len; i++)
                {
                    // For alpha use the average of both images (otherwise pixels with the same alpha won't be visible)
                    if ((i + 1) % 4 == 0)
                        *outputPtr = (byte)((*aPtr + *bPtr) / 2);
                    else
                        *outputPtr = (byte)~(*aPtr ^ *bPtr);

                    outputPtr++;
                    aPtr++;
                    bPtr++;
                }

                return output;
            }
            catch (Exception ex)
            {
                mLog.Error("Error calculating image differences: " + ex.Message);
                return null;
            }
            finally
            {
                a.UnlockBits(aData);
                b.UnlockBits(bData);
                output.UnlockBits(outputData);
            }
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top