原文:C# 使用摄像头拍照 支持Win7 64位

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 andWebCamLib.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 IFrameSourceinterface that CameraFrameSource implements… and that’s how this class ended up as a container instead of theCamera 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 theCameraFrameSource. 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.

When you grow tired of watching yourself, simply close the form. Once you are back in Visual Studio, check out thethrashOldCamera 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();
}

  

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 decompiledWebCam_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.

源码下载地址: Source

C# 使用摄像头拍照 支持Win7 64位的更多相关文章

  1. HP QC IE11不支持( win7 64位 无法安装)解决方法

    QC IE11不支持( win7 64位 无法安装)解决方法 使用HP公司的QC做项目缺陷管理,发现IE浏览器只支持IE7,IE8.安装插件ALP_Platform_Loader提示64位无法安装,顿 ...

  2. WIN7 64位配置Oracle SQL Developer工具

    在使用Oracle SQL 的过程中,很多参考资料,辅导机构,各种书籍绝大多数都是使用PL/SQL进行讲解,但是问题是PL/SQL对WIN7 64位系统支持不好,网上有各种各样的配置教程,我尝试了很多 ...

  3. Win7 64位下配置Qt5.3和Wincap

         最近在学网络编程,想在windows下用Qt做个网络抓包工具,就要用到WinPcap,而我的电脑的系统是Win7 64位,qt版本是Qt 5.3.1 for Windows 64-bit ( ...

  4. 偶然碰到的Win7 64位下CHM 的问题解决

    最近下了几个沪江资料,都是chm格式的,但是在win7 64位下,都显示不了里面的音频和视频flash之类的控件,虽然可以通过源文件的方式打开视频文件,但是很麻烦.    网上似乎碰到的人也不是很多, ...

  5. Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法 Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法

    Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法 Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法 1. 卸载IE9( 装了Win7 64位后,默认 ...

  6. 怎么安装Scrapy框架以及安装时出现的一系列错误(win7 64位 python3 pycharm)

    因为要学习爬虫,就打算安装Scrapy框架,以下是我安装该模块的步骤,适合于刚入门的小白: 一.打开pycharm,依次点击File---->setting---->Project---- ...

  7. 黄聪:win7 64位系统PS、AI、PSD缩略图预览补丁

    MysticThumbs支持Windows 7 / Vista / XP,32位和64位.除了预览PSD以外,还支持DDS.SGI缩略图显示. Mystic Thumbs是一款用来支持win7 64位 ...

  8. Loadrunner11无法在win7 64位上启用ie解决办法

    Loadrunner11无法在win7 64位上启用ie解决办法 1.loadrunner11在win7 64位上默认启用的是32位的那个IE浏览器,路径:C:\Program Files (x86) ...

  9. 雨林木风ghost win7 64位快速装机版V2016年

    雨林木风ghost win7 64位快速装机版V2016年2月 系统下载:http://www.xitongma.com 系统概述 雨林木风ghost win7 64位旗舰装机版自动无人值守安装,采用 ...

随机推荐

  1. 一个不错的 png压缩工具 pngquant 使用介绍 批量压缩png

    1.Png 简介. 我们知道Png 有4个通道,RGBA, 多了一个表示透明的.png 有 png 32, 24, 8 等. 大致我理解的是表示颜色的多少. 数字越小,文件就越小.常见的压缩方式是导入 ...

  2. 全新E:网站不是之前排名浮动 相比于竞争对手究竟缺少了什么?

    这几天有非常多朋友问新辰,为什么站点排名掉了?为什么被人家逆袭反超了?当然,这无疑与你站点的内容.外链和用户体验有非常大关系,只是.新辰在此觉得,还须要多研究一下竞争对手的站点,做到:人无我有.人有我 ...

  3. java提高篇(十)-----强制类型转换

    在java中强制类型转换分为基本数据类型和引用数据类型两种,这里我们讨论的后者,也就是引用数据类型的强制类型转换. 在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需 ...

  4. 最小二乘法拟合java实现源程序(转)

    因为我所在的项目要用到最小二乘法拟合,所有我抽时间将C++实现的程序改为JAVA实现,现在贴出来,供大家参考使用./** * <p>函数功能:最小二乘法曲线拟合</p> * @ ...

  5. W3C DOM 事件模型(简述)

    1.事件模型 由于事件捕获与冒泡模型都有其长处和解释,DOM标准支持捕获型与冒泡型,能够说是它们两者的结合体.它能够在一个DOM元素上绑定多个事件处理器,而且在处理函数内部,thiskeyword仍然 ...

  6. Centos 7 静态学习IP建立

    该研究主要集中在 Centos 7.0.1406 学习整理版! 1.编者 ifcfg-eth0 档,vim 同时尽量减少未安装安装,你并不需要自己安装叙述性说明. # vim /etc/sysconf ...

  7. 设备11g_rac配置对等

    linux平台安装oracle 11gssh同等配置简单 构造grid用户任关系 登陸rac1,rac2分别运行: $ su - grid $mkdir ~/.ssh $chmod 700 ~/.ss ...

  8. 【高德地图API】从头德国高中生JS API(三)覆盖物——大喊|折线|多边形|信息表|聚合marker|点蚀图|照片覆盖

    覆盖物,是一张地图的灵魂.有覆盖物的地图.才是完整的地图.在一张地图上,除了底层的底图(瓦片图,矢量图),控件(有功能可操作的工具).最重要最必不可少的就是覆盖物了.覆盖物有多种.包含.标注.折线.多 ...

  9. DotNetOpenAuth实践

    DotNetOpenAuth实践之搭建验证服务器 DotNetOpenAuth是OAuth2的.net版本,利用DotNetOpenAuth我们可以轻松的搭建OAuth2验证服务器,不废话,下面我们来 ...

  10. quick-cocos2d-x教程8:程序框架内lib文件夹分析

    lib文件夹是如今全部文件夹中最复杂的,包括了整个运行所需的库文件.我们简单看下,在开发初期,不须要深入到这个库里去研究 cocos2d-x 文件夹就是c++版的原始文件 framework_prec ...