Click here to Skip to main content
Click here to Skip to main content

Versatile WebCam C# library

By , 29 Jan 2013
 

Index

Introduction

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

Since then, whenever I needed to capture an image from a webcam I would go back to that old code from 2005. And even though the code served me well, I wasn’t pleased with it. The problem was in the foundation of the solution – Filip utilized PInvoke and used the clipboard for copying an image into a 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 missed 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. The 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 ;). 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 a solid library on which you can depend on when it comes to easily capturing images from a WebCam in C#.

Disclaimer

The 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 take 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 the Touchless SDK as a way of saying thanks to Michwass and his crew for the terrific job they’ve done.

So, how do we capture an image from a WebCam?

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

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

For a starting point I recommend a code view of Demo\MainForm.cs. This form implements most of the operations you can think of when it comes to WebCam access. First is the iteration through the 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;
    }
}

The CameraService class you see in the code is contained in the WebCamWrapper project and is the main wrapper over the main class CameraMethods that is the only class implemented in the C++ WebCamLib project. CameraService exposes AvailableCameras as a list of Camera classes that contain the logic for a certain WebCam. Once the user makes a 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 the currently selected Camera. Touchless developers decided not to tie their capture source exclusively to WebCam (good choice obviously) so they made a generic IFrameSource interface that CameraFrameSource implements… and that’s how this class ended up as a container instead of the Camera class directly. The rest of the code is pretty self-explanatory – if we select the same frame source, we’ll just exit; if not we will thrash the old camera and start a new one. Onto the 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 the selected Camera from the ComboBox which we then use to create and set the CameraFrameSource. Lines after that influence the capture parameters (be sure to remember these three lines as we will be getting back to them later) and after that we have a subscription to two events.

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

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

Rent is too damn high

When you grow tired of watching yourself, simply close the form. Once you are back in Visual Studio, check out the thrashOldCamera method (that is utilized from the 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 the two mentioned events, set the _frameSource variable to null, and call Dispose on Camera so that the 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#. The extra code that exists in MainForm.cs is just there for saving the 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 the 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 – the 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 one 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 the Camera class (line 254) here is what we will see (the method that gets called after an 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 the time between frames and ditching the frame if it came too soon. Which is not too bad, I guess – controlling the frame rate on C# level rather than on hardware level will probably not kill you.

But what about finding out the other two lines, which influence the size of the captured image, also not working (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, the image size is actually faked. Majority of cameras I’ve tested out will tend to return images in the 640x480 size. Which is fine in most cases – if you need a smaller image, b.GetThumbnailImage will allow you to easily resize it. However, if you wish a higher resolution image, you are stuck and that’s not a good thing.

So, anyone from the C++ world is more than welcome to help with this. The following links I’ve read gave me the impression that all that’s needed to be done is somehow invoke the Video Format window in C++, the same way we are now invoking the 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 a 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 the 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 the same thing using a different approach), criticism (you did the same thing using a different approach and you don’t like this one), or offers for help… are all welcome.

History

  • January 1st, 2013 - Added a link to the source modified by Jake Dreww[^]. Still searching for a working solution when it comes to resolution settings. Some leads:
  • 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)

About the Author

Predrag Tomasevic
Software Developer Atama Group
United States United States
Member
http://www.linkedin.com/in/ptomasevic

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionA nice effortmemberkrayknot16 May '13 - 2:53 
To be very honest this is a good effort. I am using the .cpp files from last some months and now the requirement is to render multiple webcams simultaneously. This utility provides the list of the webcams that we can select one by one but after some changes when I am trying to accessing multiple cams, it behaves erratically, it takes both the Webcam's output and shows the frames one by one into the same picturebox.
 
As per my investigation, the _latestframe captures webcam image frame, I replicate this but the problem is coming at
 
_latestframe = Frame.Image;
 

Any help would be appreciated.
GeneralThxmemberjavad_243 May '13 - 23:11 
U know that u are a life saver and a guru? Wink | ;) Rose | [Rose]
QuestionHow to use it on WebFormsmemberedwarmartinez2 Apr '13 - 12:24 
Hi:
 
This project is a good one, thank you. I am wondering if someone knows how to apply the same functionality on webforms. I am trying but I cant get the images because webforms does not have the picturebox control, and using the "image" control does not work. Any idea? Thanks in advance.
GeneralMy vote of 5memberKeith Rule1 Mar '13 - 11:23 
Very easy to use and it works well. Thanks!
QuestionSharpDxmemberneonutts12 Feb '13 - 13:47 
you might want to look at this open source project. I know you are trying a small scale integration but this is where I would base my app on since you would be building on top of an already built managed wrapper to direct x
 
