Computer Vision Using AForge.NET

With the advances we've made in computing power today, we now have more choices than ever before on how we input data into our PCs.

One area of input that's moving faster than most is the area of "Artificial Intelligence."

Much of this drive is coming about as a result of the ever growing maker culture that small home automation kits such as the Arduino and Raspberry PI have brought about.  Many IoT tinkerers are now days playing with amateur robotics and home automation, creating some very interesting projects.

One of these projects is "AForge.NET."

AForge.NET is a complete Artificial Intelligence framework for .NET developers, allowing software creators to create neural networks, computer vision, and semi-autonomous statistical analysis.

One of the more fascinating aspects of the tool kit are the classes and assemblies dedicated to computer vision. In this post, we're going to create a simple Windows Forms application that will use your computer's web cam to turn your PC into a motion detection system.

Creating the User Interface

Fire up Visual Studio (I'm using VS 2013, but this will work under the community edition too) and create a new Windows Forms application.

AForge1
Figure 1: The new Windows Forms application

Before we can start to build the UI, we first need to add the AForge.net assemblies from NuGet. We need to do this before we start anything else because one of the components we'll use is a player control embedded within the assembly.

Head to your NuGet package manager GUI and search for "aforge." From the results you get back you need to install the following packages:

  • AForge (core library)
  • AForge.Math
  • AForge.Controls
  • AForge.Video
  • AForge.Imaging
  • AForge.Vision
  • AForge.Video.DirectShow

I found that if I selected "AForge.Controls," the rest of the dependent assemblies were selected and installed automatically.

Once you have the AForge assemblies installed, go to your Visual Studio toolbox, optionally right-click and add a new tab or select a tab you want to use (I created a new tab called 'AForge').  With the tab you want to use on screen, right-click it and select 'Choose Items' from the menu that pops up.

AForge2
Figure 2: Selecting 'Choose Items'

Under .NET components, browse to the package folder in your project and select the AForge.NET controls assembly to add them to your toolbox.

Once you click OK, the AForge controls should be added to your VS toolbox.

AForge3
Figure 3: The AForge controls have been added

Now that you have the tools added, drag a 'VideoSourcePlayer' to your form. To your form, also add a button and two label controls.

I set the background of my player to 'control dark' and set the form to be centred in the screen. I also named my controls to 'VideoSourcePlayer', 'btnSelectCamera', 'lblMotionDetected', and 'lblFps'.

What you set your names and properties to is entirely up to you. For the remainder of this post, I'll set mine to names that reflect their use so you can see in code what's used and where.

Once I'd added my various controls, my main UI looks like this:

AForge4
Figure 4: The main UI with the controls added

Once you have the main form created, you also need to make a second form to allow you to select the camera to use.

Rather than describe this in full, my device selection form looks like this:

AForge5
Figure 5: The completed device selection form

And the code for it, is as follows:

using System;
using System.Windows.Forms;
using AForge.Video.DirectShow;

namespace aforgenet_blogpost
{
   public partial class FrmCaptureDevice : Form
   {
      private readonly FilterInfoCollection
         _videoDevices;
      private string _selectedDevice;

      public string SelectedDevice
      {
         get
         {
            return _selectedDevice;
         }
      }

      public FrmCaptureDevice()
      {
         InitializeComponent();
         try
         {
            _videoDevices = new FilterInfoCollection
               (FilterCategory.VideoInputDevice);
            if (_videoDevices.Count == 0)
            {
               throw new ApplicationException();
            }
            foreach (FilterInfo videoDevice in
               _videoDevices)
            {
               devicesCombo.Items.Add(videoDevice.Name);
            }
         }
         catch (ApplicationException)
         {
            devicesCombo.Items.Add("No local
               capture devices");
            devicesCombo.Enabled = false;
            btnOk.Enabled = false;
         }
         devicesCombo.SelectedIndex = 0;
      }

      private void BtnOkClick(object sender,
         EventArgs e)
      {
         _selectedDevice = _videoDevices
            [devicesCombo.SelectedIndex].MonikerString;
      }

   }
}

The purpose of this form is to take the list of available devices from the system using AForge and add them to a drop-down list. The change on this drop-down list is then used to assign the "Moniker Name" of the selected device to a string that is then used within the main form to select the camera to use.

Once you've created the device selection form, close and save the files. Then, go back to your main UI, add a timer control to your main form; then, press F7 to go to your forms code.

Turning Our UI into a Motion Detector

The first thing we need to do to our main UI is to add two methods to open and close our video source, and to add some variables that we'll use as we add more code. The basic code your form needs to have is as follows:

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Vision.Motion;

namespace aforgenet_blogpost
{
   public partial class Form1 : Form
   {
      private readonly MotionDetector _detector =
         new MotionDetector(
            new TwoFramesDifferenceDetector(),
            new MotionAreaHighlighting());
      private int _statIndex;
      private int _statReady;
      private readonly int[] _statCount = new int[15];

      public Form1()
      {
         InitializeComponent();
      }

      private void OpenVideoSource(IVideoSource source)
      {
         Cursor = Cursors.WaitCursor;
         CloseVideoSource();
         videoSourcePlayer.VideoSource =
            new AsyncVideoSource(source);
         videoSourcePlayer.Start();
         _statReady = 0;
         _statIndex = 0;
         timer.Start();
         Cursor = Cursors.Default;
      }

      private void CloseVideoSource()
      {
         Cursor = Cursors.WaitCursor;
         videoSourcePlayer.SignalToStop();

         for (int i = 0; i < 50 &&
            videoSourcePlayer.IsRunning; i++)
         {
            Thread.Sleep(100);
         }

         if (videoSourcePlayer.IsRunning)
         {
            videoSourcePlayer.Stop();
         }

         timer.Stop();

         if (_detector != null)
         {
            _detector.Reset();
         }

         videoSourcePlayer.BorderColor = Color.Black;
         Cursor = Cursors.Default;

      }

      private void VideoSourcePlayerNewFrame(object sender,
         ref Bitmap image)
      {
         if (_detector == null) return;

         var motionValue = _detector.ProcessFrame(image);
         if(motionValue > 0)
         {
            lblMotionAlert.ForeColor = Color.Red;
            lblMotionAlert.Text = "Motion Detected";
         }
         else
         {
            lblMotionAlert.ForeColor = Color.Green;
            lblMotionAlert.Text = "No Motion Detected";
         }

      }

   }
}

In the events for the 'VideoSourcePlayer', make sure you have the 'NewFrame' event assigned to the 'VideoSourcePlayerNewFrame' event in the preceding code.

Double-click the button on your form and add the following code to its event handler:

private void BtnSelectCameraClick(object sender,
   EventArgs e)
{
   FrmCaptureDevice videoCaptureDeviceForm =
      new FrmCaptureDevice();
   if (videoCaptureDeviceForm.ShowDialog(this) ==
      DialogResult.OK)
   {
      OpenVideoSource(new VideoCaptureDevice
         (videoCaptureDeviceForm.SelectedDevice));
   }
}

'FrmCaptureDevice' is the name of the form created to list and choose the devices available in your system. This method should now allow you to choose and open your web cam, and if you run your application at this point, you should see your webcam spring to life on screen.

A small side note: If you run your code at this point and nothing appears to work, try going directly to the folder where your project is stored and running it by clicking on the executable file. I found while developing the code for this article that the AForge assemblies constantly misbehaved when running under Visual Studio in debugging mode, but when run without debugging (by pressing Ctrl+f5) and when run standalone everything worked fine. I don't know yet why this happens, but I will do some research and attempt to find out.

At this stage, you should have a working application that shows your webcam and highlights any movement it sees in red. If you don't, don't worry; we've still got a bit more to add.

In the 'FormClosing' event for your main form, add a call to the method to close your camera stream so that used resources are tidied up correctly when you exit the application.

private void Form1FormClosing(object sender,
   FormClosingEventArgs e)
{
   CloseVideoSource();
}

Make sure that you also add the following code to your timer tick handler.

private void TimerTick(object sender, EventArgs e)
{
   IVideoSource theVideoSource =
      videoSourcePlayer.VideoSource;
   if (theVideoSource == null) return;

   _statCount[_statIndex] =
      theVideoSource.FramesReceived;
   _statIndex = _statIndex + 1;

   if (_statIndex >= 15)
   {
      _statIndex = 0;
   }

   if (_statReady < 15)
   {
      _statReady = _statReady + 1;
   }

   float single = 0f;
   for (int i = 0; i < _statReady; i++)
   {
      single = single + _statCount[i];
   }

   single = single / _statReady;
   _statCount[_statIndex] = 0;

   lblFps.Text = string.Concat(single.ToString("F2"),
      " fps");
}

The timer tick handler is not required to make things work correctly; it's only there to record and calculate the frame rate you're getting. The work of notifying you of motion is performed in the "New Frame" event handler.

In our case, I've just elected to make a simple comparison. If it's 0, no motion is detected at all; if it's greater than 0, a value is presented indicating how much motion AForge detected.

The library has a number of different motion detection types, and these can be changed by changing the line that reads:

private readonly MotionDetector _detector =
   new MotionDetector(
      new TwoFramesDifferenceDetector(),
      new MotionAreaHighlighting());

The different values to use are all available in the AForge documentation available at:

http://www.aforgenet.com/framework/documentation.html

With everything in place, when you run the app, you should get something like the following:

AForge6
Figure 6: The application is running

The full code for the main form is as follows:

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Vision.Motion;

namespace aforgenet_blogpost
{
   public partial class Form1 : Form
   {
      private readonly MotionDetector _detector =
         new MotionDetector(new
            TwoFramesDifferenceDetector(),
         new MotionAreaHighlighting());
      private int _statIndex;
      private int _statReady;
      private readonly int[] _statCount = new int[15];

      public Form1()
      {
         InitializeComponent();
      }

      private void OpenVideoSource(IVideoSource source)
      {
         Cursor = Cursors.WaitCursor;
         CloseVideoSource();
         videoSourcePlayer.VideoSource =
            new AsyncVideoSource(source);
         videoSourcePlayer.Start();
         _statReady = 0;
         _statIndex = 0;
         timer.Start();
         Cursor = Cursors.Default;
      }

      private void CloseVideoSource()
      {
         Cursor = Cursors.WaitCursor;
         videoSourcePlayer.SignalToStop();

         for (int i = 0; i < 50 &&
            videoSourcePlayer.IsRunning; i++)
         {
            Thread.Sleep(100);
         }

         if (videoSourcePlayer.IsRunning)
         {
            videoSourcePlayer.Stop();
         }

         timer.Stop();

         if (_detector != null)
         {
            //lock (this)
            //{
               _detector.Reset();
            //}
         }

         videoSourcePlayer.BorderColor = Color.Black;
         Cursor = Cursors.Default;

      }

      private void BtnSelectCameraClick(object
         sender, EventArgs e)
      {
         FrmCaptureDevice videoCaptureDeviceForm =
            new FrmCaptureDevice();
         if (videoCaptureDeviceForm.ShowDialog(this) ==
            DialogResult.OK)
         {
            OpenVideoSource(new VideoCaptureDevice
               (videoCaptureDeviceForm.SelectedDevice));
         }
      }

      private void TimerTick(object sender, EventArgs e)
      {
         IVideoSource theVideoSource =
            videoSourcePlayer.VideoSource;
         if (theVideoSource == null) return;

         _statCount[_statIndex] =
            theVideoSource.FramesReceived;
         _statIndex = _statIndex + 1;

         if (_statIndex >= 15)
         {
            _statIndex = 0;
         }

         if (_statReady < 15)
         {
            _statReady = _statReady + 1;
         }

         float single = 0f;
         for (int i = 0; i < _statReady; i++)
         {
            single = single + _statCount[i];
         }

         single = single / _statReady;
         _statCount[_statIndex] = 0;

         lblFps.Text = string.Concat(single.ToString("F2"),
            " fps");

      }

      private void Form1FormClosing(object sender,
         FormClosingEventArgs e)
      {
         CloseVideoSource();

      }

      private void VideoSourcePlayerNewFrame(object
         sender, ref Bitmap image)
      {
         //lock (this)
         //{
            if (_detector == null) return;

            var motionValue = _detector.ProcessFrame(image);
            if(motionValue > 0)
            {
               lblMotionAlert.ForeColor = Color.Red;
               lblMotionAlert.Text = "Motion Detected";
            }
            else
            {
               lblMotionAlert.ForeColor = Color.Green;
               lblMotionAlert.Text = "No Motion Detected";
            }

         //}

      }

   }
}

Got a sticky .NET problem you're trying to solve, or spotted a strange lib you never knew existed? Reach out to me on Twitter as @shawty_ds and let me know; I might just feature it in a future post.



Related Articles

Comments

  • Appreciation

    Posted by shruti on 05/10/2016 11:34pm

    Helped me to get started ! Thanks

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date