Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing images and labeling the differences c#

I am currently working on a project in which I am required to write software that compares two images made up of the same area and draws a box around the differences. I wrote the program in c# .net in a few hours but soon realized it was INCREDIBLY expensive to run. Here are the steps I implemented it in.

  1. Created a Pixel class that stores the x,y coordinates of each pixel and a PixelRectangle class that stores a list of pixels along with width,height,x and y properties.

  2. Looped through every pixel of each image, comparing the colour of each corresponding pixels. If the colour was different I then created a new pixel object with the x,y coordinates of that pixel and added it to a pixelDifference List.

  3. Next I wrote a method that recursively checks each pixel in the pixelDifference list to create PixelRectangle objects that only contain pixels that are directly next to each other. (Pretty sure this bad boy is causing the majority of the destruction as it gave me a stack overflow error.)

  4. I then worked out the x,y coordinates and dimensions of the rectangle based on the pixels that were stored in the list of the PixelRectangle Object and drew a rectangle over the original image to show where the differences were.

My questions are: Am I going about this the correct way? Would a quad tree hold any value for this project? If you could give me the basic steps on how something like this is normally achieved I would be grateful. Thanks in advance.

  • Dave.
like image 515
David Sleep Avatar asked Jul 11 '13 21:07

David Sleep


1 Answers

looks like you want to implement blob detection. my suggestion is not to reinvent the wheel and just use openCVSharp or emgu to do this. google 'blob detection' & opencv

if you want to do it yourself here my 2 cents worth:

first of all, let's clarify what you want to do. really two separate things:

  1. compute the difference between two images (i am assuming they are the same dimensions)

  2. draw a box around 'areas' that are 'different' as measured by 1. questions here are what is an 'area' and what is considered 'different'.

my suggestion for each step:

(my assumption is both images a grey scale. if not, compute the sum of colours for each pixel to get grey value)

1) cycle through all pixels in both images and subtract them. set a threshold on the absolute difference to determine if their difference is sufficient to represent and actual change in the scene (as opposed to sensor noise etc if the images are from a camera). then store the result in a third image. 0 for no difference. 255 for a difference. if done right this should be REALLY fast. however, in C# you must use pointers to get a decent performance. here an example of how to do this (note: code not tested!!) :

  /// <summary>
    /// computes difference between two images and stores result in a third image
    /// input images must be of same dimension and colour depth
    /// </summary>
    /// <param name="imageA">first image</param>
    /// <param name="imageB">second image</param>
    /// <param name="imageDiff">output 0 if same, 255 if different</param>
    /// <param name="width">width of images</param>
    /// <param name="height">height of images</param>
    /// <param name="channels">number of colour channels for the input images</param>
    unsafe void ComputeDiffernece(byte[] imageA, byte[] imageB, byte[] imageDiff, int width, int height, int channels, int threshold)
    {
        int ch = channels;

        fixed (byte* piA = imageB, piB = imageB, piD = imageDiff)
        {

            if (ch > 1) // this a colour image (assuming for RGB ch == 3 and RGBA  == 4)
            {
                for (int r = 0; r < height; r++)
                {
                    byte* pA = piA + r * width * ch;
                    byte* pB = piB + r * width * ch;
                    byte* pD = piD + r * width; //this has only one channels!

                    for (int c = 0; c < width; c++)
                    {
                        //assuming three colour channels. if channels is larger ignore extra (as it's likely alpha)
                        int LA = pA[c * ch] + pA[c * ch + 1] + pA[c * ch + 2];
                        int LB = pB[c * ch] + pB[c * ch + 1] + pB[c * ch + 2];

                        if (Math.Abs(LA - LB) > threshold)
                        {
                            pD[c] = 255;
                        }
                        else
                        {
                            pD[c] = 0;
                        }

                    }
                }
            }
            else //single grey scale channels
            {
                for (int r = 0; r < height; r++)
                {
                    byte* pA = piA + r * width;
                    byte* pB = piB + r * width;
                    byte* pD = piD + r * width; //this has only one channels!

                    for (int c = 0; c < width; c++)
                    {
                        if (Math.Abs(pA[c] - pB[c]) > threshold)
                        {
                            pD[c] = 255;
                        }
                        else
                        {
                            pD[c] = 0;
                        }
                    }
                }
            }
        }
    }

2)

not sure what you mean by area here. several solutions depending on what you mean. from simplest to hardest.

a) colour each difference pixel red in your output

b) assuming you only have one area of difference (unlikely) compute the bounding box of all 255 pixels in your output image. this can be done using a simple max / min for both x and y positions on all 255 pixels. single pass through the image and should be very fast.

c) if you have lots of different areas that change - compute the "connected components". that is a collection of pixels that are connected to each other. of course this only works in a binary image (i.e. on or off, or 0 and 255 as in our case). you can implement this in c# and i have done this before. but i won't do this for you here. it's a bit involved. algorithms are out there. again opencv or google connected components.

once you have a list of CC's draw a box around each. done.

like image 144
morishuz Avatar answered Sep 24 '22 09:09

morishuz