您好!欢迎来到默认站点! 请登录 注册有礼
首页 新闻资讯 > 研发日志 > 物联网

EmguCV获取网络摄像机画面

2020-8-27 21:20:00 人评论

首先先介绍一下OpenCV,OpenCV的全称是:Open Source Computer Vision Library,OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语…

首先先介绍一下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版

图片.png

基础代码很简单,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 + "/";
        }
    }
}

运行界面截图:

图片.png

上一篇:物流网IO模块开发四:功能说明

下一篇:没有了

相关资讯

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?
    会员登陆
    18004549898
    QQ
    Mvcms网站管理系统客服QQ:
    返回顶部