设为首页 收藏本站
查看: 1165|回复: 0

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

[复制链接]

尚未签到

发表于 2015-5-15 13:58:57 | 显示全部楼层 |阅读模式
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.
DSC0000.jpg
  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();
}

  
DSC0001.gif

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.

DSC0002.png
  
  
  源码下载地址: Source
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-67270-1-1.html 上篇帖子: plsql连不上win7 64位系统下的64位oracle 下篇帖子: win7+Qt+OpenCV安装攻略
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表