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

Versatile WebCam C# library

, 14 Nov 2010
Rate this:
Please Sign up or sign in to vote.
Easy to use C# WebCam library that's not plagued with weird DLL references or PInvoke calls
This is an old version of the currently published article.

Index

Introduction

It was 2005 and time to start coding project for Imagine Cup when I first stumbled upon need to capture image from a webcam. Luckily, I was in a pretty good team and the teammate who took this task upon himself (Filip Popović) wrapped up Visual Studio project after a couple days of scouring the web and reading bunch of articles (including CodeProject ones of course Wink | ;) .

Since then, whenever I needed to capture image from webcam I would go back to that old code from 2005. And even though code served me well, I wasn’t pleased with it. The problem was in foundation of solution – Filip utilized PInvoke and used clipboard for copying image into Bitmap that could be manipulated within C#. Not that I blame him or anything – me being unable to improve his solution over the years was the bigger problem. Every search I tried ended up in finding something similar (this is the only solution I liked); and the more I read about the subject the more I realized that I miss certain knowledge (C++ and DirectShow) in order to develop something better.

That’s the first reason I’m writing this article – if you are good when it comes to C++ and DirectShow, please read on and see if you can help. Second reason is that I also wanted to make this article a merging point for all fellow C# developers with the same problem – I see that StackOverflow is overflowing with questions on this subject and most solutions out there are either really (really, really) badly written or way too conceptual.

So, don’t worry that this article will be simple call for help Wink | ;) . The solution I am sharing as a foundation is pretty well performing and robust, and I am sure that when you finish reading this article, you’ll finally have solid library on which you can depend upon when it comes to easily capturing images from a WebCam in C#.

Disclaimer

Current library I’m using is completely extracted from Touchless SDK (if you have couple seconds definitely check it out – Touchless is one of those cool projects that simply invite you to start playing with them). Meaning that I’m not claiming any authorship; all I did was taking out the parts that are not WebCam related in order to promote easier reuse. If this article does spark improvements, I will make sure that they are integrated back into Touchless SDK as a way of saying thanks to Michwass and his crew for terrific job they’ve done.

So, how to capture image from a WebCam?

Once you download source code that is attached to the article you should have following 3 projects:

  • Demo – simple Windows Forms project that demonstrates how WebCam is used. It references WebCamWrapper which in turn references WebCamLib.
  • WebCamLib – this is where the magic is happening – it is C++ project with just two files (WebCamLib.h and WebCamLib.cpp) that query WebCam using DirectShow and return results.
  • WebCamWrapper – C# wrapper on top of C++ project that enables easy integration into .NET world.

For starting point I recommend code view of Demo\MainForm.cs. This form implements most of the operations you can think off when it comes to WebCam access. First is the iteration through WebCams hooked up to the computer:

private void MainForm_Load(object sender, EventArgs e)
{
    if (!DesignMode)
    {
        comboBoxCameras.Items.Clear();
        foreach (Camera cam in CameraService.AvailableCameras)
            comboBoxCameras.Items.Add(cam);

        if (comboBoxCameras.Items.Count > 0)
            comboBoxCameras.SelectedIndex = 0;
    }
}

CameraService class you see in the code is contained in the WebCamWrapper project and is the main wrapper over main class CameraMethods that is the only class implemented in C++ WebCamLib project. CameraService exposes AvailableCameras as a list of Camera classes that contain logic for certain WebCam. Once user makes choice of camera, you’ll obviously want to start the capture:

private CameraFrameSource _frameSource;
private static Bitmap _latestFrame;

private void btnStart_Click(object sender, EventArgs e)
{
    if (_frameSource != null && _frameSource.Camera == comboBoxCameras.SelectedItem)
        return;

    thrashOldCamera();
    startCapturing();
}

_frameSource is the variable in which we’ll save currently selected Camera. Touchless developers decided not to tie their capture source exclusively to WebCam (good choice obviously) so they made generic IFrameSource interface that CameraFrameSource implements… and that’s how this class ended up as a container instead of Camera class directly. Rest of the code is pretty self-explanatory – if we selected same frame source, we’ll just exit; if not we will thrash old camera and start new one. Onto startCapturing method:

private void startCapturing()
{
    try
    {
        Camera c = (Camera)comboBoxCameras.SelectedItem;
        setFrameSource(new CameraFrameSource(c));
        _frameSource.Camera.CaptureWidth = 320;
        _frameSource.Camera.CaptureHeight = 240;
        _frameSource.Camera.Fps = 20;
        _frameSource.NewFrame += OnImageCaptured;

        pictureBoxDisplay.Paint += new PaintEventHandler(drawLatestImage);
        _frameSource.StartFrameCapture();
    }
    catch (Exception ex)
    {
        comboBoxCameras.Text = "Select A Camera";
        MessageBox.Show(ex.Message);
    }
}

private void setFrameSource(CameraFrameSource cameraFrameSource)
{
    if (_frameSource == cameraFrameSource)
        return;

    _frameSource = cameraFrameSource;
}

