Page 1 of 1

Image Differences Rate Topic: -----

#1 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 13 March 2014 - 05:45 AM

Find Image Differences

There may be different reasons why we would like to compare two images and find out the differences. I would say that two good uses could be motion detection and to find whether a second copy of an image has been altered compared with the first one.

This is a very simple program where I feed two slightly different images with the same dimensions and cycling through all the pixels, I find the differences between them and show them on a PictureBox using a color that will show a contrast.

When an object enters the view window of a surveillance camera, part of the image won't change because the object won't occupy the full window, or even if it does, there would be a completely different picture compared with a former one. That may trigger another function of a program. Who knows? A border or a silhouette would be handy, but that is another program. Here we are only comparing, Ok?

A camera would get raw images. That would be the best scenario, but a saved-to-file image could have differences due to compression algorithms.

On certain formats images are stored in compressed form. Two images of the same dimensions may compress to different file sizes depending on the byte values contained on it. This compression process modifies the image, so when it is displayed again it could have some bytes that have been changed, so it is not the same as the original. To Our eyes it may look ok, but at the byte level it has been degraded, some of the byte colors are similar on shade, but not exactly the same.

On the image, the picture on the left shows a large amount of differences, but at the moment I created the second image, I only added a red trace to the original and saved to another file.

Attached Image

If you have an image, let's say on *.jpge format, save a copy, then do an small editing and then save to another file. When they are displayed again they will show more differences than the one you just created. Give it a try!

On my program I have a mechanism to compensate a bit for it. I don't just compare all the bytes; I compare the difference between them with a tolerance. This gives me a certain accuracy, because the bytes not edited, usually don't change. You would have a better grasp of it when you start playing with the program.

As I said, this is a very simple program. One function to modify the pixel format and one routine to compare bytes. The rest is just bells and whistles.

Here is a view of the program. It has a menu strip, a TrackBar, a ToolStrip with a button, a label and three PictureBoxes on split panels. The panels have the autoscroll property set to true, the PictureBoxes 1 and 2 have the height fixed and the width calculated to keep proportions of resized images, and the PictureBox3 has the autosize property set to true.

Attached Image

In this view you will see the former position of the clouds on one color, and the next on another color. This happened just by accident. There is no way to know in which directions the changes were made at least you use date creation or date modified. If the image 'A' is different than the image 'B', then the image 'B' is different than the image 'A'. What I am comparing here is changes in color, so one color shows lighter and the other shows darker. I chose to display on inverted colors to show where the differences are, instead of repeating one of the images or only displaying the changes, so they can be located, but you could do it in any way you would prefer.

Attached Image

Here is the program:

Needed Libraries
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Runtime.InteropServices



Class Level variables
Public Class Form1

    Dim ToolTip1 As New ToolTip
    Dim W As Integer = 459 'Size of my initial image
    Dim H As Integer = 700 '  "
    Dim Ratio As Single  ' To keep proportions
.
.
.



Load Form and create Tooltips
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ToolTip1.SetToolTip(TrackBar1, "Compensate For Image Compression Or Decompression")
        ToolTip1.SetToolTip(Label1, "Show Tolerance Value")
    End Sub



We need to load the two images to compare. So we open a file from the menu. It is loaded to PictureBox1. Then we open another file, it will also go to PictureBox1, but the image already on PictureBox1 is transferred to PictureBox2. So image 2 is the one loaded first, but it doesn't matter for our processing. Here you may prefer Drag-And-Drop, it would be easier to load. Look into it.

    Private Sub OpenToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenToolStripMenuItem.Click

        Dim FDialog As New OpenFileDialog
        Try
            With FDialog
                'Select file extension filter
                .Filter = "Image Files|*.bmp;*.gif;*.jpg;*.png;*.tif"

                If .ShowDialog = DialogResult.OK Then
                    'Move a copy of the image from PictureBox1 to PictureBox2
                    PictureBox2.Image = PictureBox1.Image.Clone

                    'Read File Info And Assign Image to PictureBox1
                    PictureBox1.Image = Image.FromFile(.FileName)

                    'Find And Save Image Dimensions
                    W = System.Drawing.Image.FromFile(.FileName).Width
                    H = System.Drawing.Image.FromFile(.FileName).Height

                    ' Keep Proportion On Resizing
                    Ratio = CSng(W / H)

                    'Resize PictureBoxes to New dimensions
                    PictureBox2.Height = 320
                    PictureBox1.Height = 320
                    PictureBox1.Width = CInt(320 * Ratio)
                    PictureBox2.Width = CInt(320 * Ratio)

                    'Form's Title to show last image loaded and it's dimensions
                    Me.Text = "Differ ->" & Path.GetFileName(.FileName) & " " & W & "x" & H
                End If
            End With

        Catch ex As Exception
            MessageBox.Show(ex.ToString)

        Finally
            If Not FDialog Is Nothing Then
                FDialog.Dispose()
                FDialog = Nothing
            End If
        End Try

    End Sub



