Visual Basic Express 2008 - Working With USB Cameras and JPG Images

Part III - Motion Detection (Virtual Guitar)

   

Download The VB Express Project Files


This VB Express Project demonstrates motion detection using a USB camera.  The screen on the left is the live video stream of jpeg photos.  The images are mirrored into the screen on the right and the blue lines represent motion detection "trigger areas".  If motion is detected along these lines, a unique tone is played for each line.  It is a sort of Virtual Guitar or Piano.

The number of changed pixels in each vertical bar is displayed in the 12 textboxes.  The sensitivity to motion may be set by the user.

Install Note:  There is an extra folder named "ToneWavs".  This folder must be installed as C:\ToneWavs\  otherwise the program will not know where to find the wav files needed to play tones.

   
Instructions on saving the state of check boxes and radio buttons between sessions. This is accomplished through property binding, which is not intuitive in VB Express.
   
Return to:  
Motion Patrol  
Basic Video Capture  
Save and Retrieve USB Photos  
   
   
   

 

 

Selections From The Project Are Listed here.  Download the project files for complete project code.

 

The bitmap for an image contains header information followed by the actual pixel information.  I'm using a USB camera with a height of 640 pixels by a width of 480 pixels.  The pixel information is RGB, saved as 4 bytes.  Blue is the first byte of each group, Green is the second byte, Red is the third and the 4th is not used.  Each line of pixel data has 2560 bytes (640x4).  The program skips the header information using (Dim ptr As IntPtr = bmpData.Scan0), and then sores the pixel information in a one dimension  byte array named "rgbValues", using System.Runtime.InteropServices.Marshal.Copy.  The image pixel information is then read and manipulated within the array before being displayed back to a picturebox.

 

Motion is detected by comparing the pixel information between consecutive images taken.  The timer, Timer1, tick event controls the time between images.  It has been set to a default of 1/10th of a second.  Instead of comparing every pixel, only pixel information along 13 vertical lines is analyzed. 

 

Private Sub InitializeTimer()

'Set Timer Interval 1000 = 1 sec

Timer1.Interval = 100

Timer1.Start()

End Sub

 

 

Private Sub Timer1_Tick(ByVal Sender As Object, ByVal e As EventArgs) Handles Timer1.Tick

'Use odd/even to flip flop between images captured. Capture

'one frame as dataA and the next as dataB for comparison

If OddEvenTextBox.Text = "odd" Then

    OddEvenTextBox.Text = "even"

Else

    OddEvenTextBox.Text = "odd"

End If

CaptureImage()

End Sub

 

 

Private Sub CaptureImage()

Dim dataA As IDataObject

Dim dataB As IDataObject

Dim BitmapCaptureImageA As Bitmap

Dim BitmapCaptureImageB As Bitmap

 

' Retreive Bitmap Images, first image A and then image B for comparison

If OddEvenTextBox.Text = "odd" Then

'Capture image

 dataA = CaptureImageA()

'Convert image to bitmap

BitmapCaptureImageA = BitmapCapture(dataA)

'Get the horizontal and vertical line data

Z = GetLines(BitmapCaptureImageA)

Else

dataB = CaptureImageA()

BitmapCaptureImageB = BitmapCapture(dataB)

H2 = GetLines(BitmapCaptureImageB)

End If

Dim C As String = ""

' Get the number of changed points in each horz and vert bar

' Each value in the array hold the total number of changed points on the bar

Dim HBV(9) As Integer

Dim VBV(12) As Integer

Try

    Tolerance = SensitivityTextBox.Text

Catch ex As Exception

End Try

VBV = GetVB(Tolerance)

 

' Now find the vertical bar with the largest change

Dim LargestCase As Integer = 0

Dim Largest As Integer = 0

For x As Integer = 0 To 12

If VBV(x) > Largest Then

    Largest = VBV(x)

    LargestCase = x

End If

Next

'Play a note that represents the vertical bar with largest change

If Largest > Tolerance And VertTones.Checked Then

        PlayMe(LargestCase)

End If

LastVertPosition = LargestCase

 

If LastVertTextBox.Text <> LastVertPos Then

MovementFlag = True

Movement()

End If

LastLRMotion = LeftRightMot

'PrintHBands(HBV)

PrintVBands(VBV)

End Sub

 

 

 

 

Private Function CaptureImageA() As IDataObject

Dim data As IDataObject

'---copy the image to the clipboard---

SendMessage(hWnd, WM_CAP_EDIT_COPY, 0, 0)

'---retrieve the image from clipboard

data = Clipboard.GetDataObject()

Return data

End Function

 

 

 

Private Function BitmapCapture(ByVal dataA As IDataObject) As Bitmap

'Convert Image to Bitmap

If dataA.GetDataPresent(GetType(System.Drawing.Bitmap)) Then

        BitmapCaptureImage = CType(dataA.GetData(GetType(System.Drawing.Bitmap)), Image)

End If

Return BitmapCaptureImage

End Function

 

 

 

Private Function GetLines(ByVal BitmapCaptureImageD As Bitmap) As Byte()

Dim bmp As New Bitmap(BitmapCaptureImageD)

Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)

Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rect, _

Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)

' Get the address of the first line.

Dim ptr As IntPtr = bmpData.Scan0

' Declare an array to hold the bytes of the bitmap.

Dim bytes As Integer = bmpData.Stride * bmp.Height

Dim rgbValues(bytes - 1) As Byte

System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes)

Dim Arrayy(bmpData.Height, bmpData.Stride) As Byte

Dim HB(12641) As Byte

Dim Length As Integer = bmpData.Stride

Dim Col As Integer = 0

Dim Row As Integer = 0

Dim VRowMax As Integer = 0

Dim VColMax As Integer = 0

Dim HRowMax As Integer = 0

Dim HColMax As Integer = 0

 

VidWidth = bmp.Width

VidHeight = bmp.Height

VertSpace = bmp.Height / 9 - (bmp.Height Mod 9) * 0.1

HorzSpace = bmp.Width / 12 - (bmp.Width Mod 12) * 0.1

 

'Get Vertical Band Information

Row = 0

Col = 1

For y As Integer = 1 To 12

For x As Integer = 1 To bmpData.Height - 1

HB(9 * bmp.Width + ((y - 1) * bmp.Height) + x) = rgbValues((Length * x) + (4 * VertSpace * y + 1))

If GridOn.Checked Then

'(Length * x) is the row of bitmap pixel data

'4 * VertSpace * y is the vertical column of data for the line

'The 0,1,2 are the offsets for RGB data

rgbValues((Length * x) + (4 * VertSpace * y + 0)) = 255 'blue

rgbValues((Length * x) + (4 * VertSpace * y + 1)) = 0 'green

rgbValues((Length * x) + (4 * VertSpace * y + 2)) = 0 'red

' Make lines bold by doubling line width

rgbValues((Length * x) + (4 * VertSpace * y + 0) + 4) = 255 'blue

rgbValues((Length * x) + (4 * VertSpace * y + 1) + 4) = 0 'green

rgbValues((Length * x) + (4 * VertSpace * y + 2) + 4) = 0 'red

End If

Next

Next

' Copy the RGB values back to the bitmap

System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes)

' Unlock the bits.

bmp.UnlockBits(bmpData)

'Flip the image so it looks like a mirror

bmp.RotateFlip(RotateFlipType.RotateNoneFlipX)

' Draw Stuff Like Bouncing Balls On Screen. Select From Check Boxes

DrawOnBitmap(bmp)

'Now put it in the picturebox

PictureBox1.Image = bmp.GetThumbnailImage(320, 240, Nothing, Nothing)

LastVertTextBox.Text = BallLocationV

Return HB

End Function

 

 

 

Private Function GetVB(ByVal Tolerance As Integer) As Integer()

Dim VBvariance(12) As Integer

Dim ptr As Integer = 0

'Return an array containing the number of changed pixels in each vertical line

' The ptr is used to orient the bar data from left to right

For j As Integer = 9 To 20

ptr = 20 - j

For k As Integer = 1 To VidHeight

If Math.Abs(CInt(Z(9 * VidWidth + ((j - 9) * VidHeight) + k)) - CInt(H2((9 * VidWidth) + ((j - 9) * VidHeight) + k))) >=        Tolerance Then

VBvariance(ptr) += 1

End If

Next

Next

Return VBvariance

End Function

 

 

Private Sub PlayMe(ByVal tune As Integer)

' play a wav file

'If LastNote <> tune Then

Select Case tune

Case 0

My.Computer.Audio.Play("C:\ToneWavs\1.wav", _

AudioPlayMode.Background)

Case 1

My.Computer.Audio.Play("C:\ToneWavs\2.wav", _

AudioPlayMode.Background)

Case 2

My.Computer.Audio.Play("C:\ToneWavs\3.wav", _

AudioPlayMode.Background)

Case 3

My.Computer.Audio.Play("C:\ToneWavs\4.wav", _

AudioPlayMode.Background)

Case 4

My.Computer.Audio.Play("C:\ToneWavs\5.wav", _

AudioPlayMode.Background)

Case 5

My.Computer.Audio.Play("C:\ToneWavs\6.wav", _

AudioPlayMode.Background)

Case 6

My.Computer.Audio.Play("C:\ToneWavs\7.wav", _

AudioPlayMode.Background)

Case 7

My.Computer.Audio.Play("C:\ToneWavs\8.wav", _

AudioPlayMode.Background)

Case 8

My.Computer.Audio.Play("C:\ToneWavs\9.wav", _

AudioPlayMode.Background)

Case 9

My.Computer.Audio.Play("C:\ToneWavs\10.wav", _

AudioPlayMode.Background)

Case 10

My.Computer.Audio.Play("C:\ToneWavs\11.wav", _

AudioPlayMode.Background)

Case 11

My.Computer.Audio.Play("C:\ToneWavs\12.wav", _

AudioPlayMode.Background)

Case 12

My.Computer.Audio.Play("C:\ToneWavs\13.wav", _

AudioPlayMode.Background)

Case Else

My.Computer.Audio.Play("C:\ToneWavs\13.wav", _

AudioPlayMode.Background)

End Select

LastNote = tune

End Sub

 

 

 

Private Sub Movement()

If CDec(LastVertTextBox.Text) = CDec(LastVertPos) - 1 Then

LRMotion = "Left"

LeftMotionCount += 1

LRMotionCount -= 1

ElseIf CDec(LastVertTextBox.Text) = CDec(LastVertPos) + 1 Then

LRMotion = "Right"

RightMotionCount += 1

LRMotionCount += 1

End If

 

If LRMotionCount < 0 Then

    LRMotionCount = 0

ElseIf LRMotionCount > 9 Then

    LRMotionCount = 9

End If

LastVertPos = LastVertTextBox.Text

End Sub

 

 

 

Private Sub PrintVBands(ByVal VBV() As Integer)

TextBox11.Text = VBV(0)

TextBox12.Text = VBV(1)

TextBox13.Text = VBV(2)

TextBox14.Text = VBV(3)

TextBox15.Text = VBV(4)

TextBox16.Text = VBV(5)

TextBox17.Text = VBV(6)

TextBox18.Text = VBV(7)

TextBox19.Text = VBV(8)

TextBox20.Text = VBV(9)

TextBox21.Text = VBV(10)

TextBox22.Text = VBV(11)

End Sub

 

 

Private Sub DrawOnBitmap(ByVal bmp As Bitmap)

Dim g As Graphics = Graphics.FromImage(bmp)

Dim VertPosition As Integer = VidHeight / 2

Dim BallSize As Integer = VidHeight / 20

'Draw Ball On Screen For Position

If BallOn.Checked Then

'g.DrawEllipse(Pens.Yellow, LastVertPosition * 53, 240, 20, 20)

g.FillEllipse(Brushes.Snow, LastVertPosition * VertSpace, VertPosition, BallSize, BallSize)

BallLocationV = LastVertPosition

End If

 

End Sub