Click here to Skip to main content
Click here to Skip to main content
Go to top

Capture Image from Webcam using Silverlight

, 8 Nov 2012
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

I.explore.code
Web Developer
United Kingdom United Kingdom
Worked as an Analyst Programmer in a software firm using .NET 2.0, C++, SQL Server 2000
Java UML, Formal Methods, PHP/MySQL
Currently, working as an web developer using Silverlight 4.0 , C# 4.0, WCF services, ASP.NET 4.0, SQL Server 2008 R2, LINQ, Lambda Expressions. Develop and maintain several internal web products.
Love coding and everything coding related.
An optimisation fanatic...not the pre-mature variety but a perpetually thinking one..

Comments and Discussions

 
GeneralMy vote of 2 PinmemberLyle1231325-Aug-14 11:00 
GeneralRe: My vote of 2 PinmemberI.explore.code26-Aug-14 8:53 
GeneralRe: My vote of 2 PinmemberLyle1231326-Aug-14 9:02 
GeneralRe: My vote of 2 PinmemberI.explore.code26-Aug-14 10:03 
GeneralRe: My vote of 2 PinmemberLyle1231326-Aug-14 11:34 
Questionissues [modified] PinmemberLyle1231321-Aug-14 5:24 
QuestionCommercial ?? PinmemberMember 1035464327-Oct-13 4:24 
AnswerRe: Commercial ?? PinmemberAman.A11-Nov-13 22:07 
QuestionStuck at the Webcam permission PinmemberBa Love16-Oct-13 12:39 
AnswerRe: Stuck at the Webcam permission PinmemberAman.A11-Nov-13 22:09 
QuestionImage encoding process Pinmemberjavqui2227-Jul-13 2:10 
AnswerRe: Image encoding process PinmemberAman.A28-Jul-13 0:49 
GeneralRe: Image encoding process Pinmemberjavqui2228-Jul-13 3:16 
QuestionUnable to open solution in Windows 8 64bit system PinmemberShitiz Bansal16-May-13 18:43 
AnswerRe: Unable to open solution in Windows 8 64bit system PinmemberAman.A17-May-13 0:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 8 Nov 2012
Article Copyright 2012 by I.explore.code
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid