Solved

1500 POINTS!  How would you use Windows APIs to compare 2 images and get the rectangular regions where they differ?

Posted on 2003-11-02
13
345 Views
Last Modified: 2010-05-19
I want to use Windows APIs to compare 2 images and get the rectangular regions where they are different.  

This will be an application that captures images from a camera (high def cameras - not the cheesy web cams that constantly vary light intensity so *every* capture is different) and compares it with the image taken before.  If the images differ, I would like to copy the rectangular regions of the images that differ and only send the differences so as to conserve bandwidth.

I'm not sure how to make the rectangular regions as small as possible, but this is also needed.  Perhaps only contiguous pixels that have changed should be mapped as a changed rectangular region.  (?)

It would be most helpful to see a demo in VB of the effect I'm after.

1500 points to the first person to demo this!  I'll send you a couple more 500 point links upon successful completion of this demo.

Jim
0
Comment
Question by:JimHubbard
  • 6
  • 4
13 Comments
 
LVL 5

Expert Comment

by:fantasy1001
ID: 9668380
This example show the comparison, analysis of images,

http://www.catenary.com/howto/comparator.html

Regards,
~ fantasy ~
0
 

Author Comment

by:JimHubbard
ID: 9668615
True, however, it is not using the windows API, it is using a control.  And, the difference produced does not show the different pixels in their changed color (i.e. current color) nor are the changes' rectangular regions accessible via the control.

I need to compare 2 images, get the changed pixels in image 2 as rectangular regions for transmission to a client program.

Jim
0
 

Author Comment

by:JimHubbard
ID: 9669592
I have all but a way to quickly get each rectangular region where changed pixels exist in a mask.

I have the code that will use APIs to create a mask of black (0) pixels everywhere the two images are the same, and leave pixels everywhere they are different.

Now I need to find the fastest method of identifying rectangular areas where the pixels differ (are <> 0).

SPEED is the key here - hence the API request.  Although, if you know of a DLL that would do the trick, I'm open to suggestions.

Jim

0
 

Author Comment

by:JimHubbard
ID: 9669726
Sorry about that.  

I'm new here.  I had seen it done elsewhere (even by a moderator I think) and I thought it was ok.

I'll keep the limits in mind for my future posts.

Jim
0
 

Author Comment

by:JimHubbard
ID: 9669776
I'll try and find it....but I've seen at least a thousand posts tonight.....I can't guarantee anything.

Jim
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 17

Expert Comment

by:inthedark
ID: 9670030
There is an easy way to do this.

How many images will you need to process per minute?  VB will only be good for about perhaps 2 seconds per image. But this can only be done at this speed using hardcore vb.  You don't need to use Windpws API as you can map your picture directly to an arry, then compare 2 arrays. Using slower, but simple method will take perhaps 30 seconds.  I suggest getting a working version using simple method, then converting to use fast method once proven....


1) Load images to be compared
2) Compare the images
3) Extract the rectangular regions where difference have been found.
4) Save the images of the schanged rectangles

I will post the code for a faster versions shortly...

Here is some basic code...you need 3 picture boxes, picFirst, picSecond & picSave
Set the scalemode of the form to pixels and also for all picture boxes.

Private Sub Command1_Click()

' load images
picFirst.AutoSize = True
picSecond.AutoSize = True
picFirst.Appearance = 0
picSecond.Appearance = 0
picFirst.BorderStyle = 0
picSecond.BorderStyle = 0

picFirst.ScaleMode = 3
picSecond.ScaleMode = 3
picSave.ScaleMode = 3

picFirst.Picture = LoadPicture("D:\CustData\www.racyc.org.uk\website\04a.jpg")
picSecond.Picture = LoadPicture("D:\CustData\www.racyc.org.uk\website\04x.jpg")

DoEvents
DoEvents
DoEvents
DoEvents

' compare

Dim w As Long
Dim h As Long
w = picFirst.ScaleWidth
h = picFirst.ScaleHeight

ReDim bDiffs(w, h)
ReDim bDone(w, h)


Dim x As Long
Dim x2 As Long
Dim y As Long
Dim y2 As Long
Dim xc As Long
Dim yc As Long
Dim Regions As New Collection
Dim MinSize As Long

MinSize = 20 ' se the minimum size of a chunk thats different

' compare the images


For y = 0 To h
    For x = 0 To w
        Dim p1 As Long
        Dim p2 As Long
        p1 = GetPixel(picFirst.hdc, x, y) ' this is the slow bit
        p2 = GetPixel(picSecond.hdc, x, y)
        bDiffs(x, y) = p1 <> p2
               
    Next x
