1

I'm exploring Microsoft Cognitive Face API and I'm very new to it. I'm able to achieve Face Attributes with an image which is easy but, my question is how can I get Face Attributes of a person in a real time video feed from Kinect in WPF c#. It would be great if somebody can help me out. Thanks in Advance!

I have tried capturing the frame from the Kinect color feed at every 2 seconds to some file location and use that file path and converted it to a Stream and then pass it on to the Face-API Functions and that worked. Following is the code I tried.

namespace CognitiveFaceAPISample
{

    public partial class MainWindow : Window
    {
        private readonly IFaceServiceClient faceServiceClient = new FaceServiceClient("c2446f84b1eb486ca11e2f5d6e670878");
        KinectSensor ks;
        ColorFrameReader cfr;
        byte[] colorData;
        ColorImageFormat format;
        WriteableBitmap wbmp;
        BitmapSource bmpSource;
        int imageSerial;
        DispatcherTimer timer,timer2;
        string streamF = "Frames//frame.jpg";

        public MainWindow()
        {
            InitializeComponent();
            ks = KinectSensor.GetDefault();
            ks.Open();
            var fd = ks.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);
            uint frameSize = fd.BytesPerPixel * fd.LengthInPixels;
            colorData = new byte[frameSize];
            format = ColorImageFormat.Bgra;
            imageSerial = 0;

            cfr = ks.ColorFrameSource.OpenReader();
            cfr.FrameArrived += cfr_FrameArrived;
        }

        void cfr_FrameArrived(object sender, ColorFrameArrivedEventArgs e)
        {
            if (e.FrameReference == null) return;

            using (ColorFrame cf = e.FrameReference.AcquireFrame())
            {
                if (cf == null) return;
                cf.CopyConvertedFrameDataToArray(colorData, format);
                var fd = cf.FrameDescription;

                // Creating BitmapSource
                var bytesPerPixel = (PixelFormats.Bgr32.BitsPerPixel) / 8;
                var stride = bytesPerPixel * cf.FrameDescription.Width;

                bmpSource = BitmapSource.Create(fd.Width, fd.Height, 96.0, 96.0, PixelFormats.Bgr32, null, colorData, stride);

                // WritableBitmap to show on UI
                wbmp = new WriteableBitmap(bmpSource);
                FacePhoto.Source = wbmp;          

            }
        }

        private void SaveImage(BitmapSource image)
        {
            try
            {
                FileStream stream = new System.IO.FileStream(@"Frames\frame.jpg", System.IO.FileMode.OpenOrCreate);
                JpegBitmapEncoder encoder = new JpegBitmapEncoder();
                encoder.FlipHorizontal = true;
                encoder.FlipVertical = false;
                encoder.QualityLevel = 30;
                encoder.Frames.Add(BitmapFrame.Create(image));
                encoder.Save(stream);
                stream.Close();
            }
            catch (Exception)
            {

            }
        }       


        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
            timer.Tick += Timer_Tick;
            timer.Start();
            timer2 = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
            timer2.Tick += Timer2_Tick;
            timer2.Start();
        }
        private void Timer_Tick(object sender, EventArgs e)
        {
            SaveImage(bmpSource);
        }
        private async void Timer2_Tick(object sender, EventArgs e)
        {
            Title = "Detecting...";
            FaceRectangle[] faceRects = await UploadAndDetectFaces(streamF);
            Face[] faceAttributes = await UploadAndDetectFaceAttributes(streamF);
            Title = String.Format("Detection Finished. {0} face(s) detected", faceRects.Length);

            if (faceRects.Length > 0)
            {
                DrawingVisual visual = new DrawingVisual();
                DrawingContext drawingContext = visual.RenderOpen();
                drawingContext.DrawImage(bmpSource,
                    new Rect(0, 0, bmpSource.Width, bmpSource.Height));
                double dpi = bmpSource.DpiX;
                double resizeFactor = 96 / dpi;

                foreach (var faceRect in faceRects)
                {
                    drawingContext.DrawRectangle(
                        Brushes.Transparent,
                        new Pen(Brushes.Red, 2),
                        new Rect(
                            faceRect.Left * resizeFactor,
                            faceRect.Top * resizeFactor,
                            faceRect.Width * resizeFactor,
                            faceRect.Height * resizeFactor
                            )
                    );
                }

                drawingContext.Close();
                RenderTargetBitmap faceWithRectBitmap = new RenderTargetBitmap(
                    (int)(bmpSource.PixelWidth * resizeFactor),
                    (int)(bmpSource.PixelHeight * resizeFactor),
                    96,
                    96,
                    PixelFormats.Pbgra32);
                faceWithRectBitmap.Render(visual);
                FacePhoto.Source = faceWithRectBitmap;
            }

            if (faceAttributes.Length > 0)
            {
                foreach (var faceAttr in faceAttributes)
                {
                    Label lb = new Label();
                    //Canvas.SetLeft(lb, lb.Width);
                    lb.Content = faceAttr.FaceAttributes.Gender;// + " " + faceAttr.Gender + " " + faceAttr.FacialHair + " " + faceAttr.Glasses + " " + faceAttr.HeadPose + " " + faceAttr.Smile;
                    lb.FontSize = 50;
                    lb.Width = 200;
                    lb.Height = 100;
                    stack.Children.Add(lb);
                }
            }
        }

        private async Task<FaceRectangle[]> UploadAndDetectFaces(string imageFilePath)
        {
            try
            {
                using (Stream imageFileStream = File.OpenRead(imageFilePath))
                {
                    var faces = await faceServiceClient.DetectAsync(imageFilePath);
                    var faceRects = faces.Select(face => face.FaceRectangle);
                    var faceAttrib = faces.Select(face => face.FaceAttributes);
                    return faceRects.ToArray();

                }
            }
            catch (Exception)
            {
                return new FaceRectangle[0];
            }
        }

        private async Task<Face[]> UploadAndDetectFaceAttributes(string imageFilePath)
        {
            try
            {
                using (Stream imageFileStream = File.Open(imageFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    var faces = await faceServiceClient.DetectAsync(imageFileStream, true, true, new FaceAttributeType[] { FaceAttributeType.Gender, FaceAttributeType.Age, FaceAttributeType.Smile, FaceAttributeType.Glasses, FaceAttributeType.HeadPose, FaceAttributeType.FacialHair });

                    return faces.ToArray();

                }
            }
            catch (Exception)
            {
                return new Face[0];
            }
        }
}