http://code.google.com/p/sharpdx/downloads/detail?name=SharpDX-Full-2.4.2.exe&can=2&q=
QuestionWebCamLib.dll is missing?memberPriya Bagus10 Feb '13 - 15:24 
Where can I found the WebCamLib.dll? Somehow the reference isn't resolved and I couldn't find the dll in the source zip package. Thanks
GeneralAwesome!!!memberCJLopez6 Feb '13 - 7:39 
I was researching for a open source code to build a simple camera surveillance using several usb cams and this one seems the cleaner and easier to implement
QuestionPlease helpmemberConcordPilot4 Feb '13 - 20:31 
I tried everything but i cant run this source code propertly.
Everytime i try i got error msg ... webcamlib.vcproj cannot be
oppened because vcproj doesnt supported ...
 
I see this lib is written in c++ but what should i do to make it
works.
 
thanks
AnswerRe: Please helpmemberseanmir22 Apr '13 - 19:15 
I have the same problem ! Visual Studio 2010 !
Questionc# webcam programmemberlelouch4528 Jan '13 - 23:02 
i have an error sir near using webcamlib; it said could not be found sir plz teach me im new here
GeneralMy vote of 5memberDr Bob7 Jan '13 - 9:45 
Very interesting and useful! Thanks
Questiontaking a snapshot in c# using webcam .memberMember 97231607 Jan '13 - 4:24 
I'm using c# visual studio 2010 to access my webcam. The code I'm developing is pretty simple: when a button is pressed a webcam picture is taken, and then displayed in a picture box.
AnswerRe: taking a snapshot in c# using webcam .memberYvar Birx30 Jan '13 - 8:43 
Why do you not save the PictureBox.Handle at the moment you want the picture to be taken?
SuggestionGreat job with missing key featurememberPonschyDD3 Jan '13 - 13:38 
You offer a great solution here, thanks for it! Like some other folks I tried to fix the camera resolution and stumbled over this (amvideo.h:454):
 
// Analog video variant - Use this when the format is FORMAT_AnalogVideo
//
// rcSource defines the portion of the active video signal to use
// rcTarget defines the destination rectangle
// both of the above are relative to the dwActiveWidth and dwActiveHeight fields
// dwActiveWidth is currently set to 720 for all formats (but could change for HDTV)
// dwActiveHeight is 483 for NTSC and 575 for PAL/SECAM (but could change for HDTV)
 
typedef struct tagAnalogVideoInfo {
RECT rcSource; // Width max is 720, height varies w/ TransmissionStd
RECT rcTarget; // Where the video should go
DWORD dwActiveWidth; // Always 720 (CCIR-601 active samples per line)
DWORD dwActiveHeight; // 483 for NTSC, 575 for PAL/SECAM
REFERENCE_TIME AvgTimePerFrame; // Normal ActiveMovie units (100 nS)
} ANALOGVIDEOINFO;
 
Unfortunately I'm not a C++ crack, but maybe it's a step closer to the solution.
Hope to use my Full-HD webcam soon! 8-)
SuggestionFixed Version of this ProjectmemberMember 859464027 Dec '12 - 4:39 
Someone has posted a version of this that builds. It works beautifully and the full post is good to read also.
 
https://jakemdrew.wordpress.com/2012/01/10/controlling-your-web-camera-using-c/#comment-109[^]
QuestionConstantly getting error:memberJamie McGowen17 Oct '12 - 15:26 
Error 25 File 'WebCamLib.dll' targeting 'AMD64' is not compatible with the project's target platform 'x86'
 
If I change the target to x64 then the issue goes away but then I can only install on a 64 bit system. Any words of wisdom?
AnswerRe: Constantly getting error:membermerano7 Jan '13 - 12:02 
An DLL runs in the context of the calling process. To solve the problem with running code across different platforms you have do use a DLL with the same bit-ness as the calling process. So you need more than one DLL. If you dont like this you can use different processes (x86 or x64) witch can communicate using Inter Process Communication.
 
see:
 
http://stackoverflow.com/questions/5788540/anycpu-c-sharp-dll-in-64bit-process-loads-32bit-dll
QuestionExceptionmemberAbhishekBihani16 Oct '12 - 0:07 
Hi,
I have try to use this code , but it throws "Error Starting camera"
 
Please reply.
Suggestionresolution and camera properties (vs2010)memberbntr25 Aug '12 - 5:36 
The solution here is supplemented with setting resolution and camera properties:
Versatile-WebCam-C-library-2010-Properties.zip
(based on Jake's code)
GeneralMy vote of 5mentorMd. Marufuzzaman11 Aug '12 - 8:42 
Very nice !!
GeneralMy vote of 1memberMember 898152623 Jul '12 - 9:04 
Wont work
QuestionExcelentmemberelkami8520 Jul '12 - 11:23 
Great Job dude
this is what im looking for.
i dont like it the clipboard solutions, it run well but, the clipboard its
for the user.
Congrats for this and thanks
GeneralMy vote of 5memberRoven606 Jun '12 - 0:09 
After 4 hours of googling for a working demo . . . here it is!
Thanks a lot!
GeneralMy vote of 5memberjuaduedi11 May '12 - 12:03 
Good work(to fix the .h build problem i used Jake Drew solution)
GeneralMy vote of 5membergeneral_leo3 May '12 - 0:15 
Clear code and great explanations.

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 29 Jan 2013
Article Copyright 2010 by Predrag Tomasevic
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid