6

How could I generate a System.Drawing.Image that contains the differences between the pixels of two other images?

Something similar to GitHub does, but written in C#

The algorithm that GiHub uses is implemented in javascript. There is a context-blender project that replicates Photoshop blend modes.

Do you know if is translated to C# or a similar algorithm that has the same quality level? I need to manage also transparent images (with alpha channel).

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219

2 Answers2

15

Here's a quick and dirty implementation:

void Main()
{
    var a = (Bitmap)Image.FromFile("image1.png");
    var b = (Bitmap)Image.FromFile("image2.png");
    var diff = PixelDiff(a, b);
}

unsafe Bitmap PixelDiff(Bitmap a, Bitmap b)
{
    Bitmap output = new Bitmap(a.Width, a.Height, PixelFormat.Format32bppArgb);
    Rectangle rect = new Rectangle(Point.Empty, a.Size);
    using (var aData = a.LockBitsDisposable(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
    using (var bData = b.LockBitsDisposable(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
    using (var outputData = output.LockBitsDisposable(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb))
    {
        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;
}

static class Extensions
{
    public static DisposableImageData LockBitsDisposable(this Bitmap bitmap, Rectangle rect, ImageLockMode flags, PixelFormat format)
    {
        return new DisposableImageData(bitmap, rect, flags, format);
    }

    public class DisposableImageData : IDisposable
    {
        private readonly Bitmap _bitmap;
        private readonly BitmapData _data;

        internal DisposableImageData(Bitmap bitmap, Rectangle rect, ImageLockMode flags, PixelFormat format)
        {
            bitmap.CheckArgumentNull("bitmap");
            _bitmap = bitmap;
            _data = bitmap.LockBits(rect, flags, format);
        }

        public void Dispose()
        {
            _bitmap.UnlockBits(_data);
        }

        public IntPtr Scan0
        {
            get { return _data.Scan0; }
        }

        public int Stride
        {
            get { return _data.Stride;}
        }

        public int Width
        {
            get { return _data.Width;}
        }

        public int Height
        {
            get { return _data.Height;}
        }

        public PixelFormat PixelFormat
        {
            get { return _data.PixelFormat;}
        }

        public int Reserved
        {
            get { return _data.Reserved;}
        }
    }   
}

Notes:

  • this implementation assumes that both images have the same size, which might not be the case... taking different sizes into account is possible of course, just a little harder.
  • the LockBitsDisposable method is just a convenience, if you prefer you can use the standard LockBits method (but don't forget to unlock the bits when you're done)
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Thanks for your answer, I tested it, and it works, but it has some issues. The first is that the algorithm fails if the image sizes are different. The second is that the diff image is not the same size that the original ones, do you know any "complete" implementation for this? – Daniel Peñalba Feb 21 '12 at 19:19
  • Ok, the result size is wrong due to a little bug in your snippet, when you create the output bitmap, you're creating it of size a.Width, a.Width – Daniel Peñalba Feb 21 '12 at 19:24
  • What about calculating images of different sizes? – Daniel Peñalba Feb 22 '12 at 10:17
  • @DanielPeñalba, as mentioned in my answer, it's not covered by this implementation. Anyway, how would this kind of difference be represented in the diff image? – Thomas Levesque Feb 22 '12 at 13:01
  • Something like this: https://github.com/cameronmcefee/Image-Diff-View-Modes/commit/8e95f70c9c47168305970e91021072673d7cdad8#diff-3 – Daniel Peñalba Feb 22 '12 at 13:28
  • just edit the bounds of therectangle and new image to be the largest intersecting rectangle or largest dimension from both. BitmapOutput = new Bitmap(Math.Max(a.Width, b.Width), Math.Max(a.Height,b.Height), PixelFormat.Format32bppArgb); Rectangle rect = new Rectangle(0, 0, Math.Abs(a.Width - b.Width), Math.Abs(a.Height - b.Height)); – ne2dmar Dec 01 '14 at 14:51
  • @ThomasLevesque hey i know i very late but i used your solution man and i just got an error here ` *outputPtr = (byte)~(*aPtr ^ *bPtr);` of `Attempt to read or write Protected Memory This is often an indicating that other memory is corrupt`.... can you help me when you see this? –  Jun 25 '15 at 15:10
  • @ThomasLevesque they do-i capture 2 screenshot via gdi and want to get the diffrences.. –  Jun 25 '15 at 15:28
  • @itapi, not sure what's going on then... it shouldn't be possible for this code to access memory outside the memory allocated for the image. What is the value of `i` when the exception occurs, and what are the dimensions of the image? – Thomas Levesque Jun 25 '15 at 16:22
  • @itapi, also, what is the value of `Stride`? – Thomas Levesque Jun 25 '15 at 16:24
  • @ThomasLevesque its negtive **-7680** –  Jun 25 '15 at 19:03
  • @itapi, oops, my code doesn't handle that case. But then `len` should be negative too, so it shouldn't even enter the loop, so you shouldn't encounter this error... Anyway, try to use `Math.Abs(aData.Stride)` instead of `aData.Stride`. – Thomas Levesque Jun 25 '15 at 21:33
  • @ThomasLevesque thanks bro but now im facing another problem here ` *outputPtr = *outputPtr = (byte)*bPtr;` it throws an error `Attempted to read or write protected memory. This is often an indication that other memory is corrupt.`... im breaking my head here lol –  Jun 26 '15 at 07:32
2

A quick google search yielded this:

http://www.bryancook.net/2009/10/find-differences-between-images-c.html

If your're going to be using ARGB rather then RGB, it'll probably need a bit of editing. If you wanted to get that 'inverted difference' effect, like in the Github link posted, you could find the differance between RGB colors and use that for each pixel in the difference image, etc.

Community
  • 1
  • 1
K893824
  • 1,279
  • 8
  • 21
  • +1 ive search up and down so, nearly 100, bryncook is the only answork work out of the box with lock bits. – bh_earth0 Feb 02 '19 at 10:34