private void drawLatestImage(object sender, PaintEventArgs e)
{
    if (_latestFrame != null)
    {
        e.Graphics.DrawImage(_latestFrame, 0, 0, _latestFrame.Width, _latestFrame.Height);
    }
}

public void OnImageCaptured(Touchless.Vision.Contracts.IFrameSource frameSource, Touchless.Vision.Contracts.Frame frame, double fps)
{
    _latestFrame = frame.Image;
    pictureBoxDisplay.Invalidate();
}

We start off by fetching selected Camera from the ComboBox which we then use to create and set CameraFrameSource. Lines after that influence capture parameters (be sure to remember these three lines as we will be getting back to them later) and after that we have subscription to two events.

First event, NewFrame, is raised whenever WebCamLib captures image from the WebCam. As you can see, we save that image into local variable _latestFrame and from there you can do any additional image processing you like. Second event is just a fancy (and more efficient) way of saying pictureBoxDisplay.Image = frame.Image. For some reason setting Image property on PictureBox too often causes flicker and we obviously do not want that – instead we resort to invalidating PictureBox and then handling its paint event to draw current image from the WebCam.

Now that all that is implemented, we just StartFrameCapture and enjoy view from our WebCam. Try it out – press F5 and then click ‘Start’ button once Form loads up.

Rent is too damn high

When you grow tired of watching yourself, simply close the form. Once you are back in the Visual Studio check out thrashOldCamera method (that is utilized from Form_Closing and btnStop_Click methods also):

private void thrashOldCamera()
{
    if (_frameSource != null)
    {
        _frameSource.NewFrame -= OnImageCaptured;
        _frameSource.Camera.Dispose();
        setFrameSource(null);
        pictureBoxDisplay.Paint -= new PaintEventHandler(drawLatestImage);
    }
}

Well, nothing too fancy – we unsubscribe from two mentioned events, set _frameSource variable to null and call Dispose on Camera so that C++ WebCamLib can perform cleanup operations.

Believe it or not – that’s it. There is nothing more critical to explain or implement in order to use images from your WebCam in C#. Extra code that exists in MainForm.cs is just there for saving current image:

private void btnSave_Click(object sender, EventArgs e)
{
    if (_frameSource == null)
        return;

    Bitmap current = (Bitmap)_latestFrame.Clone();
    using (SaveFileDialog sfd = new SaveFileDialog())
    {
        sfd.Filter = "*.bmp|*.bmp";
        if (sfd.ShowDialog() == DialogResult.OK)
        {
            current.Save(sfd.FileName);
        }
    }

    current.Dispose();
}

And bringing up Configuration dialog:

private void btnConfig_Click(object sender, EventArgs e)
{
    // snap camera
    if (_frameSource != null)
        _frameSource.Camera.ShowPropertiesDialog();
}
Configuration Dialog

Problem(s)

As you can see from the code – implementation is pretty easy and clean (unlike some other approaches that use WIA, obscure DLLs, clipboard or hard disk for saving images, etc), meaning that there are not many problems. Actually, currently there is only 1 problem I can identify. You remember these three lines?

_frameSource.Camera.CaptureWidth = 320;
_frameSource.Camera.CaptureHeight = 240;
_frameSource.Camera.Fps = 20;

Well, it turns out that they are not working as advertised. First, let’s talk about FPS. If we dive into Camera class (line 254) here is what we will see (method that gets called after image is captured from the webcam):

private void ImageCaptured(Bitmap bitmap)
{
    DateTime dtCap = DateTime.Now;

    // Always save the bitmap
    lock (_bitmapLock)
    {
        _bitmap = bitmap;
    }

    // FPS affects the callbacks only
    if (_fpslimit != -1)
    {
        if (_dtLastCap != DateTime.MinValue)
        {
            double milliseconds = ((dtCap.Ticks - _dtLastCap.Ticks) / TimeSpan.TicksPerMillisecond) * 1.15;
            if (milliseconds + _timeBehind >= _timeBetweenFrames)
            {
                _timeBehind = (milliseconds - _timeBetweenFrames);
                if (_timeBehind < 0.0)
                {
                    _timeBehind = 0.0;
                }
            }
            else
            {
                _timeBehind = 0.0;
                return; // ignore the frame
            }
        }
    }

    if (OnImageCaptured != null)
    {
        var fps = (int)(1 / dtCap.Subtract(_dtLastCap).TotalSeconds);
        OnImageCaptured.Invoke(this, new CameraEventArgs(bitmap, fps));
    }

    _dtLastCap = dtCap;
}

Even if you just glanced at the method you probably saw that most of it is dedicated to calculating time between frames and ditching the frame if it came too soon. Which is not too bad, I guess – controlling framerate on C# level, rather than on hardware level will probably not kill you.

But what about finding out that other two lines, which influence the size of captured image, also do not work (line 235 in Camera.cs)?

