/// <summary>
        /// Worker thread for recognition processing
        /// </summary>
        private void RecognizerWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var result = new RecognitionResult();

            result.OriginalBitmap  = this.ImageToBitmap(this.colorImageBuffer, this.imageWidth, this.imageHeight);
            result.ProcessedBitmap = (Bitmap)result.OriginalBitmap.Clone();
            e.Result = result;

            if (this.trackedSkeleton != null && this.trackedSkeleton.TrackingState == SkeletonTrackingState.Tracked)
            {
                // Reset the face tracker if we lost our old skeleton...
                if (this.trackedSkeleton.TrackingId != this.previousTrackedSkeletonId && this.faceTracker != null)
                {
                    this.faceTracker.ResetTracking();
                }

                this.previousTrackedSkeletonId = this.trackedSkeleton.TrackingId;

                if (this.faceTracker == null)
                {
                    try
                    {
                        this.faceTracker = new FaceTracker(this.Kinect);
                    }
                    catch (InvalidOperationException)
                    {
                        // During some shutdown scenarios the FaceTracker
                        // is unable to be instantiated.  Catch that exception
                        // and don't track a face.
                        this.faceTracker = null;
                    }
                }

                if (this.faceTracker != null)
                {
                    var faceTrackFrame = this.faceTracker.Track(
                        this.colorImageFormat,
                        this.colorImageBuffer,
                        this.depthImageFormat,
                        this.depthImageBuffer,
                        this.trackedSkeleton);

                    if (faceTrackFrame.TrackSuccessful)
                    {
                        var trackingResults = new TrackingResults(faceTrackFrame.GetProjected3DShape());

                        lock (this.ProcessingMutex)
                        {
                            if (this.Processor != null && this.ProcessingEnabled)
                            {
                                this.Processor.Process(result, trackingResults);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Attempt to find a trained face in the original bitmap
        /// </summary>
        public void Process(RecognitionResult result, TrackingResults trackingResults)
        {
            GraphicsPath origPath;

            using (var g = Graphics.FromImage(result.ProcessedBitmap))
            {
                // Create a path tracing the face and draw on the processed image
                origPath = new GraphicsPath();

                foreach (var point in trackingResults.FacePoints)
                {
                    origPath.AddLine(point, point);
                }

                origPath.CloseFigure();
                g.DrawPath(new Pen(Color.Red, 2), origPath);
            }

            var minX = (int)origPath.PathPoints.Min(x => x.X);
            var maxX = (int)origPath.PathPoints.Max(x => x.X);
            var minY = (int)origPath.PathPoints.Min(x => x.Y);
            var maxY = (int)origPath.PathPoints.Max(x => x.Y);
            var width = maxX - minX;
            var height = maxY - minY;

            // Create a cropped path tracing the face...
            var croppedPath = new GraphicsPath();

            foreach (var point in trackingResults.FacePoints)
            {
                var croppedPoint = new System.Drawing.Point(point.X - minX, point.Y - minY);
                croppedPath.AddLine(croppedPoint, croppedPoint);
            }

            croppedPath.CloseFigure();

            // ...and create a cropped image to use for facial recognition
            using (var croppedBmp = new Bitmap(width, height))
            {
                using (var croppedG = Graphics.FromImage(croppedBmp))
                {
                    croppedG.FillRectangle(Brushes.Gray, 0, 0, width, height);
                    croppedG.SetClip(croppedPath);
                    croppedG.DrawImage(result.OriginalBitmap, minX * -1, minY * -1);
                }

                using (var grayBmp = croppedBmp.MakeGrayscale(100, 100))
                {
                    grayBmp.HistogramEqualize();

                    string key = null;
                    float eigenDistance = -1;

                    if (this.Recognizer != null)
                        key = this.Recognizer.Recognize(grayBmp, out eigenDistance);

                    // Save detection info
                    result.Faces = new List<RecognitionResult.Face>()
                    {
                        new RecognitionResult.Face()
                        {
                            TrackingResults = trackingResults,
                            EigenDistance = eigenDistance,
                            GrayFace = (Bitmap)grayBmp.Clone(),
                            Key = key
                        }
                    };
                }
            }
        }
        /// <summary>
        /// Attempt to find a trained face in the original bitmap
        /// </summary>
        public void Process(RecognitionResult result, TrackingResults trackingResults)
        {
            GraphicsPath origPath;

            using (var g = Graphics.FromImage(result.ProcessedBitmap))
            {
                // Create a path tracing the face and draw on the processed image
                origPath = new GraphicsPath();

                foreach (var point in trackingResults.FacePoints)
                {
                    origPath.AddLine(point, point);
                }

                origPath.CloseFigure();
                g.DrawPath(new Pen(Color.Red, 2), origPath);
            }

            var minX   = (int)origPath.PathPoints.Min(x => x.X);
            var maxX   = (int)origPath.PathPoints.Max(x => x.X);
            var minY   = (int)origPath.PathPoints.Min(x => x.Y);
            var maxY   = (int)origPath.PathPoints.Max(x => x.Y);
            var width  = maxX - minX;
            var height = maxY - minY;

            // Create a cropped path tracing the face...
            var croppedPath = new GraphicsPath();

            foreach (var point in trackingResults.FacePoints)
            {
                var croppedPoint = new System.Drawing.Point(point.X - minX, point.Y - minY);
                croppedPath.AddLine(croppedPoint, croppedPoint);
            }

            croppedPath.CloseFigure();

            // ...and create a cropped image to use for facial recognition
            using (var croppedBmp = new Bitmap(width, height))
            {
                using (var croppedG = Graphics.FromImage(croppedBmp))
                {
                    croppedG.FillRectangle(Brushes.Gray, 0, 0, width, height);
                    croppedG.SetClip(croppedPath);
                    croppedG.DrawImage(result.OriginalBitmap, minX * -1, minY * -1);
                }

                using (var grayBmp = croppedBmp.MakeGrayscale(100, 100))
                {
                    grayBmp.HistogramEqualize();

                    string key           = null;
                    float  eigenDistance = -1;

                    if (this.Recognizer != null)
                    {
                        key = this.Recognizer.Recognize(grayBmp, out eigenDistance);
                    }

                    // Save detection info
                    result.Faces = new List <RecognitionResult.Face>()
                    {
                        new RecognitionResult.Face()
                        {
                            TrackingResults = trackingResults,
                            EigenDistance   = eigenDistance,
                            GrayFace        = (Bitmap)grayBmp.Clone(),
                            Key             = key
                        }
                    };
                }
            }
        }
        /// <summary>
        /// Worker thread for recognition processing
        /// </summary>
        private void RecognizerWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var result = new RecognitionResult();
            result.OriginalBitmap = this.ImageToBitmap(this.colorImageBuffer, this.imageWidth, this.imageHeight);
            result.ProcessedBitmap = (Bitmap)result.OriginalBitmap.Clone();
            e.Result = result;

            if (this.trackedSkeleton != null && this.trackedSkeleton.TrackingState == SkeletonTrackingState.Tracked)
            {
                // Reset the face tracker if we lost our old skeleton...
                if (this.trackedSkeleton.TrackingId != this.previousTrackedSkeletonId && this.faceTracker != null)
                    this.faceTracker.ResetTracking();

                this.previousTrackedSkeletonId = this.trackedSkeleton.TrackingId;

                if (this.faceTracker == null)
                {
                    try
                    {
                        this.faceTracker = new FaceTracker(this.Kinect);
                    }
                    catch (InvalidOperationException)
                    {
                        // During some shutdown scenarios the FaceTracker
                        // is unable to be instantiated.  Catch that exception
                        // and don't track a face.
                        this.faceTracker = null;
                    }
                }

                if (this.faceTracker != null)
                {
                    var faceTrackFrame = this.faceTracker.Track(
                        this.colorImageFormat,
                        this.colorImageBuffer,
                        this.depthImageFormat,
                        this.depthImageBuffer,
                        this.trackedSkeleton);

                    if (faceTrackFrame.TrackSuccessful)
                    {
                        var trackingResults = new TrackingResults(faceTrackFrame.GetProjected3DShape());

                        lock (this.ProcessingMutex)
                        {
                            if (this.Processor != null && this.ProcessingEnabled)
                            {
                                this.Processor.Process(result, trackingResults);
                            }
                        }
                    }
                }
            }
        }