Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Capture Image from Webcam using Silverlight

0.00/5 (No votes)
8 Nov 2012 1  
Capture a frame from live webcam feed and save it as an image file to disk

Introduction 

Silverlight brings a whole new set of controls and libraries for developing rich internet applications that use media content like video and audio. In this article I explore Silverlight's capability for webcam access and the subsequent use of the webcam feed to freeze a frame and save it as a PNG file onto disk.

The code presented here can be wrapped into a nice user control and used in any Silverlight application to capture images, e.g., quick profile pictures, pictures for fun, etc., use your imagination Smile, although I don't discuss that part in this article.

Background 

This article explains the basics of Silverlight webcam access: Silverlight web cam access.

The Source Code 

I have uploaded the Silverlight project that can be added into Visual Studio straight away.

The Front End 

The XAML markup for the front end looks like this:

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="800" d:DesignWidth="900">

    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Activate Camera" Height="23" HorizontalAlignment="Left" 
            Margin="156,20,0,0" Name="btnActivate" 
            VerticalAlignment="Top" Width="100" Click="btnActivate_Click" />
        <Rectangle Height="239" HorizontalAlignment="Left" 
            Margin="12,49,0,0" Name="rectangle1" Stroke="Black" 
            StrokeThickness="1" VerticalAlignment="Top" Width="376" />
        <Rectangle Height="239" HorizontalAlignment="Left" Margin="409,49,0,0" 
            Name="rectangle2" Stroke="Black" StrokeThickness="1" 
            VerticalAlignment="Top" Width="376" />
        <Button Content="Freeze Frame" Height="23" HorizontalAlignment="Left" 
            Margin="559,20,0,0" Name="btnFreeze" VerticalAlignment="Top" 
            Width="100" Click="btnFreeze_Click" />
        <Image Height="183" HorizontalAlignment="Left" Margin="409,305,0,0" 
            Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="376" />
        <Button Name="btnSave" Content="Save Shot" Click="btnSave_Click" 
            Height="23" HorizontalAlignment="Left" Width="85" Margin="500,200,0,0"></Button>
    </Grid>
</UserControl>

This is what the GUI looks like, rather simple. On the left is the web cam feed live, on the right hand side is a frame captured. (My hand moved slightly after capturing the frame and taking the screen grab, so my hand appears lower on the left than on the right).

Clicking on "Activate Camera" starts the live video feed preview in the left box, clicking on "Freeze Frame" does just that, freezes a frame from the live video and shows in the right box. Clicking "Save Shot" saves the frozen frame as a PNG image on to disk. That's all.

The C# code behind 

