# How to "add" two images so that resulting is the difference of the two

• ### Question

• I have tried using a bit by bit comparison of two bitmaps in order to find different pixels and draw the difference into yet another transparent bitmap, so that the resulting image is the difference of the 2 compared images. However, with large images the processing is very slow. I am sure there is a faster way of achieving the same. Could you please suggest? Thanks.

Dim Bitmap1 As New Bitmap("c:\Bitmap1.bmp")
Dim Bitmap2 As New Bitmap("c:\Bitmap2.bmp")
Dim BitmapDelta As New Bitmap(Bitmap1.Width, Bitmap1.Height)
Dim GraphDelta As Graphics = Graphics.FromImage(BitmapDelta)
GraphDelta.Clear(Color.Transparent)
For x = 0 To Bitmap1.Width - 1
For y = 0 To Bitmap1.Height - 1
If Not Bitmap1.GetPixel(x, y).Equals(Bitmap2.GetPixel(x, y)) Then
GraphDelta.DrawRectangle(New Pen(Bitmap2.GetPixel(x, y), 1), x, y, 1, 1)
End If
Next
Next
BitmapDelta.Save("c:\BitmapDelta.png", Imaging.ImageFormat.Png)

Saturday, August 5, 2017 4:10 PM

### All replies

• Imaxus,

Probably I've to wait on Tommy. But for me it looks impossible.

You have 2 bitmaps and somewhere they are unequal.

But what is unequal. If there is a one pixel shift to the left then what is different, the part at the left or the part at the right side?

Maybe can you show a sample with some commercial software what you try to achieve.

Success
Cor

Saturday, August 5, 2017 5:38 PM
• If they have same dimensions, you can use BitBlt() (PInvoke) with ROP like SRCINVERT

• Edited by Saturday, August 5, 2017 6:06 PM
Saturday, August 5, 2017 6:05 PM
• Hi,

copy all Bitmap-bits with Marshal.Copy to three byte-Arrays, get the difference of the first two and set these in the third array, copy the third array back to the tranparent bitmap and show this.

I'll do an example, if not someone else is faster....

Regards,

Thorsten

Saturday, August 5, 2017 9:11 PM
• Hi,

example (for same sized images, if sizes differ, you need to get the overlap-rectangle first)

