[Webinar] Streamline your web hosting managementRegister Today

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 561
  • Last Modified:

Determine changed pixels in a Bitmap

I hope this isn't too hard a question.

I have a Bitmap object in VB.NET which I drew to using Graphics.DrawImage() (it was a screenshot of the screen which I scaled down. I don't have to scale it down before I process it for changes... whichever way works faster)

Later on, I do the exact same process, and I have a second bitmap object.

Now I need a way to (quickly) determine the basic areas that have changed in the image. Ideally I'd like to know the exact pixels that have changed, but if I, say, could just split the bitmap into some number of conceptual "pieces" and determine if each piece has changed, that's perfect as well.

I can use GetPixel on each and compare the values all the way through the bitmap object... but I imagine it would be painfully slow. I need to do it quickly. Under 50ms preferably for a 1600x1200 size screen o.O I know that's a tall order. I considered maybe comparing every 100th pixel, or comparing down just a few columns and rows, but I don't think it would accurately tell me what areas have changed.

Is there any other way to compare two bitmap objects in VB.NET?
0
Frosty555
Asked:
Frosty555
  • 3
  • 2
1 Solution
 
iboutchkineCommented:
Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents Label1 As System.Windows.Forms.Label
    Friend WithEvents btnFile1 As System.Windows.Forms.Button
    Friend WithEvents btnGo As System.Windows.Forms.Button
    Friend WithEvents Panel1 As System.Windows.Forms.Panel
    Friend WithEvents btnFile2 As System.Windows.Forms.Button
    Friend WithEvents Label2 As System.Windows.Forms.Label
    Friend WithEvents dlgSelectFile As System.Windows.Forms.OpenFileDialog
    Friend WithEvents txtFile1 As System.Windows.Forms.TextBox
    Friend WithEvents txtFile2 As System.Windows.Forms.TextBox
    Friend WithEvents picResult As System.Windows.Forms.PictureBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form1))
        Me.txtFile1 = New System.Windows.Forms.TextBox
        Me.Label1 = New System.Windows.Forms.Label
        Me.btnFile1 = New System.Windows.Forms.Button
        Me.btnGo = New System.Windows.Forms.Button
        Me.Panel1 = New System.Windows.Forms.Panel
        Me.picResult = New System.Windows.Forms.PictureBox
        Me.btnFile2 = New System.Windows.Forms.Button
        Me.Label2 = New System.Windows.Forms.Label
        Me.txtFile2 = New System.Windows.Forms.TextBox
        Me.dlgSelectFile = New System.Windows.Forms.OpenFileDialog
        Me.Panel1.SuspendLayout()
        Me.SuspendLayout()
        '
        'txtFile1
        '
        Me.txtFile1.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.txtFile1.Location = New System.Drawing.Point(32, 0)
        Me.txtFile1.Name = "txtFile1"
        Me.txtFile1.Size = New System.Drawing.Size(440, 20)
        Me.txtFile1.TabIndex = 0
        Me.txtFile1.Text = ""
        '
        'Label1
        '
        Me.Label1.AutoSize = True
        Me.Label1.Location = New System.Drawing.Point(0, 0)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(32, 16)
        Me.Label1.TabIndex = 1
        Me.Label1.Text = "File 1"
        '
        'btnFile1
        '
        Me.btnFile1.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.btnFile1.BackColor = System.Drawing.Color.Aqua
        Me.btnFile1.Image = CType(resources.GetObject("btnFile1.Image"), System.Drawing.Image)
        Me.btnFile1.Location = New System.Drawing.Point(480, 0)
        Me.btnFile1.Name = "btnFile1"
        Me.btnFile1.Size = New System.Drawing.Size(24, 20)
        Me.btnFile1.TabIndex = 2
        '
        'btnGo
        '
        Me.btnGo.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.btnGo.Location = New System.Drawing.Point(228, 48)
        Me.btnGo.Name = "btnGo"
        Me.btnGo.Size = New System.Drawing.Size(48, 23)
        Me.btnGo.TabIndex = 3
        Me.btnGo.Text = "Go"
        '
        'Panel1
        '
        Me.Panel1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                    Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.Panel1.AutoScroll = True
        Me.Panel1.Controls.Add(Me.picResult)
        Me.Panel1.Location = New System.Drawing.Point(0, 80)
        Me.Panel1.Name = "Panel1"
        Me.Panel1.Size = New System.Drawing.Size(504, 292)
        Me.Panel1.TabIndex = 4
        '
        'picResult
        '
        Me.picResult.Location = New System.Drawing.Point(0, 0)
        Me.picResult.Name = "picResult"
        Me.picResult.Size = New System.Drawing.Size(56, 50)
        Me.picResult.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize
        Me.picResult.TabIndex = 0
        Me.picResult.TabStop = False
        '
        'btnFile2
        '
        Me.btnFile2.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.btnFile2.BackColor = System.Drawing.Color.Aqua
        Me.btnFile2.Image = CType(resources.GetObject("btnFile2.Image"), System.Drawing.Image)
        Me.btnFile2.Location = New System.Drawing.Point(480, 24)
        Me.btnFile2.Name = "btnFile2"
        Me.btnFile2.Size = New System.Drawing.Size(24, 20)
        Me.btnFile2.TabIndex = 7
        '
        'Label2
        '
        Me.Label2.AutoSize = True
        Me.Label2.Location = New System.Drawing.Point(0, 24)
        Me.Label2.Name = "Label2"
        Me.Label2.Size = New System.Drawing.Size(32, 16)
        Me.Label2.TabIndex = 6
        Me.Label2.Text = "File 2"
        '
        'txtFile2
        '
        Me.txtFile2.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.txtFile2.Location = New System.Drawing.Point(32, 24)
        Me.txtFile2.Name = "txtFile2"
        Me.txtFile2.Size = New System.Drawing.Size(440, 20)
        Me.txtFile2.TabIndex = 5
        Me.txtFile2.Text = ""
        '
        'dlgSelectFile
        '
        Me.dlgSelectFile.Filter = "Graphics Files|*.bmp;*.gif;*.jpg;*.jpeg;*.ico;*.png;*.tif;*.tiff|All Files|*.*"
        Me.dlgSelectFile.Title = "Select File"
        '
        'Form1
        '
        Me.AcceptButton = Me.btnGo
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(504, 373)
        Me.Controls.Add(Me.btnFile2)
        Me.Controls.Add(Me.Label2)
        Me.Controls.Add(Me.txtFile2)
        Me.Controls.Add(Me.Panel1)
        Me.Controls.Add(Me.btnGo)
        Me.Controls.Add(Me.btnFile1)
        Me.Controls.Add(Me.Label1)
        Me.Controls.Add(Me.txtFile1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.Panel1.ResumeLayout(False)
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private Sub btnFile1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFile1.Click
        If dlgSelectFile.ShowDialog(Me) = DialogResult.OK Then
            txtFile1.Text = dlgSelectFile.FileName
        End If
    End Sub

    Private Sub btnFile2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFile2.Click
        If dlgSelectFile.ShowDialog(Me) = DialogResult.OK Then
            txtFile2.Text = dlgSelectFile.FileName
        End If
    End Sub

    Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click
        Me.Cursor = Cursors.WaitCursor
        Application.DoEvents()

        ' Load the images.
        Dim bm1 As Bitmap = Image.FromFile(txtFile1.Text)
        Dim bm2 As Bitmap = Image.FromFile(txtFile2.Text)

        ' Make a difference image.
        Dim wid As Integer = Math.Min(bm1.Width, bm2.Width)
        Dim hgt As Integer = Math.Min(bm1.Height, bm2.Height)
        Dim bm3 As New Bitmap(wid, hgt)

        ' Create the difference image.
        Dim are_identical As Boolean = True
        Dim r1, g1, b1, r2, g2, b2, r3, g3, b3 As Integer
        Dim color1, color2, color3 As Color
        For x As Integer = 0 To wid - 1
            For y As Integer = 0 To hgt - 1
                color1 = bm1.GetPixel(x, y)
                r1 = color1.R : g1 = color1.G : b1 = color1.B

                color2 = bm2.GetPixel(x, y)
                r2 = color2.R : g2 = color2.G : b2 = color2.B

                r3 = 128 + (r1 - r2) \ 2
                g3 = 128 + (g1 - g2) \ 2
                b3 = 128 + (b1 - b2) \ 2

                color3 = Color.FromArgb(255, r3, g3, b3)
                bm3.SetPixel(x, y, color3)

                If (r1 <> r2) OrElse (g1 <> g2) OrElse (b1 <> b2) Then are_identical = False
            Next y
        Next x

        ' Display the result.
        picResult.Image = bm3

        Me.Cursor = Cursors.Default
        If (bm1.Width <> bm2.Width) OrElse (bm1.Height <> bm2.Height) Then are_identical = False
        If are_identical Then
            MessageBox.Show("The images are identical")
        Else
            MessageBox.Show("The images are different")
        End If

        bm1.Dispose()
        bm2.Dispose()
    End Sub
End Class
0
 
PaulHewsCommented:
https://filedb.experts-exchange.com/incoming/ee-stuff/6528-XORBitmap.zip

Two methods to output changed pixels to third picturebox:
Public Class Form1
 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim bmp1 As New Bitmap(100, 50)
        Dim bmp2 As New Bitmap(100, 50)
        Dim g As Graphics = Graphics.FromImage(bmp1)
        g.FillRectangle(Brushes.DeepSkyBlue, New RectangleF(0, 0, 100, 50))
        g.Dispose()
        g = Graphics.FromImage(bmp2)
        g.FillRectangle(Brushes.DeepSkyBlue, New RectangleF(0, 0, 100, 50))
        g.DrawString("Notbono wuz here", New Font("Arial", 8), Brushes.White, 2, 4)
        g.Dispose()
 
        PictureBox1.Image = bmp1
        PictureBox2.Image = bmp2
 
    End Sub
 
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'slow way, but fully .net
        Dim bmp1 As Bitmap = CType(PictureBox1.Image, Bitmap)
        Dim bmp2 As Bitmap = CType(PictureBox2.Image, Bitmap)
 
        Dim bmp3 As Bitmap = New Bitmap(100, 50)
 
 
        Dim bmpData1 As Imaging.BitmapData = bmp1.LockBits(New Rectangle(0, 0, bmp1.Width, bmp1.Height), Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format24bppRgb)
        Dim bmpData2 As Imaging.BitmapData = bmp2.LockBits(New Rectangle(0, 0, bmp2.Width, bmp2.Height), Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format24bppRgb)
        Dim bmpData3 As Imaging.BitmapData = bmp3.LockBits(New Rectangle(0, 0, bmp3.Width, bmp1.Height), Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format24bppRgb)
        Dim bytes As Integer = bmpData1.Stride * bmp1.Height
 
        Dim buffer1(bytes - 1), buffer2(bytes - 1), buffer3(bytes - 1) As Byte
 
        System.Runtime.InteropServices.Marshal.Copy(bmpData1.Scan0, buffer1, 0, bytes)
        System.Runtime.InteropServices.Marshal.Copy(bmpData2.Scan0, buffer2, 0, bytes)
 
        For i As Integer = 0 To buffer1.GetUpperBound(0)
            buffer3(i) = buffer1(i) Xor buffer2(i)
        Next
 
        System.Runtime.InteropServices.Marshal.Copy(buffer3, 0, bmpData3.Scan0, bytes)
 
        bmp1.UnlockBits(bmpData1)
        bmp2.UnlockBits(bmpData2)
        bmp3.UnlockBits(bmpData3)
 
        PictureBox3.Image = bmp3
 
 
    End Sub
 
 
    'Faster (?) API way
    Private Declare Auto Function BitBlt Lib "gdi32.dll" (ByVal _
        hdcDest As IntPtr, ByVal nXDest As Integer, ByVal _
        nYDest As Integer, ByVal nWidth As Integer, ByVal _
        nHeight As Integer, ByVal hdcSrc As IntPtr, ByVal nXSrc _
        As Integer, ByVal nYSrc As Integer, ByVal dwRop As _
        RasterOp) As Boolean
    Public Enum RasterOp
        SRCCOPY = &HCC0020
        PATINVERT = &H5A0049
        SRCINVERT = &H660046
    End Enum
 
 
 
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        CopyImage(PictureBox1, PictureBox3, RasterOp.SRCCOPY)
        CopyImage(PictureBox2, PictureBox3, RasterOp.SRCINVERT)
      
 
 
    End Sub
 
    Sub CopyImage(ByVal src As PictureBox, ByVal des As PictureBox, ByVal RasterOP As RasterOp)
 
        ' Get device context of source and destination picture boxes
        Dim srcHDC As IntPtr = src.CreateGraphics.GetHdc
        Dim desHDC As IntPtr = des.CreateGraphics.GetHdc
 
        ' Use api function
        BitBlt(desHDC, 0, 0, src.Width, _
        src.Height, srcHDC, 0, 0, RasterOP)
 
    End Sub
 
End Class

Open in new window

0
 
Frosty555Author Commented:
iboutchkine: Your solution is like I described in the question, comparing every pixel using the GetPixel() function. It will  be too slow to do anything productive with it.

PaulHews: I see where you're going, you're X-ORing the bits, so that a change in the bits will result in a nonzero value in the output bit (so, pixels that have changed will be not-black).

The next step is actually analyzing the final image to figure out what parts are non-black... can you think of any way to do this without resorting to GetPixel()?

What I need in the end is the coordinates of some kind of "dirty rectange" of the image. It doesn't have to be perfect. It just needs to enclose all the dirty pixels. If we could find some fast way to crop the image to the rectangle encasing all the changed pixels, that'd be perfect...
 Maybe some kind of .net-based cropping that crops all the black off the outside border of the image?
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
PaulHewsCommented:
You might find the filters used here of interest:
Motion Detection Algorithms
http://www.codeproject.com/KB/audio-video/Motion_Detection.aspx
0
 
Frosty555Author Commented:
Those do look pretty interesting... but all of it is in C#. Is there a way to still use it in VB?

I'm actually finding that my original program is running atrociously slow right now. I have no room for additional overhead. I might have to put this on hold while I figure out what's taking all the time elsewhere.
0
 
Frosty555Author Commented:
The solution works, and you can get the changed pixels this way. It's just way to slow to be used in an environment where changed pixels need to be determined several times a second. It would take longer to determine the changes pixels then it would take to just send everything down the network.
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now