Above code worked well. But, I want to convert each frame of Kinect Color Feed directly to Stream and I have no idea how to do it though I searched but nothing worked for me. If Somebody can help me then it'll be great. Thanks!

Aarti Dhiman
  • 75
  • 1
  • 9
  • Welcome to Stack Overflow! As it is, your question is rather broad. I suggest adding some code showing what you've tried so far. You can [edit] this into your question. Good luck! – S.L. Barth is on codidact.com Feb 10 '17 at 08:48
  • Hi S.L.Barth I have tried capturing the frame from the kinect color feed at every 2 seconds to some file location and use that file path to convert it into a stream and then pass it on to the Face-API Functions and that worked. Following is the code i tried. – Aarti Dhiman Feb 10 '17 at 13:08
  • Please use the "edit" function to edit it into your question. You can use this link: [edit] , or the "edit" link directly below your question. When showing code, you can use the "{}" button in the editor to format it as code. – S.L. Barth is on codidact.com Feb 10 '17 at 13:12
  • Oh, BTW - maybe I misread your comment. if you actually found out how to _solve_ it, then please post it as an answer. – S.L. Barth is on codidact.com Feb 10 '17 at 13:17
  • I edited my Question. Thanks for Helping :) – Aarti Dhiman Feb 10 '17 at 13:24
  • You're welcome! I've given you a vote to help you get started here. Do not hesitate to edit the question further, if you get new information - or to remove things that turn out to be irrelevant. Good luck, hope you'll get an answer! – S.L. Barth is on codidact.com Feb 10 '17 at 13:27
  • You already create a `BitmapSource`, so you could convert this to a stream. See: http://stackoverflow.com/questions/3751715/convert-system-windows-media-imaging-bitmapsource-to-system-drawing-image – Andrew Stephens Feb 10 '17 at 15:47
  • Hey! Andrew, Thank you! for helping. I tried converting it into `stream` but I'm ending up with no results. After putting debug point to my code I got some Exceptions which I have already shared. Can you help me in fixing up. I have also Shared my code below as well. – Aarti Dhiman Feb 16 '17 at 12:32

1 Answers1

1

Instead of persisting the frame to a file in SaveImage, you can persist it to a MemoryStream, rewind it (by calling Position = 0), and send that stream to DetectAsync().

Also note that in UploadAndDetectFaces, you should send imageFileStream, not imageFilePath, to DetectAsync(). You probably don't want to call both UploadAndDetectFaces and UploadAndDetectFaceAttributes anyway, since you're just doubling your work (and quota/rate-limit hit.)

cthrash
  • 2,938
  • 2
  • 11
  • 10
  • Hi, I tried using `MemoryStream` though it is not giving any error but it didn't work too. I found these problems: `Capacity: 'printStream.Capacity' threw an exception of type 'System.ObjectDisposedException'` `Length: 'printStream.Length' threw an exception of type 'System.ObjectDisposedException'` `Position: 'printStream.Position' threw an exception of type 'System.ObjectDisposedException'` – Aarti Dhiman Feb 16 '17 at 10:08
  • `private Stream StreamFromBitmapSource(BitmapSource writeBmp) { Stream bmp; using (bmp = new MemoryStream()) { BitmapEncoder enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(writeBmp)); enc.Save(bmp); bmp.Position = 0; } return bmp; }` That's the piece of code I tried. – Aarti Dhiman Feb 16 '17 at 10:09
  • The stream will be disposed at the end of your `using` statement. You need to keep it around until the service request finishes. The easiest thing to do is drop the using and call `stream.Dispose` later. – cthrash Feb 16 '17 at 16:45
  • I Changed my code to `private Stream StreamFromBitmapSource(BitmapSource writeBmp) { Stream bmp = new MemoryStream(); BitmapEncoder enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(writeBmp)); enc.Save(bmp); return bmp; }` – Aarti Dhiman Feb 16 '17 at 16:55
  • The above code is now giving two other exceptions: `ReadTimeout = 'imgStream.ReadTimeout' threw an exception of type 'System.InvalidOperationException' WriteTimeout = 'imgStream.WriteTimeout' threw an exception of type 'System.InvalidOperationException'` How do I Solve it. – Aarti Dhiman Feb 16 '17 at 16:57
  • Did you reset the position to 0? – cthrash Feb 16 '17 at 17:34
  • No I didn't, but what will happen when i reset the position? how it can help me? Also where should reset the position in the code. I don't know much about memory stream class. – Aarti Dhiman Feb 16 '17 at 20:12
  • The MemoryStream object conceptually is a buffer with a read+write pointer. At the point you call `enc.Save`, you've copied the output of enc to the buffer _and_ advanced the pointer. If you then attempt to read the stream, which is what the service call does, you're already at the end of the buffer and therefore have nothing to upload to the service. This is why you need to set the pointer back to the beginning by setting `Position=0`. – cthrash Feb 16 '17 at 22:26
  • Hi, I tried resetting position to 0 after `enc.Save` but it didn't help too. I'm still getting exceptions. – Aarti Dhiman Feb 17 '17 at 13:07