```Option Strict On
Imports System.Runtime.InteropServices

Partial Public Class Form1
Inherits Form
Private _rnd As New Random()

Public Sub New()
InitializeComponent()
Init()
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs)
Dim bmp As New Bitmap(Me.pictureBox1.ClientSize.Width, Me.pictureBox1.ClientSize.Height)
Using g As Graphics = Graphics.FromImage(bmp)
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
g.Clear(Color.Green)
For i As Integer = 0 To 3
Using sb As New SolidBrush(Color.FromArgb(_rnd.[Next](256), _rnd.[Next](256), _rnd.[Next](256), _rnd.[Next](256)))
g.FillEllipse(sb, New RectangleF(_rnd.[Next](bmp.Width \ 2), _rnd.[Next](bmp.Width \ 2), _rnd.[Next](bmp.Width \ 2), _rnd.[Next](bmp.Width \ 2)))
End Using
Next
End Using

Dim bmp2 As Bitmap = DirectCast(bmp.Clone(), Bitmap)
Using g As Graphics = Graphics.FromImage(bmp2)
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias

Using sb As New SolidBrush(Color.FromArgb(_rnd.[Next](256), _rnd.[Next](256), _rnd.[Next](256), _rnd.[Next](256)))
g.FillEllipse(sb, New RectangleF(_rnd.[Next](bmp2.Width \ 2), _rnd.[Next](bmp2.Width \ 2), _rnd.[Next](bmp2.Width), _rnd.[Next](bmp2.Width)))
End Using
End Using

Me.pictureBox1.Image = bmp
Me.pictureBox2.Image = bmp2

End Sub

Private Sub button1_Click(sender As Object, e As EventArgs)
Me.pictureBox3.Image = CompareBitmaps(DirectCast(Me.pictureBox1.Image, Bitmap), DirectCast(Me.pictureBox2.Image, Bitmap))
Me.pictureBox3.Refresh()
End Sub

Private Function CompareBitmaps(image1 As Bitmap, image2 As Bitmap) As Image
If image1.Width <> image2.Width OrElse image1.Height <> image2.Height Then
Throw New Exception("Bitmaps must be of same size.")
End If

Dim bmData As System.Drawing.Imaging.BitmapData = Nothing
Dim bmData2 As System.Drawing.Imaging.BitmapData = Nothing

Dim bmpOUt As New Bitmap(image1.Width, image1.Height)
Dim bmDataOut As System.Drawing.Imaging.BitmapData = Nothing

Try
bmData = image1.LockBits(New Rectangle(0, 0, image1.Width, image1.Height), System.Drawing.Imaging.ImageLockMode.[ReadOnly], System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
bmData2 = image2.LockBits(New Rectangle(0, 0, image2.Width, image2.Height), System.Drawing.Imaging.ImageLockMode.[ReadOnly], System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
bmDataOut = bmpOUt.LockBits(New Rectangle(0, 0, bmpOUt.Width, bmpOUt.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)

Dim stride As Integer = bmData.Stride
Dim nW As Integer = image1.Width
Dim nH As Integer = image1.Height

Dim p(stride * nH - 1) As Byte
Dim p2(stride * nH - 1) As Byte
Dim pOut(stride * nH - 1) As Byte

Marshal.Copy(bmData.Scan0, p, 0, p.Length)
Marshal.Copy(bmData2.Scan0, p2, 0, p2.Length)
Marshal.Copy(bmDataOut.Scan0, pOut, 0, pOut.Length)

Parallel.[For](0, nH, Sub(y)
Dim pos As Integer = y * stride
Dim pos2 As Integer = y * stride
Dim posOut As Integer = y * stride

For x As Integer = 0 To nW - 1
If p(pos) <> p2(pos2) OrElse p(pos + 1) <> p2(pos2 + 1) OrElse p(pos + 2) <> p2(pos2 + 2) OrElse p(pos + 3) <> p2(pos2 + 3) Then
For i As Integer = 0 To 2
'pOut(posOut + i) = CByte(Math.Abs(CInt(p(pos + i)) - CInt(p2(pos2 + i)))) 'real difference
pOut(posOut + i) = p2(pos2 + i)
Next

'set opaque
pOut(posOut + 3) = CByte(255)
End If

pos += 4
pos2 += 4
posOut += 4
Next

End Sub)

Marshal.Copy(pOut, 0, bmDataOut.Scan0, pOut.Length)

image1.UnlockBits(bmData)
image2.UnlockBits(bmData2)
bmpOUt.UnlockBits(bmDataOut)

p = Nothing
p2 = Nothing
pOut = Nothing
Catch
Try
image1.UnlockBits(bmData)

Catch
End Try
Try
image2.UnlockBits(bmData2)

Catch
End Try
Try
bmpOUt.UnlockBits(bmDataOut)

Catch
End Try
End Try

Return bmpOUt
End Function

'usually done with the Designer
Private button1 As System.Windows.Forms.Button
Private pictureBox1 As System.Windows.Forms.PictureBox
Private pictureBox2 As System.Windows.Forms.PictureBox
Private pictureBox3 As System.Windows.Forms.PictureBox

Private Sub Init()
Me.button1 = New System.Windows.Forms.Button()
Me.pictureBox1 = New System.Windows.Forms.PictureBox()
Me.pictureBox2 = New System.Windows.Forms.PictureBox()
Me.pictureBox3 = New System.Windows.Forms.PictureBox()
DirectCast(Me.pictureBox1, System.ComponentModel.ISupportInitialize).BeginInit()
DirectCast(Me.pictureBox2, System.ComponentModel.ISupportInitialize).BeginInit()
DirectCast(Me.pictureBox3, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
'
' button1
'
Me.button1.Location = New System.Drawing.Point(597, 290)
Me.button1.Name = "button1"
Me.button1.Size = New System.Drawing.Size(75, 23)
Me.button1.TabIndex = 0
Me.button1.Text = "button1"
Me.button1.UseVisualStyleBackColor = True
'
' pictureBox1
'
Me.pictureBox1.Location = New System.Drawing.Point(13, 13)
Me.pictureBox1.Name = "pictureBox1"
Me.pictureBox1.Size = New System.Drawing.Size(276, 260)
Me.pictureBox1.TabIndex = 1
Me.pictureBox1.TabStop = False
'
' pictureBox2
'
Me.pictureBox2.Location = New System.Drawing.Point(309, 13)
Me.pictureBox2.Name = "pictureBox2"
Me.pictureBox2.Size = New System.Drawing.Size(276, 260)
Me.pictureBox2.TabIndex = 1
Me.pictureBox2.TabStop = False
'
' pictureBox3
'
Me.pictureBox3.Location = New System.Drawing.Point(597, 13)
Me.pictureBox3.Name = "pictureBox3"
Me.pictureBox3.Size = New System.Drawing.Size(276, 260)
Me.pictureBox3.TabIndex = 1
Me.pictureBox3.TabStop = False
'
' Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0F, 13.0F)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(890, 322)
Me.Name = "Form1"
Me.Text = "Form1"
DirectCast(Me.pictureBox1, System.ComponentModel.ISupportInitialize).EndInit()
DirectCast(Me.pictureBox2, System.ComponentModel.ISupportInitialize).EndInit()
DirectCast(Me.pictureBox3, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False)
End Sub

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs)
If Me.pictureBox1.Image IsNot Nothing Then
Me.pictureBox1.Image.Dispose()
End If
If Me.pictureBox2.Image IsNot Nothing Then
Me.pictureBox2.Image.Dispose()
End If
If Me.pictureBox3.Image IsNot Nothing Then
Me.pictureBox3.Image.Dispose()
End If
End Sub
End Class```