Next y

' extract the regions
y = 0
x = 0
Do
    If Not bDone(x, y) And bDiffs(x, y) Then
        ' find the extent of the change along x axis
        x2 = x
        Do
            x2 = x2 + 1
            If x2 > w Then
                x2 = w
                Exit Do
            End If
            If bDone(x2, y) Then
                x2 = x2 - 1
                Exit Do
            End If
            If x2 - x > MinSize Then ' ensure that the changed section is a minumum size
                If Not bDiffs(x2, y) Then
                    x2 = x2 - 1
                    Exit Do
                End If
            End If
        Loop
        ' find the extent of the change along y axis
        y2 = y
        Do
            y2 = y2 + 1
            If y2 > h Then
                y2 = h
                Exit Do
            End If
            If bDone(x, y2) Then
                y2 = y2 - 1
                Exit Do
            End If
           
            If y2 - y > MinSize Then ' ensure minumum size
                Dim bChanged As Boolean
                bChanged = False
                For xc = x To x2
                    ' take the region if any pixel change along x axis has changed
                    bChanged = bChanged Or bDiffs(xc, y2)
                Next
                If Not bChanged Then
                    y2 = y2 - 1
                    Exit Do
                Else
                  '  Stop
                End If
            End If
        Loop
       
       
        Regions.Add Array(x, y, x2, y2)
        ' mark the region as done
        For yc = y To y2
            For xc = x To x2
                bDone(xc, yc) = True
            Next
        Next
        'start searching from next point
        'y = y2 + 1
       
        x = x2
   End If
   
    ' move to next pixel
    x = x + 1
    If x > w Then
        x = 0
        y = y + 1
        If y > h Then
            Exit Do
        End If
    End If
   
Loop


Dim ar
MsgBox "No of changes=" + CStr(Regions.Count)

On Error Resume Next
Kill "C:\results\*.bmp"
On Error GoTo 0
   

' Create pictures from the changed regions
For Each ar In Regions
    x = ar(0)
    y = ar(1)
    x2 = ar(2)
    y2 = ar(3)
   
   
    Dim tw As Long
    Dim th As Long
    tw = x2 - x + 1
    th = y2 - y + 1
    picSave.Move 0, 0, tw * 2, th
    picSave.Cls

    picSave.PaintPicture picFirst.Image, 0, 0, tw, th, x, y, tw, th, RasterOpConstants.vbSrcCopy
    picSave.PaintPicture picSecond.Image, tw, 0, tw, th, x, y, tw, th, RasterOpConstants.vbSrcCopy
    picSecond.Line (x, y)-Step(tw, th), RGB(0, 0, 0), B

   
    DoEvents
   
    SavePicture picSave.Image, "C:\results\" + CStr(x) + "_" + CStr(y) + "_" + CStr(x2) + "_" + CStr(y2) + ".bmp"

   
Next

End Sub
0
 
LVL 17

Expert Comment

by:inthedark
ID: 9670085
You will also need this in your decs.

Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long

0
 
LVL 17

Accepted Solution

by:
inthedark earned 500 total points
ID: 9670212
Here is the bit to compare images real fast it tooks just about 0.2 seconds.  You get the best results in a compiled exe.

replace the code between ' compare the images and ' extract the regions
with this bit:

' Directly map the picture's bitmap memory to an array
LoadPicFirst picFirst

' and for the other picture
LoadPicSecond picSecond

' compare the arrays
For Y = 0 To mvarUBoundY
    For X = 0 To mvarUBoundX
        bDiffs(X, Y) = FirstPicData(X, Y) <> SecondPicData(X, Y)
    Next X
Next Y

' must do this to stop VB getting screwed
CopyMemory ByVal VarPtrArray(FirstPicData), 0&, 4
CopyMemory ByVal VarPtrArray(SecondPicData), 0&, 4

---------------------Now some declarations
Option Explicit

Dim bDiffs() As Boolean
Dim bDone() As Boolean


Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Function GetObjectAPI Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long

Private Type SAFEARRAYBOUND
    cElements As Long
    lLbound As Long
End Type


Private Type SAFEARRAY2D
    cDims As Integer
    fFeatures As Integer
    cbElements As Long
    cLocks As Long
    pvData As Long
    Bounds(0 To 1) As SAFEARRAYBOUND
End Type

