Page 1 of 2

Webcam motion detector thingy

Posted: Thu Apr 16, 2009 5:18 pm
by Joakim Christiansen
Since I'm planning to make Terminators to take over the world I started messing around with how to analyze frames from my webcam (the AI needs to be able to see). With this piece of code (which is just for fun) I'm able to detect motion and to demonstrate this I visualize it with green boxes.

Feel free to tweak and improve, it would be cool if anyone help me make it detect moving objects, like a hand for example and then make it so you can use your hand to move the cursor. Or detect faces, etc. :D

Edit:
If it is slow then run without debugger, wow what a speed :D

Code: Select all

EnableExplicit

;{ CAP constants
#WM_CAP_START = #WM_USER
#WM_CAP_DRIVER_CONNECT = #WM_CAP_START + 10
#WM_CAP_DRIVER_DISCONNECT = #WM_CAP_START + 11
#WM_CAP_DRIVER_GET_CAPS = #WM_CAP_START + 14
#WM_CAP_EDIT_COPY = #WM_CAP_START + 30
#WM_CAP_SET_PREVIEW = #WM_CAP_START + 50
#WM_CAP_SET_PREVIEWRATE = #WM_CAP_START + 52
#WM_CAP_STOP = #WM_CAP_START + 68
#WM_CAP_SET_SCALE = #WM_CAP_START + 53
#WM_CAP_START = #WM_USER
#WM_CAP_DLG_VIDEOSOURCE = #WM_CAP_START + 42
;}
#Main=0
#camWidth = 640
#camHeight = 480
#w16 = 40
#h16 = 30

Structure avg
  r.l
  g.l
  b.l
EndStructure

Global hWndC
Define Ccam_lib1, *capAddress

Procedure rotate90(*MemoryTarget,W,H,size)
  Protected X,Y,Target, Origin, *MemoryOrigin = AllocateMemory(size)
  CopyMemory(*MemoryTarget,*MemoryOrigin,size)
  For Y = 0 To H - 1
    For X = 0 To W - 1
      Origin = (Y * W + X) << 2
      Target = ((H - Y - 1) + (X * H)) << 2
      PokeL(*MemoryTarget + Target, PeekL(*MemoryOrigin + Origin))
    Next
  Next
  FreeMemory(*MemoryOrigin)
EndProcedure
Procedure greyScale(*Memory,MemorySize)
  Protected Counter, Color
  For Counter = 0 To MemorySize - 1 Step 4
    Color = PeekL(*Memory + Counter)
    Color = (Red(Color) + Green(Color) + Blue(Color)) / 3
    PokeL(*Memory + Counter, RGB(Color, Color, Color))
  Next
