接到新任务,用来监控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文件夹中,就有摄像头的驱动例子:实现摄像头实时采样,只需要几行代码:

...privatestaticCapture_cameraCapture;...voidrun(){try{_cameraCapture=newCapture();}catch(Exceptione){MessageBox.Show(e.Message);return;}Application.Idle+=ProcessFrame;...voidProcessFrame(objectsender,EventArgse){Matframe=_cameraCapture.QueryFrame();}

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

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

host端代码:

usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.InteropServices;usingSystem.ServiceModel;usingSystem.ServiceModel.Description;usingSystem.Text;usingEmgu.CV;namespaceWebservice_Host{classProgram{staticvoidMain(string[]args){Uriuri=newUri("net.tcp://localhost:8082/ImageService");NetTcpBindingbinding=newNetTcpBinding();binding.Security.Mode=SecurityMode.None;//WSHttpBindingbinding=newWSHttpBinding();binding.MaxReceivedMessageSize=2147483647;ServiceHosthost=newServiceHost(typeof(ImageService));ServiceBehaviorAttributeserviceBehavior=host.Description.Behaviors.Find<ServiceBehaviorAttribute>();serviceBehavior.IncludeExceptionDetailInFaults=true;//Createendpointhost.AddServiceEndpoint(typeof(IImageService),binding,uri);host.Open();Console.WriteLine("Serviceisready,pressanykeytoterminate.");Console.ReadKey();host.Close();}}}

ImageService.csusingSystem;usingSystem.Collections.Generic;usingSystem.Text;usingSystem.ServiceModel;usingEmgu.CV;usingEmgu.CV.CvEnum;usingEmgu.CV.Structure;namespaceWebservice_Host{publicclassImageService:IImageService{publicImage<Bgr,Byte>GrabFrame(){Capture_capture=newCapture();Matframe=newMat();//_capture.Retrieve(frame,0);//Image<Bgr,Byte>img=frame.ToImage<Bgr,Byte>();Image<Bgr,Byte>img=_capture.QueryFrame().ToImage<Bgr,Byte>();if(_capture!=null)_capture.Dispose();returnimg;}}}

IImageService.csusingSystem;usingSystem.Collections.Generic;usingSystem.Text;usingEmgu.CV;usingEmgu.CV.Structure;usingSystem.ServiceModel;namespaceWebservice_Host{[ServiceContract][XmlSerializerFormat]publicinterfaceIImageService{[OperationContract(IsOneWay=false)]Image<Bgr,Byte>GrabFrame();}}

client端代码:

//----------------------------------------------------------------------------//Copyright(C)2004-2016byEMGUCorporation.Allrightsreserved.//----------------------------------------------------------------------------usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Text;usingSystem.Windows.Forms;usingSystem.ServiceModel;usingSystem.Threading;usingEmgu.CV;namespaceWebservice_Client{publicpartialclassForm1:Form{privatebool_started;privateWebservice_Host.IImageService_p_w_picpathGrabber;privateNetTcpBinding_binding;privatestaticQueue<IImage>p_w_picpathQ;publicForm1(){InitializeComponent();_binding=newNetTcpBinding();_binding.Security.Mode=SecurityMode.None;_binding.MaxReceivedMessageSize=2147483647;Queue<IImage>p_w_picpathQ=newQueue<IImage>();//System.Collections.Queuep_w_picpathQ=newSystem.Collections.Queue();_started=false;}publicboolStarted{get{return_started;}set{_started=value;serviceUrlBox.Enabled=!_started;if(!_started){//stopgrabingframes//Application.Idle-=ProcessImage;//System.Timers.Timert=newSystem.Timers.Timer(1);//t.Start();//t.Elapsed+=ProcessImage;}else{//starttograbframes_p_w_picpathGrabber=newChannelFactory<Webservice_Host.IImageService>(_binding,newEndpointAddress(serviceUrlBox.Text)).CreateChannel();//System.Timers.Timert=newSystem.Timers.Timer(50);//t.Start();////button2.Click+=ProcessImage;//t.Elapsed+=ProcessImage;////Application.IdleThreadrx_p_w_picpath_thread=newThread(newThreadStart(get_p_w_picpath));Threadshow_p_w_picpath_thread=newThread(newThreadStart(show_p_w_picpath));rx_p_w_picpath_thread.Start();show_p_w_picpath_thread.Start();}}}privatevoidget_p_w_picpath(){Queue<IImage>p_w_picpathQ=newQueue<IImage>();while(true){p_w_picpathQ.Enqueue(_p_w_picpathGrabber.GrabFrame());}}privatevoidshow_p_w_picpath(){while(true){//if(p_w_picpathQ.Count>0&&p_w_picpathQ!=null)//p_w_picpathBox1.Image=p_w_picpathQ.Dequeue();}}privatevoidProcessImage(objectsender,EventArgse){p_w_picpathBox1.Image=_p_w_picpathGrabber.GrabFrame();}privatevoidbutton1_Click(objectsender,EventArgse){Started=!Started;if(Started==true)button1.Text="Stop";elsebutton1.Text="Start";}privatevoidReleaseResource(){Started=false;}privatevoidbutton2_Click(objectsender,EventArgse){}}}

IImageService

usingSystem;usingSystem.Collections.Generic;usingSystem.Text;usingEmgu.CV;usingEmgu.CV.Structure;usingSystem.ServiceModel;namespaceWebservice_Host{[ServiceContract][XmlSerializerFormat]publicinterfaceIImageService{[OperationContract(IsOneWay=false)]Image<Bgr,Byte>GrabFrame();}}

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


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


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