Update #1
- I have added
Directx
to obtain cameras that did not show up using WIA. You may download the source code from Sourceforge.
- Minimized delay at startup.
- Even though it was pretty fast to grab a frame, it is now a little faster.
- I have also added the ability to continuously grab frames from camera (delegates and timers).
- I have not documented most of my code, but I will very soon (Sorry).
- For the next update, you should be able to multicast images on your network. I am actually testing it right now. I have to say it works really well.
Please let me know if you run into any bugs or if you have any questions. I would be happy to help you.
Introduction
For a long time, I have been looking for a good way to capture images without too much trouble. All you need to do is call one method to get the image.
I have used Windows Image Acquisition (WIA) to capture images. WIA provides a great deal of compatibility with webcams already running on Microsoft Windows XP.
This is my first posting on The Code Project, please do not hesitate to comment or give any suggestions. I would be very thankful if you provide any feedback.
WIA
Improved Image Capture Technologies: WIA
Windows XP supports still-image devices through Windows Image Acquisition (WIA), which uses the Windows Driver Model (WDM) architecture. WIA provides robust communication between applications and image-capture devices, allowing you to capture images efficiently and transfer them to your computer for editing and use.
WIA supports small computer system interface (SCSI), IEEE 1394, USB, and serial digital still image devices. Support for infrared, parallel, and serial still-image devices—which are connected to standard COM ports—comes from the existing infrared, parallel, and serial interfaces. Image scanners and digital cameras are examples of still image devices. WIA also supports Microsoft DirectShow®-based webcams and digital video (DV) camcorders to capture frames from video.
Windows Image Acquisition Architecture
WIA architecture is both an Application Programming Interface (API) and a Device Driver Interface (DDI). The WIA architecture includes components provided by the software and hardware vendor, as well as by Microsoft. Figure 1 below illustrates the WIA architecture.
Figure 1: The components of WIA architecture.
Click
here to see full-sized image.
The DLL Fix
I have included all the required references in the source code. You don't need to do this, but if you wish to know how you may read this section.
You want to add a reference to Microsoft Windows Image Acquisition type library.
You also want to add a reference to WiaVideo Type Library. However, after some trouble I read about a bug and its fix that occurs while importing the type library.
Use ILDASM to dump the type library Interop.WIAVIDEOLib.dll, then use Notepad to replace occurrences of valuetype _RemotableHandle&
to native int
. Then compile the iL dump file you fixed using ILASM
in the CMD ilasm /DLL WIAVIDEOLib.il
. Add a reference to the new DLL and you are set to go.
The Code
Please keep in mind that the first image you take will initialize the camera. The initialization process takes a few seconds. You may notice the Join(3000)
I added when activating the webcam. I added the join
in order to avoid exceptions and allow enough time for the camera to do its thing. It was necessary to use the join
. I think this delay is because USB protocol is slow or maybe it is just my camera. After the first image is taken, images start flying so fast it is going to look like a live video feed!!
using System;
using System.Collections.Generic;
using System.Text;
using WIALib;
using WIAVIDEOLib;
using System.Runtime.InteropServices;
namespace WebCamLibrary
{
public class WebCam
{
private static WebCam WebCamObj;
private WIAVIDEOLib.WiaVideo WiaVideoObj;
private WIALib.Wia WiaObj;
private WIALib.DeviceInfo DefaultDevice;
private WIALib.DeviceInfo[] Devices;
private bool DeviceIsOpen;
private WebCam()
{
DeviceIsOpen = false;
Initialize();
SetDefaultDevice();
OpenDefaultDevice();
}
public static WebCam NewWebCam()
{
if (WebCam.WebCamObj == null)
WebCam.WebCamObj = new WebCam();
return WebCam.WebCamObj;
}
private void Initialize()
{
if (this.WiaObj == null)
{
this.WiaObj = new WiaClass();
}
if (this.WiaVideoObj == null)
{
this.WiaVideoObj = new WiaVideoClass();
this.WiaVideoObj.ImagesDirectory = System.IO.Path.GetTempPath();
}
}
public void CloseResources()
{
try
{
if (WiaObj != null)
Marshal.FinalReleaseComObject(WiaObj);
if (WiaVideoObj != null)
Marshal.FinalReleaseComObject(WiaVideoObj);
if (DefaultDevice != null)
Marshal.FinalReleaseComObject(DefaultDevice);
if (Devices != null)
Marshal.FinalReleaseComObject(Devices);
DeviceIsOpen = false;
GC.Collect();
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
}
private void SetDefaultDevice()
{
CloseDefaultDevice();
System.Collections.Generic.List<WIALIB.DEVICEINFOCLASS> devs =
new List<DEVICEINFOCLASS>();
WIALib.Collection devsCol =
this.WiaObj.Devices as WIALib.CollectionClass;
foreach (object obj in devsCol)
{
WIALib.DeviceInfoClass dev = (WIALib.DeviceInfoClass)
System.Runtime.InteropServices.Marshal.CreateWrapperOfType(
obj, typeof(WIALib.DeviceInfoClass));
if (dev.Type.Contains("Video") == true)
{
devs.Add(dev);
}
else
{
Marshal.FinalReleaseComObject(obj);
Marshal.FinalReleaseComObject(dev);
}
}
Devices = devs.ToArray();
if (Devices.Length > 0)
{
DefaultDevice = Devices[0];
}
else
{
throw new Exception("No Cameras Detected");
}
Marshal.FinalReleaseComObject(devsCol);
GC.ReRegisterForFinalize(devs);
}
public string[] GetConnectedCameras()
{
string[] devs = new string[this.Devices.Length];
for (int i = 0; i < devs.Length; i++)
{
devs[i] = this.Devices[i].Name;
}
return devs;
}
private void CloseDefaultDevice()
{
if (this.DeviceIsOpen == false)
return;
try
{
if (this.WiaVideoObj != null)
{
this.WiaVideoObj.DestroyVideo();
Marshal.FinalReleaseComObject(this.DefaultDevice);
DeviceIsOpen = false;
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
finally
{
DeviceIsOpen = false;
}
}
public bool SetDefaultDevice(string name)
{
CloseDefaultDevice();
for (int i = 0; i < Devices.Length; i++)
{
if (Devices[i].Name.Equals(name))
{
DefaultDevice = Devices[i];
return true;
}
}
return false;
}
private bool OpenDefaultDevice()
{
CloseDefaultDevice();
try
{
this.WiaVideoObj.PreviewVisible = 0;
this.WiaVideoObj.CreateVideoByWiaDevID(this.DefaultDevice.Id,
IntPtr.Zero, 0, 0);
System.Threading.Thread.CurrentThread.Join(3000);
this.DeviceIsOpen = true;
this.WiaVideoObj.Play();
System.Threading.Thread.CurrentThread.Join(3000);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
CloseDefaultDevice();
return false;
}
return true;
}
public byte[] GrabFrame()
{
string imagefile;
this.WiaVideoObj.TakePicture(out imagefile);
return ReadImageFile(imagefile);
}
private byte[] ReadImageFile(string img)
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(img);
byte[] buf = new byte[fileInfo.Length];
System.IO.FileStream fs = new System.IO.FileStream(img,
System.IO.FileMode.Open,System.IO.FileAccess.Read);
fs.Read(buf, 0, buf.Length);
fs.Close();
fileInfo.Delete();
GC.ReRegisterForFinalize(fileInfo);
GC.ReRegisterForFinalize(fs);
return buf;
}
}
}
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "www.codeproject.com")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebCamService : System.Web.Services.WebService
{
private WebCamLibrary.WebCam cam;
public WebCamService()
{
cam = WebCamLibrary.WebCam.NewWebCam();
}
[WebMethod]
public byte[] GrabFrame() {
return cam.GrabFrame();
}
[WebMethod]
public string[] GetConnectedCameras()
{
return cam.GetConnectedCameras();
}
}
Future Features
- Redo this article, I don't think I did a very good job because its 6:00 am -> bedtime.
- I would like use my other project to be able to broadcast images over the network(multicasting).
- Look into possible compression technologies.
- I think it would be great to transfer Images over UDP using smallest headers possible.
WebControl
for Handheld devices to view camera images over the Web.
Notes
Fixed article layout