EndProcedure
Procedure captureImage(dummy)
  Protected image, bmp.BITMAP, imageID, imageSize, *bits, pos
  Protected Dim prev.avg(#w16,#h16)
  Protected x,y,yy,xx, avg.avg, color, diff
  
  Repeat
    SendMessage_(hWndC,#WM_CAP_EDIT_COPY,0,0)
    image = GetClipboardImage(#PB_Any,32)
    imageID = ImageID(image) ;maybe flip it first
    
    GetObject_(imageID,SizeOf(BITMAP),@bmp.BITMAP)
    imageSize = bmp\bmWidthBytes * bmp\bmHeight
    *bits = bmp\bmBits
    
    StartDrawing(WindowOutput(0))
    DrawImage(imageID,0,0)
    DrawingMode(#PB_2DDrawing_Outlined)
    
    For x=0 To #w16-1
      For y=0 To #h16-1
        avg\r = 0: avg\g=0: avg\b=0
        For xx=x*16 To x*16+16-1
          For yy=y*16 To y*16+16-1
            pos = (yy * bmp\bmWidth + xx) << 2
            color = PeekL(*bits+pos)
            avg\r + Red(color)
            avg\g + Green(color)
            avg\b + Blue(color)
            ;Plot(xx,479-yy,color)
          Next
        Next
        avg\r = avg\r / 256
        avg\g = avg\g / 256
        avg\b = avg\b / 256
        ;Box(x*16,y*16,16,16,RGB(avg\r,avg\g,avg\b))
        diff = 0
        diff + Abs(prev(x,y)\r - avg\r)
        diff + Abs(prev(x,y)\g - avg\g)
        diff + Abs(prev(x,y)\b - avg\b)
        If diff > 20
          Box(x*16,464-y*16,16,16,#Green)
          ;Circle(x*16+8,464-y*16+8,8,#Green) or circles if you want
        EndIf
        prev(x,y)\r = avg\r
        prev(x,y)\g = avg\g
        prev(x,y)\b = avg\b
      Next
    Next
    
      ;DrawImage(imageID,0,0)
    StopDrawing()
    FreeImage(image)
    
    ;Delay(2000)
  ForEver
EndProcedure

If OpenWindow(#Main,0,0,640,480,"Webcam Motion Detector by JLC",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  Ccam_lib1 = OpenLibrary(#PB_Any, "avicap32.dll")
  If Ccam_lib1
    *capAddress = GetFunction(Ccam_lib1, "capCreateCaptureWindowA")
    hWndC = CallFunctionFast(*capAddress, "My Capture Window", #WS_CHILD | #WS_VISIBLE, 0,0, 1 , 1, WindowID(#Main),0)
    SendMessage_(hWndC, #WM_CAP_DRIVER_CONNECT, 0, 0)
    SendMessage_(hWndC, #WM_CAP_SET_OVERLAY, #True, 0)
    SendMessage_(hWndC, #WM_CAP_SET_PREVIEW, #True, 0)
    SendMessage_(hWndC, #WM_CAP_SET_PREVIEWRATE, 1, 0)
  EndIf
EndIf

CreateThread(@captureImage(),0)

Repeat
  Delay(10)
Until WaitWindowEvent() = #PB_Event_CloseWindow

SendMessage_(hWndC,#WM_CAP_STOP,0,0)
SendMessage_(hWndC,#WM_CAP_DRIVER_DISCONNECT,0,0)
DestroyWindow_(hWndC)
CloseLibrary(Ccam_lib1)

Posted: Thu Apr 16, 2009 6:15 pm
by DarkDragon
Hello,

I have a glass table and fixed a paper on top of it. Always when I go down with my fingers, the shadow is shown at the bottom of the paper. So I have made a multi touchpad, which works quite well, also with other software.

Here is my own code:

Code: Select all

#WM_CAP_START = #WM_USER

#WM_CAP_SET_CALLBACK_ERROR = #WM_CAP_START + 2
#WM_CAP_SET_CALLBACK_STATUS = #WM_CAP_START + 3
#WM_CAP_SET_CALLBACK_YIELD = #WM_CAP_START + 4
#WM_CAP_SET_CALLBACK_FRAME = #WM_CAP_START + 5
#WM_CAP_SET_CALLBACK_VIDEOSTREAM = #WM_CAP_START + 6
#WM_CAP_SET_CALLBACK_WAVESTREAM = #WM_CAP_START + 7

#WM_CAP_DRIVER_CONNECT        =  #WM_USER + 10
#WM_CAP_DRIVER_DISCONNECT     =  #WM_USER + 11
#WM_CAP_DRIVER_GET_CAPS = #WM_CAP_START + 14

#WM_CAP_DLG_VIDEOFORMAT = #WM_CAP_START + 41
#WM_CAP_DLG_VIDEOSOURCE = #WM_CAP_START + 42
#WM_CAP_DLG_VIDEODISPLAY = #WM_CAP_START + 43

#WM_CAP_SET_PREVIEW = #WM_CAP_START + 50
#WM_CAP_SET_PREVIEWRATE = #WM_CAP_START + 52
#WM_CAP_GET_STATUS = #WM_CAP_START + 54

#WM_CAP_FILE_SAVEDIB          =  #WM_USER + 25
#WM_CAP_SET_SCALE             =  #WM_USER + 53

#WM_CAP_SET_CALLBACK_CAPCONTROL = #WM_CAP_START + 85

Structure VIDEOHDR
  lpData.l
  dwBufferLength.l
  dwBytesUsed.l
  dwTimeCaptured.l
  dwUser.l
  dwFlags.l
  dwReserved.l[3]
EndStructure

Structure CAPSTATUS
  uiImageWidth.l
  uiImageHeight.l
  fLiveWindow.l
  fOverlayWindow.l
  fScale.l
  ptScroll.Point
  fUsingDefaultPalette.l
  fAudioHardware.l
  fCapFileExists.l
  dwCurrentVideoFrame.l
  dwCurrentVideoFramesDropped.l
  dwCurrentWaveSamples.l
  dwCurrentTimeElapsedMS.l
  hPalCurrent.l
  fCapturingNow.l
  dwReturn.l
  wNumVideoAllocated.l
  wNumAudioAllocated.l
EndStructure


CompilerIf #PB_Compiler_Unicode = 0
  Import "avicap32.lib"
    capCreateCaptureWindow.l(name.s, style.l, x.l, y.l, width.l, height.l, hWndParent.l, nId.l) As "_capCreateCaptureWindowA@32"
    capGetDriverDescription.l(index.l, name.l, cbName.l, ver.l, cbVer.l) As "_capGetDriverDescriptionA@20"
  EndImport
  CompilerElse
  Import "avicap32.lib"
    capCreateCaptureWindow.l(name.s, style.l, x.l, y.l, width.l, height.l, hWndParent.l, nId.l) As "_capCreateCaptureWindowW@32"
    capGetDriverDescription.l(index.l, name.l, cbName.l, ver.l, cbVer.l) As "_capGetDriverDescriptionW@20"
  EndImport
CompilerEndIf

ExamineDesktops()

; Macro GetColorXY(DataPointer, PixelX, PixelY, ImageWidth, ImageHeight)
;   PeekL(DataPointer + ((ImageHeight - PixelY) * ImageWidth + PixelX) * 3) & $00FFFFFF
; EndMacro

Structure SBGR
  b.b
  g.b
  r.b
EndStructure

Global *oldMem.BYTE, oldPosX, oldPosY, made

*oldMem = AllocateMemory(320 * 240 * 3)
made = 0

CreateImage(0, 320, 240)

Procedure FrameCallback(hWnd.l, *lpVHdr.VIDEOHDR)
  Protected *VideoMemoryAdress1.SBGR = *lpVHdr\lpData
  Protected *VideoMemoryAdress2.SBGR = *oldMem
  Protected leftred1, leftred2, leftgreen1, leftgreen2, leftblue1, leftblue2
  Protected red1, red2, green1, green2, blue1, blue2
  Protected density1, density2
  Protected maxChange, change
  Protected posX, posY

  If made <= 0
    CopyMemory(*lpVHdr\lpData, *oldMem, 320 * 240 * 3)
    made = 1
  EndIf

  maxChange = 0

  For y = 240 - 1 To 0 Step -1

    blue1  = *VideoMemoryAdress1\b & $FF
    green1 = *VideoMemoryAdress1\g & $FF
    red1   = *VideoMemoryAdress1\r & $FF

    blue2  = *VideoMemoryAdress2\b & $FF
    green2 = *VideoMemoryAdress2\g & $FF
    red2   = *VideoMemoryAdress2\r & $FF

    *VideoMemoryAdress1 + 3
    *VideoMemoryAdress2 + 3

    For x = 320 - 1 To 1 Step -1

      leftblue1  = blue1
      leftgreen1 = green1
      leftred1   = red1

      leftblue2  = blue2
      leftgreen2 = green2
      leftred2   = red2

      blue1  = (*VideoMemoryAdress1\b & $FF + leftblue1) * 0.5
      green1 = (*VideoMemoryAdress1\g & $FF + leftgreen1) * 0.5
      red1   = (*VideoMemoryAdress1\r & $FF + leftred1) * 0.5

      blue2  = (*VideoMemoryAdress2\b & $FF + leftblue2) * 0.5
      green2 = (*VideoMemoryAdress2\g & $FF + leftgreen2) * 0.5
      red2   = (*VideoMemoryAdress2\r & $FF + leftred2) * 0.5

      density1 = (red1 + green1 + blue1) * 0.3333
      density2 = (red2 + green2 + blue2) * 0.3333

      change = Pow(density2 - density1, 2) * 0.1

      *VideoMemoryAdress1\b = change
      *VideoMemoryAdress1\g = *VideoMemoryAdress1\b
      *VideoMemoryAdress1\r = *VideoMemoryAdress1\b

      ;       change * (320 * 320 + 240 * 240) * 3 / (Pow(x - oldPosX, 2) + Pow(y - oldPosY, 2))

      If maxChange < change
        maxChange = change
        posX = x
        posY = y
      EndIf

      *VideoMemoryAdress1 + 3
      *VideoMemoryAdress2 + 3
    Next x
  Next y

  posX = Int((DesktopWidth(0)  / (320.0 * 2)) * posX) * 2
  posY = Int((DesktopHeight(0) / (240.0 * 2)) * posY) * 2
  
  *VideoMemoryAdress1 = *lpVHdr\lpData

  StartDrawing(ImageOutput(0))
  For y = 240 - 1 To 0 Step -1
    For x = 0 To 320 - 1
      Plot(x, y, RGB(*VideoMemoryAdress1\r & $FF, *VideoMemoryAdress1\g & $FF, *VideoMemoryAdress1\b & $FF))
      *VideoMemoryAdress1 + 3
    Next
  Next
  StopDrawing()

  If maxChange > 130
    posX = oldPosX + (posX - oldPosX) * 0.25
    posY = oldPosY + (posY - oldPosY) * 0.25

    SetWindowTitle(0, Str(posX) + ":" + Str(posY))
    SetCursorPos_(posX, posY)
    oldPosX = posX
    oldPosY = posY
  EndIf

EndProcedure

hWnd = OpenWindow(0, 0, 0, 320, 240, "Touchpad", #PB_Window_SystemMenu)

hWebcam = capCreateCaptureWindow("Bradan.Eu - Touchpad", #WS_VISIBLE | #WS_CHILD, 0, 0, 320, 240, hWnd, 0)

SendMessage_(hWebcam, #WM_CAP_DRIVER_CONNECT          , 0, 0)
SendMessage_(hWebcam, #WM_CAP_SET_SCALE               , 1, 0)
SendMessage_(hWebcam, #WM_CAP_SET_PREVIEWRATE         , 10, 0)
SendMessage_(hWebcam, #WM_CAP_SET_PREVIEW             , 1, 0)
SendMessage_(hWebcam, #WM_CAP_SET_CALLBACK_FRAME      , 0, @FrameCallback())

Repeat
Until WaitWindowEvent(3) = #PB_Event_CloseWindow
End
Maybe you have to change the line where it says " If maxChange > 130" to your own value, because the light-conditions aren't always the same. And afaik if you have a milk-glass table you don't even have to put a paper on it ;-) .

Posted: Thu Apr 16, 2009 8:16 pm
by Joakim Christiansen
Nice DarkDragon!
I will play with that later :D
But PureBasic complained about not finding "avicap32.lib" btw, but I'll figure it out. Nice to see you used the FrameCallback, I wanted some example for that.

Posted: Fri Apr 17, 2009 12:53 am
by pdwyer
I don't know if it's of any use to you (it certainly wouldn't help with the hand controlled cursor thing) but I have a proc called "ImageDistance" that I could post (there's an older version on the forums) that gives you a distance of difference between two images. Zero if identical, <100 if very similar 10,000+ if completely different.

You could use something like that to check the image distance between frames and then the threshold which could be set by the user would be how sensitive it was to movement (change). You could also keep the smaller signatures of frames if you wanted to see if a scene had returned to a previous situation without keeping the whole image frame.

Just thought I'd mention it, not necessarily where you are going I guess.

Posted: Fri Apr 17, 2009 2:03 am
by idle
This is coming up quite a bit.

The main problems you encounter tracking with a web cam is simply due to loads of noise and variances in illumination, which makes it really hard to segment your image properly.

Motion detection in it self is simply a case of image subtraction, you can then compute a flow field and determine what to do from it.
what your really need is to get a bunch of vectors that describe the motion using something like blob analysis.

Though you could also just use the moments of an object and track a set of points. Blob analysis is usually reliant on you having segmented your image, this is problematic as it usually involves running numerous algorithms to produce a suitable blob pattern. eg requiring median cut, dynamic thresholding, dilation / erosion... It really depends on what your looking for.

So anyway here's a cheats way of blob analysis minus the connected component part which is a bitch to write. How you use it really depends on what you need.

While this isn't useful for shape detection without the connected component scan, It's got an ellipse routine though you would need to add a perimeter accumulator in the analysis loop and accommodate for diagonals between scan lines to get the right result. You could then determine if a shape an ellipse or circle. Shape params are scale and rotational invariant though you need to use bit of fuzzyness.

So for instance call the Moments routine with the entire width and height of the image to get the global moments. Center of mass, orientation, bounding box.

Then call moments a second time with a say a 32 x 32 grid.

Then call the moments again redefining the region to center them.
In other words you should get a nice point cloud mesh.
You've can then merge the point cloud together to minimize the set and track the points.

You can also reuse the point cloud for the next frame assuming that the movement isn't going to be to great.

Code: Select all


Structure blobs
   cx.i
   cy.i
   area.i
   minx.i
   maxx.i
   miny.i
   maxy.i
   mmx.i
   mmy.i
   orient.f
   shape.f
   mja.f
   mia.f 
   dx.f
EndStructure   

Procedure Moments(img,*rc.RECT,*blob.blobs,draw)

;see image moments http://en.wikipedia.org/wiki/Image_moments for details 

Protected sumXX, sumYY, sumX, sumY, Area, MinX, MaxX, MinY, MaxY, Ix.f ,Iy.f, Ixy.f, cx.f, cy.f
Protected ta.f ,ra,f,rb.f,Shape.f, majoraxis.f, orient.f

hdc = StartDrawing(ImageOutput(img))

For x = *rc\left To *rc\right
  For y = *rc\top To *rc\bottom 
   ;test for pixel val  
   px = Red(GetPixel_(hdc,x,y))      
   If px < 100    

     sumXX + (x*x)
     sumYY + (y*y)
     sumXY + (x*y)
     sumX + x
     sumY + y
     Area + 1 
     
     If minX = 0 
       minx = x
     EndIf 
     
     If x > MaxX 
       MaxX = x
     EndIf 
     
     If miny = 0 
       MinY = y
     EndIf 
  
     If y > maxY
       maxY = y
     EndIf 

   EndIf    

  Next 
Next

;set the mimimum area 
If area > 20
;cx = centerX cy=centerY  1st moment
cx = sumx / Area
cy = sumy / Area

*blob\Area = Area 
*blob\cx = cx  
*blob\cy = cy 


;Ix Iy Ixy 2nd moments 
Ix = SumXX - (Area * (cx * cx))
Iy = SumYY - (Area * (cy * cy))
Ixy = sumxy - (Area * (cx * cy))


;the shape parameter is invarent to scale and rotation 
ta = Sqr(((Ix + Iy) * (Ix + Iy)) - (4 * (Ixy * Ixy))) / 2
Ra = (Ix + Iy) + ta
Rb = (Ix + Iy) - ta
Shape = Ra / Rb

*blob\shape = Shape
*blob\minx = minx
*blob\maxx = maxx
*blob\miny = miny
*blob\maxy = maxy 

BoundingArea = (maxX - minX) * (maxY - minY)
mmx = (maxX - minX)
mmy = (maxY - minY)

;fiddly orientaion calculation 
If Ixy < 0 Or Iy = Ix 
  If Ixy < IY And Ixy < IX 
     orient = 0
  Else    
     orient = ATan(90.0) 
  EndIf 
  majoraxis = (Sqr(mmx * mmx + mmy * mmy)) * 0.5
ElseIf mmx >= mmy 
   orient = (0.5 * (ATan(2 * (Ixy / (Iy - Ix))))) / (#PI / 180)
   majoraxis = (mmx / Cos(Abs(orient) / (180 / #PI))) * 0.5
  
   If Abs(Ix) >= Abs(Iy) 
       orient = (180 - orient) / (180 / #PI)
   Else
       orient = orient / (180 / #PI)
   EndIf
Else
   orient = (0.5 * (ATan(2 * Ixy / (Iy - Ix)))) / (#PI / 180)
   majoraxis = (mmy / Cos(Abs(orient) / (180 / #PI))) * 0.5
   If Ix >= Iy 
       orient = (90 + orient) / (180 / #PI)
   Else
       orient = (orient - 45) / (180 / #PI)
   EndIf
EndIf

minoraxis = (1.5*Area) / (#PI * majoraxis)
*blob\orient = orient 
*blob\mja = majoraxis
*blob\mia = minorAxis 
*blob\mmx = mmx
*blob\mmy = mmy


If draw 
;draw elipse 
 

For da = 1 To 500   
     
                    
     dx = cx + (Sin(da) * majoraxis)
     dy = cy + (Cos(da) * minoraxis)
          
     ndx = cx + (((dx - cx) * Cos(orient))) - (((dy - cy) * Sin(orient)))
     ndy = cy + (((dx - cx) * Sin(orient))) + (((dy - cy) * Cos(orient)))
          
     lx = SetPixel_(hdc, ndx, ndy, RGB(0, 0, 255))
Next

SetPixel_(hdc, Int(cx), Int(cy), RGB(255, 0, 0))


EndIf 

EndIf 

StopDrawing()

EndProcedure 

If OpenWindow(0,0,0,800,600,"test",#PB_Window_SizeGadget | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget |#PB_Window_MaximizeGadget | #PB_Window_ScreenCentered)

img1 = LoadImage(#PB_Any,"C:\hands\hand2.bmp")

width = ImageWidth(img1)
height = ImageHeight(img1)
ResizeWindow(0,0,0,width,height) 
ImageGadget(0,0,0,width,height,ImageID(img1))


rc.rect 
rc\left=0
rc\right=width
rc\top = 0
rc\bottom=height

;get the global moments of the image 
rblob.Blobs 
moments(img1,rc,@rblob,#True) 

st = GetTickCount_()

;specify grid size and call moments again 
nc = 32
gArea = (width/nc) * (height/nc)
Dim blob.blobs(nc*nc) 

For a = 0 To nc-1
 For b = 0 To nc-1 
   rc\left = a * width/nc 
   rc\right = a * width/nc + width/nc 
   rc\top = b * height/nc 
   rc\bottom = b * height/nc + height/nc
   moments(img1,rc,@blob(ct),#False )
   ct+1  
 Next 
Next 


;define a radius to rescan this will center the blobs  
dxy = 20 
;run through the momments obtained, to recenter them 
For a = 0 To nc * nc
   
   If blob(a)\cx <> 0 
   rc\left = blob(a)\cx -dxy
   rc\right = blob(a)\cx + dxy 
   rc\top = blob(a)\cy - dxy
   rc\bottom = blob(a)\cy + dxy
   
   If rc\left < 0 
     rc\right - rc\left 
     rc\left = 0
   EndIf 
   If rc\right > width 
     rc\left - (rc\right - width)
     rc\right = width 
   EndIf 
   If rc\top < 0 
      rc\bottom - rc\top 
      rc\top = 0 
   EndIf 
   If rc\bottom > height 
      rc\top = (rc\bottom - height)
      rc\bottom = height
   EndIf           
   
   moments(img1,rc,@blob(a),#True)
    
       
  EndIf 
Next  


et = GetTickCount_()

  
 
 SetWindowText_(WindowID(0),Str(et-st))
 SetGadgetState(0,ImageID(img1))

Repeat
 
    Event = WaitWindowEvent()
 
 Until event = #WM_CLOSE 

EndIf 




Posted: Fri Apr 17, 2009 2:12 am
by Rook Zimbabwe
The main problems you encounter tracking with a web cam is simply due to loads of noise and variances in illumination, which makes it really hard to segment your image properly.
I had been thinking of simply making an image and generating a checksum of that image and then a few seconds later taking another snapshot and generating a checksum of THAT snapshot and if the two were different motion happened... but I realize it is MUCH more complicated...

Posted: Fri Apr 17, 2009 5:42 am
by pdwyer
Thats basically what I was doing. It wasn't a checksum in the binary CRC sense though it was colour base. doing a shrink of the image first gives you a bit more of a vague area though so you don't need to deal with single pixel changes

http://www.purebasic.fr/english/viewtop ... e&start=15

Posted: Fri Apr 17, 2009 6:30 am
by idle
There was a problem with the orientation calculation.
I originally got the math from a paper published by National Instruments and it was wrong. So now it should give you the true orientation of a blob.

Code: Select all

Procedure Moments(img,*rc.RECT,*blob.blobs,draw)

;see image moments http://en.wikipedia.org/wiki/Image_moments for details 

Protected sumXX, sumYY, sumX, sumY, Area, MinX, MaxX, MinY, MaxY, Ix.f ,Iy.f, Ixy.f, cx.f, cy.f
Protected ta.f ,ra,f,rb.f,Shape.f, majoraxis.f, orient.f

hdc = StartDrawing(ImageOutput(img))

For x = *rc\left To *rc\right
  For y = *rc\top To *rc\bottom 
   ;test for pixel val  
   px = Red(GetPixel_(hdc,x,y))      
   If px < 100    

     sumXX + (x*x)
     sumYY + (y*y)
     sumXY + (x*y)
     sumX + x
     sumY + y
     Area + 1 
     
     If minX = 0 
       minx = x
     EndIf 
     
     If x > MaxX 
       MaxX = x
     EndIf 
     
     If miny = 0 
       MinY = y
     EndIf 
  
     If y > maxY
       maxY = y
     EndIf 

   EndIf    

  Next 
Next

;set the mimimum area 
If area > 20
;cx = centerX cy=centerY  1st moment
cx = sumx / Area
cy = sumy / Area

*blob\Area = Area 
*blob\cx = cx  
*blob\cy = cy 


;Ix Iy Ixy 2nd moments 
Ix = SumXX - (Area * (cx * cx))
Iy = SumYY - (Area * (cy * cy))
Ixy = sumxy - (Area * (cx * cy))


;the shape parameter is invarent to scale and rotation 
ta = Sqr(((Ix + Iy) * (Ix + Iy)) - (4 * (Ixy * Ixy))) / 2
Ra = (Ix + Iy) + ta
Rb = (Ix + Iy) - ta
Shape = Ra / Rb

*blob\shape = Shape
*blob\minx = minx
*blob\maxx = maxx
*blob\miny = miny
*blob\maxy = maxy 

BoundingArea = (maxX - minX) * (maxY - minY)
mmx = (maxX - minX)
mmy = (maxY - minY)

;fiddly orientaion calculation 
If Ixy < 0 Or Iy = Ix 
  If Ixy < IY And Ixy < IX 
     orient = 0
  Else    
     orient = ATan(90.0) 
  EndIf 
  majoraxis = (Sqr(mmx * mmx + mmy * mmy)) * 0.5
ElseIf mmx >= mmy 
   orient = (0.5 * (ATan(2 * (Ixy / (Iy - Ix))))) / (#PI / 180)
   majoraxis = (mmx / Cos(Abs(orient) / (180 / #PI))) * 0.5
  
   If Abs(Ix) >= Abs(Iy) 
       orient = (180 - orient) / (180 / #PI)
   Else
       orient = orient / (180 / #PI)
   EndIf
Else
   orient = (0.5 * (ATan(2 * Ixy / (Iy - Ix)))) / (#PI / 180)
   majoraxis = (mmy / Cos(Abs(orient) / (180 / #PI))) * 0.5
   If Ix >= Iy 
       orient = (90 + orient) / (180 / #PI)
   Else
       orient = (orient - 45) / (180 / #PI)
   EndIf
EndIf

minoraxis = (1.5*Area) / (#PI * majoraxis)
*blob\orient = orient 
*blob\mja = majoraxis
*blob\mia = minorAxis 
*blob\mmx = mmx
*blob\mmy = mmy


If draw 
;draw elipse 
 

For da = 1 To 500   
     
                    
     dx = cx + (Sin(da) * majoraxis)
     dy = cy + (Cos(da) * minoraxis)
          
     ndx = cx + (((dx - cx) * Cos(orient))) - (((dy - cy) * Sin(orient)))
     ndy = cy + (((dx - cx) * Sin(orient))) + (((dy - cy) * Cos(orient)))
          
     lx = SetPixel_(hdc, ndx, ndy, RGB(0, 0, 255))
Next

SetPixel_(hdc, Int(cx), Int(cy), RGB(255, 0, 0))


EndIf 

EndIf 

StopDrawing()


EndProcedure 

Posted: Fri Apr 17, 2009 7:38 am
by DarkDragon
Joakim Christiansen wrote:Nice DarkDragon!
I will play with that later :D
But PureBasic complained about not finding "avicap32.lib" btw, but I'll figure it out. Nice to see you used the FrameCallback, I wanted some example for that.
I don't know whats the replacement in PureBasic 4.30 for this. But you could use the PB 4.20 "avicap32.lib".

Posted: Fri Apr 17, 2009 10:45 am
by pdwyer
@Joakim Christiansen, Now I'm home and can try this, it's pretty cool!! 8) I didn't have any PB code that can use my web cam (never really tried).

I'm gonna have a play further.

So step two is teh Neural network to do the lie detecting from facial expression! ;)

Posted: Sat Apr 18, 2009 9:15 pm
by utopiomania
Thanks for sharing, Joakim! Generated a lot of useful info too. :wink:

Re: Webcam motion detector thingy

Posted: Mon Nov 23, 2009 10:31 am
by idle
a quick port of AVI capture and background window motion detection in the pb terrain sample.

set camera to 160 x 120
rock head side to side to look around

and for the terminally disinclined who find it to hard to follow instructions or even say thank you

I've added the example in a zip

http://www.idlearts.com/motion.zip

Re:

Posted: Wed Mar 17, 2010 1:39 pm
by dobro
DarkDragon wrote:CallFunctionFast(capId,"capGetDriverDescriptionA",i,pName,lName,pVer,lVer)
Hello,

I have a glass table and fixed a paper on top of it. Always when I go down with my fingers, the shadow is shown at the bottom of the paper. So I have made a multi touchpad, which works quite well, also with other software.


File no found "avicap32.lib" for your code in V4.41 :cry:

where to find "avicap32.lib" please ? :shock:

I have just "avicap32.dll"

and, the codes that

Code: Select all

CallFunctionFast(capId,"capGetDriverDescriptionA",i,pName,lName,pVer,lVer)
do not work and cause an error of string

for my webcam on my Samsung NC10 ;)

Re: Webcam motion detector thingy

Posted: Wed Mar 17, 2010 8:28 pm
by idle

Code: Select all

OpenLibrary(1,"avicap32.dll")
  *capCreateWindow=GetFunction(1,"capCreateCaptureWindowW")
  *capGetDriverDescrition=GetFunction(1,"capGetDriverDescriptionW")
  ;...
CloseLibrary(1)

Re: Webcam motion detector thingy

Posted: Thu Mar 18, 2010 11:34 pm
by dobro
thanks :)