The C# code that does the capture and save is discussed in the following phases:

  1. Getting access to webcam
  2. As you can see below, the first step is to get the video capture device and stream its feed into a pre-viewing Rectangle i.e., rectangle1. This is done by painting or "Fill"ing rectangle1 by a VideoBrush whose content source has been set to the video capture device. 

    Keep in mind also that rectangle2 has been filled with an image brush the reason for which will be discussed shortly.

    Once that's done, the RequestDeviceAccess() method asks for user permission to access the webcam, which if gets approved starts the webcam and live-feed appears in rectangle1.

    private void btnActivate_Click(object sender, RoutedEventArgs e)
    {
        source = new CaptureSource();
        VideoCaptureDevice vcd = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
        source.VideoCaptureDevice = vcd;
        VideoBrush vb = new VideoBrush();
        ib = new ImageBrush();
        vb.SetSource(source);
        rectangle1.Fill = vb;
        rectangle2.Fill = ib;
    
        if (CaptureDeviceConfiguration.RequestDeviceAccess())
        {
            source.Start();
        }
    }
  3. Freezing a frame
  4. Attach the CaptureImageCompleted event handler to the click event of the "Freeze Frame" button like so: 

    private void btnFreeze_Click(object sender, RoutedEventArgs e)
    {
        source.CaptureImageCompleted += new EventHandler<CaptureImageCompletedEventArgs>(source_CaptureImageCompleted);
        source.CaptureImageAsync();
    }

    and call the CaptureImageAsync() method which is an asynchronous method which fires and continues with the rest of the code without having to block and wait for a response. When the method returns, the event handler gets fired and the result is collected like so: 

    void source_CaptureImageCompleted(object sender, CaptureImageCompletedEventArgs e)
    {
        ib.ImageSource = e.Result;
        CapturedImage = e.Result;
    }

    If you remember, moments ago we had filled rectangle2 with an image brush "ib" and at that point it was empty i.e., it had no source to paint an image from. Well, now it does. As can be seen above, I am setting the ImageSource of ImageBrush "ib" to be the result of the CaptureImageCompleted event. This results in a "frame" being frozen from the live video feed and appearing in rectangle2 as shown in the GUI screenshot previously. 

    Also notice, I am setting another property, CapturedImage with the same result. What about it? Well, CapturedImage is defined as follows:  

    private WriteableBitmap _capimage;
    public WriteableBitmap CapturedImage
    {
        get 
        {              
            return _capimage; 
        }
        set { _capimage = value; }
    }

    It is a WriteableBitmap object that will help in saving the frame to an actual image which is what I talk about in the penultimate phase. 

  5. Saving the frame to PNG
  6. Now, this is where you are going to need some third party open source library support. ImageTools is just the library for this job. It provides Encoders/Decoders for a couple of image formats, PNG being one of them.

    You will need to reference the following DLLs in your Silverlight project:

    1. ImageTools.dll
    2. ImageTools.IO.PNG.dll
    3. ImageTools.Utils.dll

    Include the following namespaces as well if not already there:

    using System.Windows.Media;
    using System.Windows.Media.Effects;
    using System.Windows.Media.Animation;
    using System.Windows.Media.Imaging;

    ImageTools also provides an extension method: ToImage(WriteableBitmap wmp) which converts a WriteableBitmap object to an ExtendedImage object (part of the ImageTools library). 

    Save this frame to PNG on the click of "Save Shot" button like so:

    private void btnSave_Click(object sender, RoutedEventArgs e)
    {
        SaveFileDialog sfd = new SaveFileDialog();
        sfd.Filter = "PNG files (*.PNG)|*.png|All Files (*.*)|*.*";
        var enc = new PngEncoder();
        if ((bool)sfd.ShowDialog())
        {
            Stream stream = sfd.OpenFile();
            var image = Imager.ToImg(CapturedImage);
            enc.Encode(image, stream);
            stream.Close();
        }
    }

    [N.B.] : I, for some unknown reason, could not access the ToImage() extension method directly so I de-compiled the ImageTools DLL using IL-Spy and copied the ToImage() method to a separate class in my Silverlight project and named it ToImg(). I just used the original method "as-is" and did not modify it in anyway, its original authors are the guys who built the ImageTools library not me. Just so you know Smile.

    Anyway, getting back, as you can see above, I am opening a SaveFileDialog to create an empty PNG file, open it into a Stream, convert the CapturedImage i.e.n WriteableBitmap to an ExtendedImage typen and then call the Encode() method defined in the PngEncoder class in ImageTools.IO.PNG.dll to encode the image into PNG format and write back to the stream. Since the stream is directly connected with the actual file, the encoded data gets written to the file as soon as the stream is closed and voila! You have successfully captured an image from your webcam in a Silverlight web app into a PNG file.

Points of Interest   

I would be honest, I did try some weird hacks to write my own PngEncoder (this was way before I had even heard of ImageTools and the "build-my-own" desire was at toxic levels Smile) but I couldn't really make much headway into it. Finally, I just decided to bite the bullet and go for the third party library way. But studying this library, one can begin to understand how various image formats work and how to approach writing an encoder for them. 

The next steps for this code could be to wrap it into a nice Silverlight user control that can be plugged into any application and it will work right out of the box (though that remains to be seen). 

If it gets too complex for a control then it can even remain as a standalone application that can then be embedded into another Silverlight application and loaded dynamically from the host app. I discuss how to do that in my blog here.

As to the use case of such a control, I am sure people can use their imaginations and find ways to use it. I have already mentioned a couple off the top of my head in the beginning of this article but they are by no means all.

Thanks for considering this article worth your reading.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here