private void CaptureCallbackProc(int dataSize, byte[] data)
{
    // Do the magic to create a bitmap
    int stride = _width * 3;
    GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
    var scan0 = (int)handle.AddrOfPinnedObject();
    scan0 += (_height - 1) * stride;
    var b = new Bitmap(_width, _height, -stride, PixelFormat.Format24bppRgb, (IntPtr)scan0);
    b.RotateFlip(_rotateFlip);
    // Copy the image using the Thumbnail function to also resize if needed
    var copyBitmap = (Bitmap)b.GetThumbnailImage(_width, _height, null, IntPtr.Zero);
    //var copyBitmap = (Bitmap)b.Clone();

    // Now you can free the handle
    handle.Free();

    ImageCaptured(copyBitmap);
}

As you can see, image size is actually faked. Majority of cameras I’ve tested out will tend to return images in 640x480 size. Which is fine in most cases – if you need smaller image, that b.GetThumbnailImage will allow you to easily resize it. However, if you wish higher resolution image, you are stuck and that’s not a good thing.

So, anyone from C++ world is more than welcome to help with this. Following links I’ve read gave me impression that all that’s needed to be done is somehow invoke Video Format window in C++, the same way we are now invoking Video Source Configuration window (for setting Brightness, Contracts, etc):

  • Setting up Webcam properties with DirectShow forum post
  • EasyWebCam project – for most part it is bad, but it does have Video Format window. I decompiled WebCam_Capture.dll from the project only to find out that everything is implemented using PInvoke - meaning that it’s useless for our approach. So, if somebody can bring up that same window using C++ and DirectShow – please help out by extending existing CameraMethods class.

    Video Format Dialog

Conclusion

I hope that you leave this article with that warm and fuzzy feeling of happiness knowing that you can now easily communicate with your WebCam even though you are just a simple C# developer.

In case you have anything to add, be my guest and go wild - I’ll be watching the comments section. Any calls for help (even this simple approach is not simple enough for you), suggestions (you did same thing using different approach), criticism (you did same thing using different approach and you don’t like this one) or offers for help (did I already mentioned that I suck in C++ and DirectShow?)… are all welcome.

History

  • November 7th, 2010 - Initial version of the article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Predrag Tomasevic
Chief Technology Officer Atama Group
United States United States
http://www.linkedin.com/in/ptomasevic
 
Bitcoin wallet: 1ARXmh58S6aRiZPCbneDCzrB1HUU6PLmDQ

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
QuestionSet Resolution? Pinmembersitichai0184-Sep-14 0:53 
GeneralMy vote of 5 Pinmemberyumbelie23-May-14 9:05 
GeneralRe: My vote of 5 PinmemberPredrag Tomasevic5-Jun-14 13:07 
GeneralRe: My vote of 5 Pinmemberyumbelie5-Jun-14 13:27 
GeneralRe: My vote of 5 PinmemberPredrag Tomasevic7-Jun-14 17:50 
QuestionVs2013 - unable to compile PinmemberThornik13-Apr-14 4:16 
QuestionVisual studio 2008 running Versatile Webcam C# PinmemberMember 1070441927-Mar-14 6:44 
AnswerRe: Visual studio 2008 running Versatile Webcam C# PinmemberPredrag Tomasevic5-Jun-14 13:10 
Questionmultiple pictureBox to view mor than one cam PinmemberMember 1031676328-Oct-13 20:01 
AnswerRe: multiple pictureBox to view mor than one cam [modified] Pinmemberfree5lot29-Dec-13 20:56 
QuestionpictureBox crops the webcam preview Even in stretch mode PinmemberMember 1031676328-Oct-13 18:35 
AnswerRe: pictureBox crops the webcam preview Even in stretch mode PinmemberMember 1031676328-Oct-13 18:50 
GeneralMy vote of 5 PinprofessionalShambhoo kumar16-Sep-13 18:16 
GeneralMy vote of 5 PinmemberAntonio Petricca15-Sep-13 20:19 
Questioncan i record video of webcam using asp.net PinmemberMahesh_Bhosale31-Aug-13 22:02 
QuestionDoes what it says on the box ... PinmemberJerry Evans25-Aug-13 4:41 
GeneralProblems with my WIA code in Windows 7 PinmemberJPerryQuedgeley14-Aug-13 4:39 
QuestionVery nice, Thank you! PinmemberShawn-USA5-Aug-13 15:26 
GeneralBravo! This appears to work reliably with the Homannn Designs machining camera for Mach3. PinmemberMember 355731128-Jul-13 6:44 
QuestionCapturing webcam in Web application PinmemberMember 953140522-Jul-13 17:25 
QuestionPlease Help Me PinmemberMember 1003980519-Jun-13 4:35 
GeneralMy vote of 5 PinprofessionalPrasad Khandekar12-Jun-13 3:49 
QuestionA nice effort Pinmemberkrayknot16-May-13 2:53 
AnswerRe: A nice effort Pinmemberwolfysan25-Jul-13 22:48 
GeneralThx Pinmemberjavad_243-May-13 23:11 

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.140916.1 | Last Updated 14 Nov 2010
Article Copyright 2010 by Predrag Tomasevic
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid