Skip to content
October 5, 2011 / igrali

Live face detection on Windows Phone

Mango brings you the possibility to use your application to get the live image from camera. This has brought many new options to people interested in augmented reality and for applying all sorts of filters straightforward. I personally find the use for face recognition very exciting and interesting. Unfortunately, you cannot use OpenCV library (actually, the wrapper for .NET called EmguCV). Fortunately there are people who write software on Codeplex. In this article I will use FaceLight, a project for face recognition in Silverlight, created by René Schulte, and create a Windows Phone application that can detect faces on camera.

Preparing the FaceLight for Windows Phone

The example he used demonstrates the use of FaceLight in Silverlight for the Web. You need to do small modifications to the source code in order to make it work on the WP7. First of all, go here and download the latest version of FaceLight source code. Once you did that, open the solution in Visual Studio 2010. Go to the References and remove the reference to the System.Windows.Browser. After you do that, Visual Studio will show that there’s an error in MainPage.xaml.cs, but feel free to ignore it for now. You should have the following situation in the solution Explorer:

image

Delete the App.xaml, MainPage.xaml and the whole FaceLight.Web project. Change the build to Release. You should now have only one project in the solution, called FaceLight. Add a class called FaceLight.cs to the solution. This class will serve you for initializing all the needed objects. This code was the part of MainPage.xaml.cs, but I have used it for creating the FaceLight object which you’ll use from your project. Now, the class should look like this:

 1: using System;
 2: using System.Windows.Media;
 3: using System.Windows.Media.Imaging;
 4:
 5: namespace FaceLight
 6: {
 7:     public class FaceLight
 8:     {
 9:
 10:         IFilter erodeFilter;
 11:         IFilter dilateFilter;
 12:         HistogramMinMaxSegmentator segmentator;
 13:         HistogramVisualizer histogramViz;
 14:
 15:         public ColorRangeFilter SkinColorFilter { get; set; }
 16:
 17:         public FaceLight()
 18:         {
 19:             try
 20:             {
 21:                 SkinColorFilter = new ColorRangeFilter
 22:                 {
 23:                     LowerThreshold = new YCbCrColor(0.10f, -0.15f, 0.05f),
 24:                     UpperThreshold = new YCbCrColor(1.00f, 0.05f, 0.20f)
 25:                 };
 26:                 erodeFilter = new Erode5x5Filter();
 27:                 dilateFilter = new Dilate5x5Filter();
 28:                 segmentator = new HistogramMinMaxSegmentator();
 29:                 histogramViz = new HistogramVisualizer { Scale = 20 };
 30:             }
 31:             catch (Exception ex)
 32:             {
 33:                 throw ex;
 34:             }
 35:         }
 36:
 37:         public WriteableBitmap Process(WriteableBitmap bmpToProcess)
 38:         {
 39:
 40:             if (bmpToProcess == null)
 41:                 return null;
 42:
 43:             var skin = SkinColorFilter.Process(bmpToProcess);
 44:             var erode = erodeFilter.Process(skin);
 45:             var dilate = dilateFilter.Process(erode);
 46:             dilate = dilateFilter.Process(dilate);
 47:             dilate = dilateFilter.Process(dilate);
 48:
 49:             var histogram = Histogram.FromWriteabelBitmap(dilate);
 50:             segmentator.Histogram = histogram;
 51:             segmentator.ThresholdLuminance = histogram.Max * 0.1f;
 52:             var foundSegments = segmentator.Process(dilate);
 53:
 54:             histogramViz.Histogram = histogram;
 55:             histogramViz.Visualize(dilate);
 56:
 57:             var result = new WriteableBitmap(bmpToProcess.PixelWidth, bmpToProcess.PixelHeight);
 58:             foreach (var foundSegment in foundSegments)
 59:             {
 60:                 var c = foundSegment.Center;
 61:                 result.DrawEllipseCentered(c.X, c.Y, foundSegment.Width >> 1, foundSegment.Height >> 1, Colors.Green);
 62:             }
 63:
 64:             return result;
 65:         }
 66:     }
 67: }

So, as I said, this is basically the original code from the FaceLight project. You will use it to create the FaceLight object, and then send the frame (WriteableBitmap) to the method Process which returns you another WriteableBitmap with the ellipse. This ellipse has the center in the recognized face. You overlay this WriteableBitmap with the video frames coming from the camera. That way you have the recognized face with a green ellipse around it. Have in mind that you can detect only one face with this project.

Build the solution. You should get the “Build succeeded” message and now the FaceLight.dll should be available at the Facelight/Bin/Release folder. Remember the location! :)

Using the Facelight in Windows Phone project for face recognition

Now, create a new Windows Phone project. Name it whatever you like, it’s not important. I named it FaceLive. Go to references, and add the reference to the FaceLight.dll you’ve created earlier. Add it to the MainPage.xaml.cs project, too. You will need the following namespaces, too:

using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using Microsoft.Devices;
using Microsoft.Phone.Controls;

You’ll also need the WriteableBitmapEx reference. You can install it via NuGet in the Package Manager Console:

PM> Install-Package WriteableBitmapEx

If it succeeded, we’re done with removing and adding references for this article. :) Now, the fun part!

First of all, create the UI for the application. It’s nothing dramatic, just the VideoBrush object, through which you get the video frames, overlayed with the Image object to draw the ellipse. Make the supported orientations Landscape only, and Orientated LandscapeLeft:

<phone:PhoneApplicationPage
    x:Class="LiveFace.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    ....
    SupportedOrientations="Landscape" Orientation="LandscapeLeft"
    ....>

Define the ContentPanel and delete the Application and Page titles:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Canvas x:Name="camera"
                    HorizontalAlignment="Stretch"
                    Tap="camera_Tap">
                <Canvas.Background>
                    <VideoBrush x:Name="cameraBrush" />
                </Canvas.Background>
            </Canvas>
            <Image x:Name="overlayRectangle"
                   HorizontalAlignment="Stretch"
                   Stretch="Uniform"/>
        </Grid>

As you probably noticed, I added the camera_Tap event handler to the tap event of the camera canvas. Now, in the MainPage.xaml.cs define the following:

 1: PhotoCamera cam;
 2: bool isFaceRecognizing = false;
 3: bool pushFrames = false;
 4:
 5: FaceLight.FaceLight faceLight;
 6:
 7: private static ManualResetEvent pauseFramesEvent = new ManualResetEvent(true);
 8:
 9: Thread frameThread;

Override the OnNavigatedTo and OnNavigatingFrom methods to make sure you initialize the PhotoCamera object which uses the primary camera and makes it the source for the cameraBrush, the VideoBrush object defined in xaml, and to dispose the cam object when it’s no longer needed:

 1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
 2:         {
 3:             cam = new PhotoCamera(CameraType.Primary);
 4:             cameraBrush.SetSource(cam);
 5:             base.OnNavigatedTo(e);
 6:         }
 7:
 8: protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
 9:         {
 10:             if (cam != null)
 11:             {
 12:                 cam.Dispose();
 13:             }
 14:             base.OnNavigatingFrom(e);
 15:         }

I mentioned the camera_Tap event handler. When you run the application, nothing happens. All you get are video frames from the camera. Unfortunately the emulator doesn’t do the job here, so you’ll need the real device, but it demonstrates what’s going on in the UI:

EMULATORvideo

The black rectangle is moving around, and on real device you get the picture from camera. Now, the already mentioned camera_Tap event handler actually starts the process of recognizing faces:

 1: private void camera_Tap(object sender, GestureEventArgs e)
 2:         {
 3:             if (!isFaceRecognizing)
 4:             {
 5:                 InitFaceRecognizer();
 6:                 isFaceRecognizing = !isFaceRecognizing;
 7:             }
 8:             else
 9:             {
 10:                 DestroyFaceRecognizer();
 11:                 isFaceRecognizing = !isFaceRecognizing;
 12:             }
 13:         }

If the process hasn’t started, it get’s initialized, and if it is, it gets destroyed. The methods for that are the following:

 1: private void InitFaceRecognizer()
 2:         {
 3:             faceLight = new FaceLight.FaceLight();
 4:             pushFrames = true;
 5:             frameThread = new Thread(PushFrame);
 6:             frameThread.Start();
 7:         }
 8:
 9: private void DestroyFaceRecognizer()
 10:         {
 11:             pushFrames = false;
 12:         }

Basically, in the initializer, the program starts processing video frames (actually, starts a new thread with the method PushFrame. So, by now you must have realized that the PushFrame method is the most important, and gets the job done. Let’s analyze part by part. While the pushFrames bool is true, we make two WriteableBitmaps, and create an array of integers from the video buffer:

 1: private void PushFrame()
 2:         {
 3:
 4:             while (pushFrames)
 5:             {
 6:                 PhotoCamera phCam = (PhotoCamera)cam;
 7:                 int[] ARGBPx = new int[(int)cam.PreviewResolution.Width * (int)cam.PreviewResolution.Height];
 8:                 WriteableBitmap wb;
 9:                 WriteableBitmap wbBmp;
 10:                 pauseFramesEvent.WaitOne();
 11:
 12:                 phCam.GetPreviewBufferArgb32(ARGBPx);
 13:                 pauseFramesEvent.Reset();

Since it’s running on another thread, we need to use the Dispatcher.BeginInvoke to make changes to the UI. We copy the integer array to the wbBmp WriteableBitmap, which then contains the image from the video buffer, and give it to the method Process from the FaceLight library. It return another WriteableBitmap (the one with the green ellipse), and we save it in the wb WriteableBitmap. Finally, we draw the ellipse by setting the overlayRectangle source property to wb. For every frame, we get signaled and use the same method.

 1: Deployment.Current.Dispatcher.BeginInvoke(delegate()
 2: {
 3:     wbBmp = new WriteableBitmap((int)cam.PreviewResolution.Width, (int)cam.PreviewResolution.Height);
 4:     ARGBPx.CopyTo(wbBmp.Pixels, 0);
 5:     wb = faceLight.Process(wbBmp);
 6:     wb.Invalidate();
 7:     overlayRectangle.Source = wb;
 8:     pauseFramesEvent.Set();
 9: });
 10: }
 11: }

When you test the project in daylight, it gets the job done almost always right. In the words of the author: “it runs in real time and works for most cases”. I hope you’ll find it useful in the Windows Phone world! :)

*EDIT: I have edited this article to make a clear distinction between “detect” and “recognize”. My article shows how to detect faces. Face recognizing would mean that the application has a database of faces which it uses for comparison to recognize who the person is. Thanks to Kristof Van De Voorde for leaving the comment!

17 Comments

Leave a Comment
  1. Kenstone / Oct 5 2011 14:39

    Thanks for posting this. I have an upcoming project that doesn’t use face recognition per se, but the code samples here should help.

    • igrali / Oct 5 2011 16:50

      Thank you for your comment! It’s always nice to hear some feedback!

      You are right – these code samples can be used with any project that uses some sort of image overlay and direct video frame manipulation. Using FaceLight as sort of a “filter” was just an example that demonstrated live frame manipulation! :)

  2. Martin / Oct 6 2011 22:22

    My project is saying PhotoCamera cam; is not a proper type. Do I need to use another using statement after Using FaceLight?

    • igrali / Oct 6 2011 22:53

      PhotoCamera is from Microsoft.Devices namespace. You need to add it after or before “using FaceLight;”

      using Microsoft.Devices;

      I’ll edit the post and add that detail, thanks!

      • Martin / Oct 6 2011 23:21

        Well I didn’t mention the other missing types, but while you can answer….

        private static ManualResetEvent
        Thread frameThread;
        WriteableBitmap wb;

        These are also missing types. Do you know what using statements are used for those? I know the Writeable Bitmap should be an added reference, but do I need to put it in as a Using statement also?

      • igrali / Oct 6 2011 23:32

        You will need the following “usings”

        using System.Threading;
        using System.Windows;
        using System.Windows.Input;
        using System.Windows.Media.Imaging;
        using Microsoft.Devices;
        using Microsoft.Phone.Controls;

        I’ll edit the article again :)

      • Martin / Oct 6 2011 23:42

        Thanks!

  3. Kristof Van De Voorde / Oct 19 2011 09:12

    I think a clear distinction should be made between face detection and face recognition. This article tackles face detection; face recognition however, can also match faces with a list of “known faces”.

    • igrali / Oct 19 2011 13:56

      I agree with you, I will edit the article to make that distinction… thanks for the comment!

  4. Lexi Marshal / Jan 9 2012 05:32

    Major thankies for the article post.Much thanks again. Fantastic.

  5. Diego Karcher / Jan 9 2012 13:08

    Im thankful for the blog. Will read on…

  6. Bailey Weathers / Jan 10 2012 19:37

    Looking forward to reading more. Great blog.

  7. Reg IGrali / Mar 6 2012 23:33

    Hi
    I’ve constructed the solution at specified, but don’t see the overlay after tapping.
    Is there something that i’m missing?

  8. Elisa Sunga / Mar 19 2012 14:48

    Is it possible to have your project detect hands instead of face? :)

    • Kristof Van De Voorde / Mar 19 2012 14:59

      Only if you can get your phone to work as a Surface… :-)

    • igrali / Mar 19 2012 20:12

      This project uses a library which is specifically for face detection. The whole library should be rewritten to detect hands, but it would require extensive knowledge in computer vision and similar highly technical and mathematical fields of science… :)

Trackbacks

  1. Live face recognition on Windows Phone

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.