首先先介绍一下OpenCV,OpenCV的全称是:Open Source Computer Vision Library,OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。OpenCV 拥有包括 300 多个C函数的跨平台的中、高层 API。它不依赖于其它的外部库——尽管也可以使用某些外部库。
EmguCV是OpenCV的一个跨平台的.Net封装,由于OpenCV是用C和C++编写的,Emgu用C#对其进行封装,允许用.Net语言来调用OpenCV函数,如C#、VB、VC++等,同时该封装也可以被编译到Mono平台和允许在Windows、Mac OS、Android、iPhone、iPad等多个平台上运行
特性:
Image class with Generic Color and Depth
Automatic garbage collection(自动垃圾回收)
Xml Serializable Image(用于网络)
Image class / Direct invoke function from OpenCV(直接对OpenCV函数的invoke操作)
Generic operations on image pixel(对像素操作)
安装:采用NuGet管理器安装,示例代码采用EmguCV3.1.0.1 for .NetFramework3.0版
基础代码很简单,RTSP地址格式为rtsp://username:password@192.168.1.1,可以通过PotPlayer等视频播放器直接播放视频流:
public Bitmap GetImage(string ipaddress, int port, string username, string password){ Bitmap result = null; try { string url = string.Format(@"rtsp://{0}:{1}@{2}:{3}", username, password, ipaddress, port);//设置RTSP视频流地址 using (Capture capture = new Capture(url)) {//创建视频捕获对象 Mat mat = capture.QueryFrame();//将旧数据关键帧保存到数据矩阵中(不是实时数据,是1-2秒前旧数据),网络摄像机获取图像为jpg格式 result = mat.Bitmap;//转换数据矩阵为位图数据 } } catch(Exception e) { message = e.Message; } return result; }
对于Web程序需要在线程中运行此方法, 有之前文本朗读合成器System.Speech.Synthesis开发经验,已经知道.Net4.5的async新特性可以解决异步无效异常Bug,那就直接采用async方式处理:
/// <summary> /// 异步方式获取摄像机单帧画面 /// </summary> /// <param name="ipaddress"></param> /// <param name="username"></param> /// <param name="password"></param> /// <returns></returns> public async Task<Bitmap> GetImageAsync(string ipaddress, string username, string password) { return await Task.Run<Bitmap>(() => GetImage(ipaddress, 554, username, password)); }
至此可以在Web应用调用此方法获取运行正常的网络摄像机画面,但是如果IP地址不可访问,系统将卡死(无限期的等待。。。),那么就需要引入超时中止方法的机制:
/// <summary> /// 异步方式获取摄像机单帧画面 /// </summary> /// <param name="ipaddress"></param> /// <param name="port"></param> /// <param name="username"></param> /// <param name="password"></param> /// <param name="timeout">超时时间(毫秒)</param> /// <returns></returns> public async Task<Bitmap> GetImageAsync(string ipaddress, int port, string username, string password, int timeout) { using (CancellationTokenSource timeeoutCancellationTokenSouce = new CancellationTokenSource()) {//异步线程控制对象 Task<Bitmap> getImage = GetImageAsync(ipaddress, port, username, password);//执行线程任务 //有效线程与超时线程任务一同执行 Task completedTask = await Task.WhenAny(getImage, Task.Delay(timeout, timeeoutCancellationTokenSouce.Token)); if (completedTask == getImage) {//超时时间内有效线程(获取摄像机单幅画面)响应 timeeoutCancellationTokenSouce.Cancel();//中止超时计时器线程 return await getImage;//返回单幅摄像机画面 } else { code = 408; message = "此操作已经超时!"; return null;//超时退出,返回空值,由调用方法处理后续逻辑 } } }
至此可以在Web应用中稳定运行了,下面给出IPCameraHelper.cs管理类完整代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Drawing; using Emgu.CV; namespace Mvcms.Common.Net { /// <summary> /// 网络摄像机管理类 /// </summary> public class IPCameraHelper { private int code = 0; private string message = string.Empty; /// <summary> /// 获取异常消息代码 /// </summary> public int ErrorCode { get { return code; } } /// <summary> /// 获取异常消息 /// </summary> public string Message { get { return message; } } #region 获取单帧画面 /// <summary> /// 获取摄像机单帧画面 /// </summary> /// <param name="ipaddress"></param> /// <param name="username"></param> /// <param name="password"></param> /// <returns></returns> public Bitmap GetImage(string ipaddress, string username, string password) { return GetImage(ipaddress, 554, username, password); } /// <summary> /// 获取摄像机单帧画面 /// </summary> /// <param name="ipaddress"></param> /// <param name="port"></param> /// <param name="username"></param> /// <param name="password"></param> /// <returns></returns> public Bitmap GetImage(string ipaddress, int port, string username, string password){ Bitmap result = null; try { string url = string.Format(@"rtsp://{0}:{1}@{2}:{3}", username, password, ipaddress, port);//设置RTSP视频流地址 using (Capture capture = new Capture(url)) {//创建视频捕获对象 Mat mat = capture.QueryFrame();//将旧数据关键帧保存到数据矩阵中(不是实时数据,是1-2秒前旧数据),网络摄像机获取图像为jpg格式 result = mat.Bitmap;//转换数据矩阵为位图数据 } } catch(Exception e) { message = e.Message; } return result; } #endregion #region 异步方式获取单幅画面 /// <summary> /// 异步方式获取摄像机单帧画面 /// </summary> /// <param name="ipaddress"></param> /// <param name="username"></param> /// <param name="password"></param> /// <returns></returns> public async Task<Bitmap> GetImageAsync(string ipaddress, string username, string password) { return await Task.Run<Bitmap>(() => GetImage(ipaddress, 554, username, password)); } /// <summary> /// 异步方式获取摄像机单帧画面 /// </summary> /// <param name="ipaddress"></param> /// <param name="port"></param> /// <param name="username"></param> /// <param name="password"></param> /// <returns></returns> public async Task<Bitmap> GetImageAsync(string ipaddress, int port, string username, string password) { return await Task.Run<Bitmap>(() => GetImage(ipaddress, port, username, password)); } #endregion #region 异步方式获取摄像机单幅画面(有超时处理机制) /// <summary> /// 异步方式获取摄像机单帧画面 /// </summary> /// <param name="ipaddress">ip地址,默认554端口</param> /// <param name="username"></param> /// <param name="password"></param> /// <param name="timeout">超时时间(毫秒)</param> /// <returns></returns> public async Task<Bitmap> GetImageAsync(string ipaddress, string username, string password, int timeout) { return await GetImageAsync(ipaddress, 554, username, password, timeout); } /// <summary> /// 异步方式获取摄像机单帧画面 /// </summary> /// <param name="ipaddress"></param> /// <param name="port"></param> /// <param name="username"></param> /// <param name="password"></param> /// <param name="timeout">超时时间(毫秒)</param> /// <returns></returns> public async Task<Bitmap> GetImageAsync(string ipaddress, int port, string username, string password, int timeout) { using (CancellationTokenSource timeeoutCancellationTokenSouce = new CancellationTokenSource()) {//异步线程控制对象 Task<Bitmap> getImage = GetImageAsync(ipaddress, port, username, password);//执行线程任务 //有效线程与超时线程任务一同执行 Task completedTask = await Task.WhenAny(getImage, Task.Delay(timeout, timeeoutCancellationTokenSouce.Token)); if (completedTask == getImage) {//超时时间内有效线程(获取摄像机单幅画面)响应 timeeoutCancellationTokenSouce.Cancel();//中止超时计时器线程 return await getImage;//返回单幅摄像机画面 } else { code = 408; message = "此操作已经超时!"; return null;//超时退出,返回空值,由调用方法处理后续逻辑 } } } #endregion } }
控制器示例代码,代码中用到RequestHelper.cs,ImageHelper.cs,FileHelper.cs类库,只是简单封装相关操作,源码包含在Mvcms源码的Mvcms.Common类库中:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Mvc; using System.Net; using System.Threading.Tasks; using System.Drawing; using Mvcms.Common; using Mvcms.Common.Net; namespace Mvcms.Web.Controllers.admin.IOT { public class ip_cameraController: UI.Controllers.BaseManageController { private string ip = string.Empty; string username = string.Empty; string password = string.Empty; int width = 0; int height = 0; private void InitParameter() { IsPlugin = true; ip = request.QueryString["ip"].Trim(); if (request.QueryString["user"].IsExist()) { //Url参数中包含用户登录信息 username = request.QueryString["username"].Trim(); password = request.QueryString["psssword"].Trim(); } else { //使用缺省登录信息 username = "admin"; password = "jtdy1111"; } //处理高度或宽度缩放 width = request.QueryString["width"].ToInt(1024);///默认缩放至1024宽度 height = request.QueryString["height"].ToInt(); } public async Task<ActionResult> GetImageUrl() { InitParameter(); IPCameraHelper ipcHelper = new IPCameraHelper(); Image img = await ipcHelper.GetImageAsync(ip, username,password, 5000); if (img == null) { return Content(ipcHelper.Message); } Image img2 = ImageHelper.MakeThumbnailImage(img, width, height, "W"); byte[] byteData = ImageConvert.ToArray(img2); string newFileName = Utils.GetRamCode() + ".jpg"; //随机生成新的文件名 string newThumbnailFileName = "thumb_" + newFileName; //随机生成缩略图文件名 string upLoadPath = GetUpLoadPath(); //本地上传目录相对路径 string fullUpLoadPath = Utils.GetMapPath(upLoadPath); //本地上传目录的物理路径 if (!System.IO.Directory.Exists(fullUpLoadPath)) { System.IO.Directory.CreateDirectory(fullUpLoadPath); } FileHelper.SaveFile(byteData, fullUpLoadPath + newFileName); return Content(upLoadPath + newFileName); } public async Task<ActionResult> GetImage() { InitParameter(); IPCameraHelper ipcHelper = new IPCameraHelper(); Image img = await ipcHelper.GetImageAsync(ip, username, password, 5000); if (img == null) { if (ipcHelper.ErrorCode == 408) { return new HttpStatusCodeResult(HttpStatusCode.RequestTimeout); } else { return Content(ipcHelper.Message); } } Image img2 = ImageHelper.MakeThumbnailImage(img, width, height, "W"); byte[] byteData = ImageConvert.ToArray(img2); string newFileName = Utils.GetRamCode() + ".jpg"; //随机生成新的文件名 string newThumbnailFileName = "thumb_" + newFileName; //随机生成缩略图文件名 string upLoadPath = GetUpLoadPath(); //本地上传目录相对路径 string fullUpLoadPath = Utils.GetMapPath(upLoadPath); //本地上传目录的物理路径 if (!System.IO.Directory.Exists(fullUpLoadPath)) { System.IO.Directory.CreateDirectory(fullUpLoadPath); } FileHelper.SaveFile(byteData, fullUpLoadPath + newFileName); return File(fullUpLoadPath + newFileName, "image/jpg"); } /// <summary> /// 返回上传目录相对路径 /// </summary> /// <param name="fileName">上传文件名</param> private string GetUpLoadPath() { string path = sysConfig.webpath + sysConfig.filepath + "/"; //站点目录+上传目录 switch (sysConfig.filesave) { case 1: //按年月日每天一个文件夹 path += DateTime.Now.ToString("yyyyMMdd"); break; default: //按年月/日存入不同的文件夹 path += DateTime.Now.ToString("yyyyMM") + "/" + DateTime.Now.ToString("dd"); break; } return path + "/"; } } }
运行界面截图:
共有条评论 网友评论