|
|||||||||||||||||||||||||
|
|||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThere are several ways to grab and process webcamera images: WIA, DirectShow, VFW... There are lots of C# VFW examples on the Internet and most of them use .NET clipboard to transfer each frame's data from buffer to The Idea
API[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindow")]
static extern int capCreateCaptureWindow(string lpszWindowName,
int dwStyle, int X, int Y,
int nWidth, int nHeight, int hwndParent, int nID);
The capCreateCaptureWindow function creates a new window for video stream capturing and returns its handle. This function is called in step 1. [DllImport("user32", EntryPoint = "SendMessage")]
static extern bool SendMessage(int hWnd, uint wMsg, int wParam, int lParam);
The SendMessage function sends the specified message to a window or windows. It calls the window procedure for the specified window and does not return until the window procedure has processed the message. Note that both [DllImport("avicap32.dll")]
static extern bool capGetDriverDescription(intwDriverIndex,
[MarshalAs(UnmanagedType.VBByRefStr)] ref String lpszName, int cbName,
[MarshalAs(UnmanagedType.VBByRefStr)] ref String lpszVer, int cbVer);
The capGetDriverDescription function retrieves the version description of the capture driver. The The structures are: BITMAPINFO, BITMAPINFOHEADER and VIDEOHDR. The Inside the CodeThe main part of the solution is the
WebCameraDevicepublic WebCameraDevice(int frameWidth, int frameHeight, int preferredFPS,
int camID, int parentHwnd)
{
/*...*/
}
This initializes a new instance of the public void Start()
{
/*...*/
camHwnd = capCreateCaptureWindow("WebCam", 0, 0, 0, frameWidth,
frameHeight, parentHwnd, camID); // Step 2
//Try to connect a capture window to a capture driver
if (SendMessage(camHwnd, WM_CAP_DRIVER_CONNECT, 0, 0))
{
//Step 3, fill bitmap structure (see source)
/*...*/
// Enables preview mode
SendMessage(camHwnd, WM_CAP_SET_PREVIEW, 1, 0);
// Sets the frame display rate in preview mode. 34 ms ~ 29FPS
SendMessage(camHwnd, WM_CAP_SET_PREVIEWRATE, 34, 0);
// Sets the format of captured video data.
SendBitmapMessage(camHwnd, WM_CAP_SET_VIDEOFORMAT,
Marshal.SizeOf(bInfo), ref bInfo);
// Multithreading begins here
frameThread = new Thread(new ThreadStart(this.FrameGrabber));
bStart = true; // Flag variable
frameThread.Priority = ThreadPriority.Lowest;
frameThread.Start();
}
/*...*/
}
The multithreading mechanism in After calling the private void FrameGrabber()
{
while (bStart) // if worker active thread is still required
{
/*...*/
// get the next frame. This is the SLOWEST part of the program
SendMessage(camHwnd, WM_CAP_GRAB_FRAME_NOSTOP, 0, 0);
//Make a function callback
SendHeaderMessage(camHwnd, WM_CAP_SET_CALLBACK_FRAME, 0,
delegateFrameCallBack);
/*...*/
}
}
What happens in this block of code? The private void "code-string" name="<span">"FrameCallBack">FrameCallBack(IntPtr hwnd, ref VIDEOHEADER hdr)
{
if (OnCameraFrame != null)
{
Bitmap bmp = new Bitmap(frameWidth, frameHeight, 3 *
frameWidth, System.Drawing.Imaging.PixelFormat.Format24bppRgb,
hdr.lpData);
OnCameraFrame(this, new WebCameraEventArgs(bmp));
}
if (preferredFPSms == 0)
{
// blocks thread until WaitHandle receives a signal
autoEvent.WaitOne();
}
else
{
// blocks thread for preferred milliseconds
autoEvent.WaitOne(preferredFPSms, false);
}
}
As you can see, the function contains all public void Set()
{
//Send a signal to the current WainHandle and allow blocked worker
//(FrameGrabber) thread to proceed
autoEvent.Set();
}
public void Stop()
{
try
{
bStart = false;
Set();
SendMessage(camHwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
}
catch { }
}
public void ShowVideoDialog()
{
SendMessage(camHwnd, WM_CAP_DLG_VIDEODISPLAY, 0, 0);
}
How Does It Work?We have a class library with all the necessary Web camera image capturing classes. First of all, we have to get the available VFW devices and display them to the user: public FormMain()
{
InitializeComponent();
WebCameraDeviceManager camManager = new WebCameraDeviceManager();
// fill combo box with available devices' names
cmbDevices.Items.AddRange(camManager.Devices);
// First available video device.
// I always receive "Microsoft WDM Image Capture (Win32)"
cmbDevices.SelectedIndex = 0;
}
The start button and the private void btnStart_Click(object sender, EventArgs e)
{
/*...*/
camDevice = new WebCameraDevice
(320, 200, 0, cmbDevices.SelectedIndex, this.Handle.ToInt32());
// Register for event notification
camDevice.OnCameraFrame +=
new WebCameraFrameDelegate(camDevice_OnCameraFrame);
camDevice.Start();
/*...*/
}
void camDevice_OnCameraFrame(object sender, WebCameraEventArgs e)
{
/*...*/
ImageProcessing.Filters.Flip(e.Frame, false, true); // Explained below
pictureBox.Image = e.Frame;
camDevice.Set(); // comment this if prefferedFPS != 0
/*...*/
}
Have you noticed the Unexpected Image FlipThere was an unexpected vertical image flip. I haven't discovered why this happens yet. It happens only in the case of History
P.S.I'd like to ask you to be lenient with the article because it's my first article on The Code Project. Please let me know if you have liked/disliked it or have any questions about it.
|
||||||||||||||||||||||||