Private Type BITMAP
    bmType As Long
    bmWidth As Long
    bmHeight As Long
    bmWidthBytes As Long
    bmPlanes As Integer
    bmBitsPixel As Integer
    bmBits As Long
End Type

Private Type POINTAPI
    X As Long
    Y As Long
End Type

Private Type SCANLINE
    minx As Long
    maxx As Long
End Type
Private SecondPic As SAFEARRAY2D

Private FirstPic As SAFEARRAY2D

Private BMP As BITMAP

'Private Data() As Byte
Private FirstPicData() As Byte
Private SecondPicData() As Byte

'local variable(s) to hold property value(s)
Private mvarBytesPerPixel As Byte 'local copy
Private mvarColor As Long 'local copy
'local variable(s) to hold property value(s)
Private mvarUBoundX As Long 'local copy
Private mvarUBoundY As Long 'local copy
'local variable(s) to hold property value(s)
Private mvarErrorMsg As String 'local copy

---------------------Now some Subs


Private Function LoadPicFirst(p As StdPicture) As Boolean
'returns true if function works.
If GetObjectAPI(p.Handle, Len(BMP), BMP) Then    'retrieve bitmap information about p
    If BMP.bmWidth Then
        mvarBytesPerPixel = BMP.bmWidthBytes \ BMP.bmWidth
        If (mvarBytesPerPixel > 0) And (mvarBytesPerPixel < 4) Then
            ' make the local matrix point to bitmap pixels
            With FirstPic
              .cbElements = 1
              .cDims = 2
              .Bounds(0).lLbound = 0
              .Bounds(0).cElements = BMP.bmHeight
              .Bounds(1).lLbound = 0
              .Bounds(1).cElements = BMP.bmWidthBytes
              .pvData = BMP.bmBits
            End With
            ' copy bitmap data into byte array
            CopyMemory ByVal VarPtrArray(FirstPicData), VarPtr(FirstPic), 4
            mvarUBoundX = UBound(FirstPicData, 1) \ mvarBytesPerPixel
            mvarUBoundY = UBound(FirstPicData, 2)
            LoadPicFirst = True
        Else
            mvarErrorMsg = "Colour resolution must be 1-3 bytes instead of " & CStr(mvarBytesPerPixel)
        End If
    Else
        mvarErrorMsg = "Picture width cannot be zero"
    End If
Else
    mvarErrorMsg = "Can't retrieve BMP object"
End If
End Function

Private Function LoadPicSecond(p As StdPicture) As Boolean
'returns true if function works.
If GetObjectAPI(p.Handle, Len(BMP), BMP) Then    'retrieve bitmap information about p
    If BMP.bmWidth Then
        mvarBytesPerPixel = BMP.bmWidthBytes \ BMP.bmWidth
        If (mvarBytesPerPixel > 0) And (mvarBytesPerPixel < 4) Then
            ' make the local matrix point to bitmap pixels
            With SecondPic
              .cbElements = 1
              .cDims = 2
              .Bounds(0).lLbound = 0
              .Bounds(0).cElements = BMP.bmHeight
              .Bounds(1).lLbound = 0
              .Bounds(1).cElements = BMP.bmWidthBytes
              .pvData = BMP.bmBits
            End With
            ' copy bitmap data into byte array
            CopyMemory ByVal VarPtrArray(SecondPicData), VarPtr(SecondPic), 4
            mvarUBoundX = UBound(SecondPicData, 1) \ mvarBytesPerPixel
            mvarUBoundY = UBound(SecondPicData, 2)
            LoadPicSecond = True
        Else
            mvarErrorMsg = "Colour resolution must be 1-3 bytes instead of " & CStr(mvarBytesPerPixel)
        End If
    Else
        mvarErrorMsg = "Picture width cannot be zero"
    End If
Else
    mvarErrorMsg = "Can't retrieve BMP object"
End If
End Function
0
 

Author Comment

by:JimHubbard
ID: 9672835
Thanks for the posts.

Had to get some sleep this morning, now have to go out of town for the afternoon.

BUT, I *will* test all of these solutions and award points tonight.

Thanks for your suggestions!

Jim
0
 
LVL 17

Expert Comment

by:inthedark
ID: 9674229
Sleep is the most valuable resource you can deploy!
0
 

Author Comment

by:JimHubbard
ID: 9675805
Most excellent inthedark!

Thanks!

Jim
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Article by: Martin
Here are a few simple, working, games that you can use as-is or as the basis for your own games. Tic-Tac-Toe This is one of the simplest of all games.   The game allows for a choice of who goes first and keeps track of the number of wins for…
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now