接到新任务,用来监控3D打印机的摄像头,需要对其进行控制。之前我没有做过任何这方面的工作,很是挠头。不过有脚本和开源两个强大的工具,我也不是很害怕。

  首先有人指明需要用到的库是OpenCV。CV是指Computer vision, 从名字就能判断这是专注于视频处理的库。很快我就找到了python的调用demo.并且成功的带动了手边的摄像头。http://opencv.org/安装后,就能在sources/samples/python/demo文件夹下面找到完整的demo程序。

  不过这不能解决项目问题。我们的项目要求在.net环境下开发。所以很快,我就找到了成熟的.net平台下OpenCV解决方案,名叫EMGU;

 同样的,EMGU也完整的demo例程。在EMGU3.1版本的安装路径下面\Solution\VS2013-2015文件夹中,就有摄像头的驱动例子:实现摄像头实时采样,只需要几行代码:

...
private static Capture _cameraCapture;
...
void run(){
    try
         {
            _cameraCapture = new Capture();
         }
         catch (Exception e)
         {
            MessageBox.Show(e.Message);
            return;
         }
          Application.Idle += ProcessFrame;
...
void ProcessFrame(object sender, EventArgs e)
      {
         Mat frame = _cameraCapture.QueryFrame();
}

通过Applicaiton.Idle+= ProcessFrame的加载事件,非常优雅的实现了摄像头的驱动。

 进一步,EMGU中有Websevice的例子。但是例子中传输的是一串字符串,采用的是net.tcp协议。我尝试传输p_w_picpath,结果在旁边电脑成功访问了我的摄像头,但是卡顿非常严重。(暂时分析不出来卡顿的原因)

host端代码:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using Emgu.CV;

namespace Webservice_Host
{
   class Program
   {

      static void Main(string[] args)
      {
          Uri uri = new Uri("net.tcp://localhost:8082/ImageService");
          NetTcpBinding binding = new NetTcpBinding();
          binding.Security.Mode = SecurityMode.None;

    
     
         //WSHttpBinding binding = new WSHttpBinding();

         binding.MaxReceivedMessageSize = 2147483647;
         ServiceHost host = new ServiceHost(typeof(ImageService));

         ServiceBehaviorAttribute serviceBehavior = host.Description.Behaviors.Find();
         serviceBehavior.IncludeExceptionDetailInFaults = true;



         // Create endpoint
         host.AddServiceEndpoint(typeof(IImageService), binding, uri);

         host.Open();
         Console.WriteLine("Service is ready, press any key to terminate.");
         Console.ReadKey();
         host.Close();
 
      }

   }
}
ImageService.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;

namespace Webservice_Host
{
   public class ImageService : IImageService
   {
      public Image GrabFrame()
      {
          
         Capture _capture = new Capture();
          Mat frame=new Mat();
          //_capture.Retrieve(frame,0);
          //Image img = frame.ToImage();
         Image img = _capture.QueryFrame().ToImage();
         if (_capture != null)
             _capture.Dispose();
         return img;
      }

   }
}

IImageService.cs
using System;
using System.Collections.Generic;
using System.Text;
using Emgu.CV;
using Emgu.CV.Structure;
using System.ServiceModel;

namespace Webservice_Host
{
   [ServiceContract]
   [XmlSerializerFormat]
   public interface IImageService
   {
      [OperationContract(IsOneWay = false)]
      Image GrabFrame();
   }
    
}

client端代码:

//----------------------------------------------------------------------------
//  Copyright (C) 2004-2016 by EMGU Corporation. All rights reserved.       
//----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.Threading;
using Emgu.CV;

namespace Webservice_Client
{
   public partial class Form1 : Form
   {
      private bool _started;
      private Webservice_Host.IImageService _p_w_picpathGrabber;
      private NetTcpBinding _binding;
      private static Queue p_w_picpathQ;

      public Form1()
      {
         InitializeComponent();

         _binding = new NetTcpBinding();
         _binding.Security.Mode = SecurityMode.None;
         _binding.MaxReceivedMessageSize = 2147483647;
         Queue p_w_picpathQ = new Queue();
          //System.Collections.Queue p_w_picpathQ= new System.Collections.Queue();
 
         _started = false;
      }
       
      public bool Started
      {
         get { return _started; }
         set
         {
            _started = value;
            serviceUrlBox.Enabled = !_started;

            if (!_started)
            {  
               //stop grabing frames
               //Application.Idle -= ProcessImage;

                // System.Timers.Timer t =new System.Timers.Timer(1);
               //  t.Start();
               // t.Elapsed+= ProcessImage;
            }
            else
            {
               //start to grab frames
           
                _p_w_picpathGrabber = new ChannelFactory(
                   _binding,
                   new EndpointAddress(serviceUrlBox.Text)
                   ).CreateChannel();
                //System.Timers.Timer t =new System.Timers.Timer(50);
                //t.Start();
                ////button2.Click += ProcessImage;
                //t.Elapsed+= ProcessImage;
                //// Application.Idle 
                Thread rx_p_w_picpath_thread = new Thread(new ThreadStart(get_p_w_picpath));
                Thread show_p_w_picpath_thread = new Thread(new ThreadStart(show_p_w_picpath));
                rx_p_w_picpath_thread.Start();
                show_p_w_picpath_thread.Start();
            }
         }
      }
      private void get_p_w_picpath()
      {
          Queue p_w_picpathQ = new Queue();
          while (true)
          {
              p_w_picpathQ.Enqueue(_p_w_picpathGrabber.GrabFrame());
          }
      }
      private void show_p_w_picpath()
      {
          while (true) {
            //  if (p_w_picpathQ.Count>0&&p_w_picpathQ!=null)
            //  p_w_picpathBox1.Image=p_w_picpathQ.Dequeue();
          }
      }
      private void ProcessImage(object sender, EventArgs e)
      {
         p_w_picpathBox1.Image = _p_w_picpathGrabber.GrabFrame();
      }

      private void button1_Click(object sender, EventArgs e)
      {
         Started = !Started;
         if (Started == true)
            button1.Text = "Stop";

         else 
             button1.Text = "Start";
          
            
      }

      private void ReleaseResource()
      {
         Started = false;
      }

      private void button2_Click(object sender, EventArgs e)
      {

      }
   }
}

IImageService

using System;
using System.Collections.Generic;
using System.Text;
using Emgu.CV;
using Emgu.CV.Structure;
using System.ServiceModel;

namespace Webservice_Host
{
   [ServiceContract]
   [XmlSerializerFormat]
   public interface IImageService
   {
      [OperationContract(IsOneWay = false)]
      Image GrabFrame();
   }
    
}

到此为止,我对EMGU带摄像头的尝试就结束了。后面我采用EMGU做了两件事情,都是和网络摄像头有关:一是用ASP.NET用网页控制远程web摄像头拍照并传过来实时图像。二是将EMGU截图封装成为dll的方法,交给其他工程师使用。


总体而言,也可能是受制于我的编程功力,无法用EMGU来实现更复杂的摄像头访问应用。但是EMGU作为OpenCV在.net平台的封装,其项目重心用在了图像识别算法的封装而不是多媒体应用上:我们可以看到大量的例程进行图像识别等算法,调用起来非常方便。


而对于将摄像头转成流媒体源的工作,我找到了更加有效的框架,即微软自带的Media Foudation框架。