Edit: Added a (commented-out) line that gets the real "difference" (like the difference operator in image editing applications). Remove the comment and comment out the next line, if you like to get it that way.

Regards,

Thorsten

Saturday, August 5, 2017 9:26 PM
• If both are 24bit bitmap, you can change one of them into a 32bitmap (I think that happens anyway when you instantiate into memory). Then alter the alpha channel to 127, then use GDI to overlay that onto the first bitmap.

Pride is the most destructive force in the universe

Saturday, August 5, 2017 11:23 PM
•  I am guessing that you want to create a new image that only shows the pixels of the 2nd image which have changed from the first (original) image.  If so,  here is another example which uses the Bitmap.LockBits Method as Thorsten has shown too.  Using the LockBits method is quite a bit faster for processing/manipulating larger images.

This example can be tested in a new form project with 1 Button and 3 PictureBoxes added to it.  I set the SizeMode of all 3 PictureBoxes to (Zoom) in the designer properties.  You will also need to set the paths to the images to ones on your computer.

```Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

Public Class Form1
Private sw As New Stopwatch

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim BmImg1 As New Bitmap("C:\TestFolder\RollingInCatnip.bmp") 'Snoopy_Original.png | QuarryShowing.jpg
Dim BmImg2 As New Bitmap("C:\TestFolder\RollingInCatnip_Changed.bmp") 'Snoopy_Changed.png | QuarryShowing3q99.jpg
PictureBox1.Image = BmImg1
PictureBox2.Image = BmImg2

sw.Restart()
PictureBox3.Image = FindDifference(BmImg1, BmImg2)
sw.Stop()
Me.Text = sw.ElapsedMilliseconds.ToString & " ms  |  Image Size: " & BmImg1.Size.ToString
End Sub

Private Function FindDifference(bm1 As Bitmap, bm2 As Bitmap) As Bitmap
Dim bm As New Bitmap(bm1.Width, bm1.Height)
Dim bts1() As Byte = GetImageArgbBytes(bm1)
Dim bts2() As Byte = GetImageArgbBytes(bm2)
If bts1.Length = bts2.Length Then
For i As Integer = 0 To bts1.Length - 5 Step 4
If bts1(i + 3) = bts2(i + 3) AndAlso bts1(i + 2) = bts2(i + 2) AndAlso bts1(i + 1) = bts2(i + 1) AndAlso bts1(i) = bts2(i) Then
bts2(i + 3) = 0
End If
Next
Dim bmd As BitmapData = bm.LockBits(New Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb)
Marshal.Copy(bts2, 0, bmd.Scan0, bts2.Length)
bm.UnlockBits(bmd)
Else
MessageBox.Show("Images must be the same size.", "Error...")
End If
Return bm
End Function

Private Function GetImageArgbBytes(b As Bitmap) As Byte()
Dim bmd As BitmapData = b.LockBits(New Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
Dim bts((bmd.Stride * bmd.Height) - 1) As Byte
Marshal.Copy(bmd.Scan0, bts, 0, bts.Length)
b.UnlockBits(bmd)
Return bts
End Function
End Class```

Here you can see two examples,  the first using a 32bpp Png image that is 1024x768.  It took an average of 51ms for this one.  The second was with a 24bpp Bmp image that is 2064x1161 and took an average of 144ms.  However,  the speed will very depending on the ratio of same/different pixel colors.

You should also be aware that Jpg type images use an encoder which will loose image quality.  This can cause the same jpg image saved two different times to have some slightly different colored pixels.  This might give you results that you are not expecting,  for example....

If you say it can`t be done then i`ll try it

• Edited by Sunday, August 6, 2017 1:34 AM
• Proposed as answer by Thursday, August 10, 2017 3:44 PM
Sunday, August 6, 2017 1:11 AM
• If both are 24bit bitmap, you can change one of them into a 32bitmap (I think that happens anyway when you instantiate into memory). Then alter the alpha channel to 127, then use GDI to overlay that onto the first bitmap.

Pride is the most destructive force in the universe

Sorry Imaxus - - - ignore my post - - it was way off.

Pride is the most destructive force in the universe

Wednesday, August 9, 2017 9:18 AM
• I managed to achieve this using my company’s product (LEADTOOLS imaging SDK) based on the following forum post:

I modified the code (and converted it to VB.NET), and it looks like this:

```Dim codecs As RasterCodecs = New RasterCodecs()
Dim bitmap1 As RasterImage = codecs.Load(imagesPath + "Image1.png")
Dim bitmap2 As RasterImage = codecs.Load(imagesPath + "Image2.png")
Dim temp As RasterImage = bitmap2.Clone()
' pefrom XOR combining to convert similar pixels to black in image2. All non-similar pixels become non-black
Dim combineCmd As CombineFastCommand = New CombineFastCommand(temp, combineRect, New LeadPoint(0, 0), CombineFastCommandFlags.OperationXor)
combineCmd.Run(bitmap1)
' create a region in image2 with all the black pixels. Those pixels were the similar ones.
' fill the region with White
Dim fillWhite As FillCommand = New FillCommand(RasterColor.FromKnownColor(RasterKnownColor.White))
fillWhite.Run(temp)
' perfom copy combining to copy the white pixels to the first image
combineCmd.DestinationImage = bitmap2
combineCmd.Flags = CombineFastCommandFlags.SourceCopy
combineCmd.Run(temp)
' Save the result
codecs.Save(bitmap2, imagesPath + "Result.png", RasterImageFormat.Png, 24)
```
If you’d like to try it, the SDK has a free evaluation edition.

Thursday, August 10, 2017 4:30 PM
• Hi imaxus22，

It seems that IronRazerz's reply resolve your issue, if so, please mark it as answer, it will be beneficial to other communities who have the same issue.