When we open a file from the menu, the image dimensions are stored on the variables 'W' and 'H', with them we calculate the ratio between height and width, and resize the PictureBoxes 1 and 2 accordingly. Then we modify hte text of the form's title bar. And do a cleanup.

When we press the "Find Differences" button we call two functions:

-ConvertToARGB()
-Diff1()

And feed the two images to the process.

    Private Sub btnOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
        'Display Results On PictureBox3
        PictureBox3.Image = Diff1(ConvertToARGB(PictureBox1.Image), ConvertToARGB(PictureBox2.Image))
        'Show It
        PictureBox3.Refresh()
    End Sub




The two images we want to compare could be on of any of several file types. Some of them won't allow certain processes like generating graphic objects needed to our program, due to palette indexing or other reasons. So we modify them by changing the pixel format with the function ConvertToARGB(); this way our program won't complain.

    Public Shared Function ConvertToARGB(ByVal original As Bitmap) As Bitmap
        Dim newImage As New Bitmap(original.Width, original.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        newImage.SetResolution(original.HorizontalResolution, original.VerticalResolution)
        Dim g As Graphics = Graphics.FromImage(newImage)
        g.DrawImageUnscaled(original, 0, 0)
        g.Dispose()
        Return newImage
    End Function



This function returns an image on a 32 bits per pixel with alpha channel. We apply it to the two images. On it we create a bitmap with the desired format and paint on it a new image.

The function Diff1(), is the one that produces our seeked results. The first thing it does with the images is to find out their dimensions. If they don't match it returns image 2 and exits. You can't compare the pixels if they are not arranged on the same way and they are not the same amount. If the pictures are already different in size, we have our result before comparing bytes.

If images dimensions are the same, then we create two data objects and isolate them on memory using lockbits. The lockbits process uses a rectangular selection of the image. We will use the whole of the images. And compare the bits of image one with the corresponding bits on image 2.

    'Function to cycle through the two images pixels
    Private Function Diff1(ByVal imgx As Bitmap, ByVal imgY As Bitmap) As Image

        'If the two images are of different dimension, tell so and exit function returning something to PictureBox3
        If imgx.Size <> imgY.Size Then
            MessageBox.Show("Images Are Of Different Sizes")
            Return imgY
        End If

        'Use LockBits for Processing

        Dim x As Integer 'Counter For Loop
        Try
            'Get Data For Both Images For Processing For Rectangular Area(Full Image)
            Dim bmpSr1 As BitmapData = imgx.LockBits(New Rectangle(0, 0, W, H), ImageLockMode.ReadWrite, imgx.PixelFormat)
            Dim bmpSr2 As BitmapData = imgY.LockBits(New Rectangle(0, 0, W, H), ImageLockMode.ReadWrite, imgx.PixelFormat)

            'Find Initial Point For Checking
            Dim ptrSr1 As IntPtr = bmpSr1.Scan0
            Dim ptrSr2 As IntPtr = bmpSr2.Scan0

            'Position of color bits and values of intensity and color for pixels On Images 1 and 2
            Dim A, R, G, B, A1, A2 As Integer
            B = 0
            G = 1
            R = 2
            A = 3
            'Lenght of Data
            Dim bytesSr1 As Integer = bmpSr1.Stride * H
            Dim bytesSr2 As Integer = bmpSr2.Stride * H

            'Type Of Data
            Dim rgbvaluesSr1(bytesSr1) As Byte
            Dim rgbvaluesSr2(bytesSr1) As Byte

            'Lock Rectangular Section While Processing
            System.Runtime.InteropServices.Marshal.Copy(ptrSr1, rgbvaluesSr1, 0, bytesSr1)
            System.Runtime.InteropServices.Marshal.Copy(ptrSr2, rgbvaluesSr2, 0, bytesSr2)

            'Loop Thru And Compare
            For x = 0 To bytesSr1 - 4 Step 4

                'What Value For First Image
                A1 = CInt(rgbvaluesSr1(x + R)) + CInt(rgbvaluesSr1(x + G)) + CInt(rgbvaluesSr1(x + B)/>/>)
                'What Value For Second Image
                A2 = CInt(rgbvaluesSr2(x + R)) + CInt(rgbvaluesSr2(x + G)) + CInt(rgbvaluesSr2(x + B)/>/>)

                'Set The Alpha Value
                rgbvaluesSr2(x + A) = 255

                'Compare Difference With A Tolerance 
                If A1 - A2 > TrackBar1.Value Then
                    'If There Is A Difference Mark It On Second Image With A Color That Shows
                    rgbvaluesSr2(x + R) = 0
                    rgbvaluesSr2(x + G) = 185
                    rgbvaluesSr2(x + B)/>/> = 255
                ElseIf A2 - A1 > TrackBar1.Value Then
                    'Or With Another
                    rgbvaluesSr2(x + R) = 255
                    rgbvaluesSr2(x + G) = 0
                    rgbvaluesSr2(x + B)/>/> = 200
                Else
                    'If There Is No Difference Invert Color Of Pixel On Second Image
                    rgbvaluesSr2(x + R) = 255 - rgbvaluesSr2(x + R)
                    rgbvaluesSr2(x + G) = 255 - rgbvaluesSr2(x + G)
                    rgbvaluesSr2(x + B)/>/> = 255 - rgbvaluesSr2(x + B)/>/>
                End If
            Next

            'Loop Ends
            'Copy Locked Data To Byte Array
            System.Runtime.InteropServices.Marshal.Copy(rgbvaluesSr2, 0, ptrSr2, bytesSr2)

            'Unlock Both Data Objects
            imgx.UnlockBits(bmpSr1)
            imgY.UnlockBits(bmpSr1)

            'And Return Image
            Return imgY

        Catch ex As Exception
            'If There Was A Problem, Say So And Return Something To Image3
            MessageBox.Show("There Was An Error")
            Return PictureBox1.Image
        End Try
        MessageBox.Show("Can't use" & PictureBox1.Image.PixelFormat.ToString())
        Return imgx
    End Function




We locate the beginning of the two images and using a counter we move along the data. This counter increases 4 bits on every loop. Every bit represents the value of the 3 color components and the transparency. B-G-R-A (ARGB). So if we are at the green bit, the next green will be 4 bits away.

On a rectangular image we have the width and height on pixels, but to use the lockbits, we need another value that is the "Stride". Stride is the width multiplied by the bytes of the pixel: 4. It is the same as the width measured on bytes instead of pixels.

After, we find the two colors for each corresponding pixel on the 2 images by using A1 and A2:

                A1 = CInt(rgbvaluesSr1(x + R)) + CInt(rgbvaluesSr1(x + G)) + CInt(rgbvaluesSr1(x + B)/>/>)
                A2 = CInt(rgbvaluesSr2(x + R)) + CInt(rgbvaluesSr2(x + G)) + CInt(rgbvaluesSr2(x + B)/>/>)



Substract them and compare the difference with the value of the TrackBar. If we didn't have the alterations introduced by the compression we could just compare one with the other, but this is not a perfect world as you may already have found out. Also I used two comparisons, for brighter or darker, to avoid the use of Math.Abs that slows down the process.

After finding our values I modify image2 with a color for a difference on the brighter direction and another on the darker. The rest is only inverted on color. When the loop has finished, we unlock our memory blocks and return a new image, to PictureBox3.

The tolerance set by the TrackBar value, produces a cleaner image. For a good quality image, it could be small, for a bad one it must be high. For my initial images it is set to 50, and still shows too many errors. For a good quality one, probably 10 will be good enough.

    Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
        'Show TrackBar's Value
        Label1.Text = TrackBar1.Value.ToString
    End Sub



End of program
    Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
        Application.Exit()
    End Sub
End Class



Thank you for reading and, please check the attached project.

Attached File(s)



Is This A Good Question/Topic? 0
  • +

Page 1 of 1