【 Emgu CV教程】1.1、Emgu CV 简介及使用_emgucv-程序员宅基地

技术标签: c#  Emgu CV  Emgu CV使用教程  计算机视觉  图像处理  

作者在工作中偶然接触到了Emgu CV这个视觉处理封装包,并对它的具体功能做了比较全面的试验,为了方便广大C#程序员也能愉快的体验到视觉处理的乐趣,我决定通过一系列的文章和代码演示,来一步步的实现Emgu CV,或者说是OpenCV的基础功能。

由于作者代码水平有限,以及是一个视觉处理方便的业余爱好者,因此只能从门外汉的角度进行编程和文字描述,不足之处希望大家不要介意。另外介绍的代码、功能描述、章节划分,甚至是理论的介绍都有可能出现错误之处,请大家多多指正。

本系列文章适用于C#程序员,对计算机视觉有一定爱好,并且理论基础比较薄弱。提供的代码也仅限于相互交流和技术验证,请勿用于商业目的。

1、OpenCV

在计算机视觉处理领域,OpenCV可是说是大名鼎鼎。其用C++语言编写,它具有C ++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS,OpenCV主要倾向于实时视觉应用,并在可用时利用MMX和SSE指令, 如今也提供对于C#、Ch、Ruby,GO的支持(来自百度百科)。

对于 .NET 程序员来说,直接使用OpenCV很不方便。如果程序员想用WinForm或者WPF开发一些视觉应用的程序,此时就有两个选择,分别是OpenCvSharpEmgu CV

2、OpenCvSharp

好像是一位日本程序员开发并维护的OpenCV封装包,具体的使用方法不在本系列课程的介绍范围内,据说是使用方式上更接近原始的OpenCV,有兴趣的朋友可以单独了解一下。其项目网址如下:

Github网址OpenCV wrapper for .NET. Contribute to shimat/opencvsharp development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/shimat/opencvsharp

​3、Emgu CV

其官方介绍如下:Emgu CV is a cross platform .Net wrapper to the OpenCV image processing library. Allowing OpenCV functions to be called from .NET compatible languages. The wrapper can be compiled by Visual Studio and Unity, it can run on Windows, Linux, Mac OS, iOS and Android.

Emgu CV: OpenCV in .NET (C#, VB, C++ and more)icon-default.png?t=N7T8https://emgu.com/其在版本的更新上,紧随OpenCV,截止到2023年12月11日,其最新版本为Emgu CV-4.8.0

从具体功能上来说,它实现了OpenCV应有的以下功能:

  1. 图像处理,比如滤波、形态学处理、二值化、色彩空间转换、图像增强等。
  2. 计算机视觉:支持各种计算机视觉算法,如特征提取、目标检测、跟踪、人脸识别等。
  3. 模式识别:提供了模式识别算法,如支持向量机(SVM)、随机森林等。
  4. 深度学习:支持深度学习算法,如卷积神经网络(CNN)、循环神经网络(RNN)等。
  5. OCR:可以结合Tesseract OCR引擎实现光学字符识别(OCR)功能,将图像中的文字转换为可编辑和可搜索的文本。
  6. 视频处理:提供了视频处理功能,如视频帧提取、视频编解码等。
  7. 几何变换:支持各种几何变换算法,如平移、旋转、缩放等。
  8. 图像分割:提供了各种图像分割算法,如基于阈值的分割、基于区域的分割等。

​​        上面这个功能介绍也是我抄的,据说是有这些功能,但是我也还没有全部实现呢。

4、WPF项目引用Emgu CV

本系列课程都以WPF项目为例,来进行Emgu CV的演示,WinForm的使用方法应该差不多,读者可以自行试验。

首先创建WPF应用,基于.NET Framework 4.7.2

然后在NuGet中引用以下四个包:

    Emgu.CV

    Emgu.CV.runtime.windows

    Emgu.CV.Bitmap

    Emgu.CV.UI

引用并安装好NuGet包,WPF应用如下图所示:

5、简单应用

Emgu CV用于图像分析,最简单的应用当然是读取出一副原始图片并显示。在实际过程中分为四种情况:

1、读取.jpg、.bmp、png类的图片文件;

2、读取本地.mp4类的视频文件并显示每一帧图像;

3、读取计算机的摄像头并显示;

4、读取网络上的视频文件并显示。

补充资料:视频其实就是在一秒内连续播放n幅图片,然后下一秒再不停的播放,直到结束为止。

5.1、显示图片文件

在MainWindow.xmal中建立一个Image控件,代码如下:

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Image Name="Image1" Grid.Row="0"  Grid.Column="0" Grid.ColumnSpan="1" HorizontalAlignment="Left" Margin="10,10,10,10" VerticalAlignment="Top"/>
    </Grid>

窗体的Load事件中执行如下代码:

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Image<Bgr, Byte> image = new Image<Bgr, byte>("lena.jpg"); // 创建Image类的变量image,并从文件加载图片
            Image1.Source = image.ToBitmapSource(); // Image控件显示加载到的图片
        }

其实就定义一个Emgu CV内的Image类,加载本地 lena.jpg 文件,然后让WPF的Image1控件显示。运行项目,结果如下: 

5.2、显示本地mp4文件

窗体的Load事件中要先建立一个VideoCapture类(Emgu CV中用于操作视频的类),用于打开本地 J20.mp4 文件,打开成功后,通过       

ComponentDispatcher.ThreadIdle += new EventHandler(ProcessFrame)

去执行ProcessFrame函数内的代码。而ProcessFrame函数就是在Image1控件中显示出 J20.mp4 文件的每一帧,全部代码如下:

        /// <summary>
        /// Window窗体加载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            videoCapture?.Dispose(); // 先释放现有的VideoCapture类
            videoCapture = new VideoCapture("J20.mp4");

            // 进行异常判断
            if (!videoCapture.IsOpened)
            {
                System.Windows.MessageBox.Show("流媒体视频加载出错!", "提示", (MessageBoxButton)MessageBoxButtons.OK);
                return;
            }

            // 开始捕捉并显示每一帧
            ComponentDispatcher.ThreadIdle += new EventHandler(ProcessFrame);
        }

        /// <summary>
        /// 显示每一帧.
        /// </summary>
        /// <param name="sender">触发事件的控件对象,就是当前的对象.</param>
        /// <param name="e">触发事件的控件对象,记录事件传递过来的额外信息.</param>
        private void ProcessFrame(object sender, EventArgs e)
        {
            try
            {
                // 进行异常判断,如果打不开流媒体,则终止视频分析
                if (videoCapture == null || videoCapture.Ptr == IntPtr.Zero || !videoCapture.IsOpened)
                {
                    return;
                }

                frame = videoCapture.QueryFrame();
                if (frame == null)
                {
                    return;
                }

                if (frame.IsEmpty)
                {
                    return;
                }
                Image1.Source = frame.ToBitmapSource();
            }
            catch (Exception)
            {
            }
        }

程序运行效果如下图所示,视频在播放时速度比利用VLC等软件播放的要快。原因是VLC会调整播放速度,比如一秒钟显示24帧,每一帧中间会有短暂的间隔;而上面的代码则是显示完一帧马上就显示下一帧,中间没有间隔。

5.3、显示本地计算机的摄像头

显示本地文件的代码是

       

videoCapture = new VideoCapture("J20.mp4");

只需要改成

       

videoCapture = new VideoCapture(0);

就可以打开本地计算机的摄像头。可以简单的理解为:new VideoCapture(0) 的意思就是打开本地计算机的第0个摄像头。如果您的计算机上有多个外接摄像机,可以用

videoCapture = new VideoCapture(1);
videoCapture = new VideoCapture(2);

分别打开不同的外接摄像头。

5.3、读取网络上的视频文件并显示

5.2章节已经可以利用VideoCapture显示出本地文件,是这样写的

videoCapture = new VideoCapture(0);

此时,只需要把数字 0 ,换成网络视频的地址,就可以播放出对应的视频,代码及效果如下:

            videoCapture?.Dispose(); // 先释放现有的VideoCapture类
            videoCapture = new VideoCapture("http://gcalic.v.myalicdn.com/gc/sgns01_1/index.m3u8");

提示

1、WPF的Image1控件显示时,或是 image.ToBitmapSource(),或是 Image1.Source = frame.ToBitmapSource()。这个.ToBitmapSource()方法来自于Emgu CV的一个官方文件,可以通过这个路径下载:

emgucv/Emgu.CV.NativeImage/BitmapSourceExtension.cs at master · emgucv/emgucv · GitHub

2、当Emgu CV要操作视频时,需要用到VideoCapture进行初始化。

  • 如果是本地文件,就在括号内写文件路径。
  • 如果是计算机连接的外接摄像头,就在括号内写摄像头的数字编号(编号顺序和规则是什么,作者不知道,您可以试一试)。
  • 如果是网络视频,就在括号内写视频地址,RTSP、RTMP、HLS各种格式的都可以播放。

这篇文章全部代码如下,MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="540" Width="800" Loaded="Window_Loaded">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Image Name="Image1" Grid.Row="0"  Grid.Column="0" Grid.ColumnSpan="1" HorizontalAlignment="Left" Margin="10,10,10,10" VerticalAlignment="Top"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using Emgu.CV;
using System;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private static VideoCapture videoCapture = null;
        private Mat frame; // 视频播放的n帧

        public MainWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Windowc窗体加载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // 1.显示图片文件
            // Image<Bgr, Byte> image = new Image<Bgr, byte>("lena.jpg"); // 创建Image类的变量image,并从文件加载图片
            // Image1.Source = image.ToBitmapSource(); // Image控件显示加载到的图片

            // 2.显示本地mp4文件
            // videoCapture?.Dispose(); // 先释放现有的VideoCapture类
            // videoCapture = new VideoCapture("J20.mp4");

            // 3.显示本地计算机的摄像头
            // videoCapture?.Dispose(); // 先释放现有的VideoCapture类
            // videoCapture = new VideoCapture(0);

            // 3.显示本地计算机的摄像头
            videoCapture?.Dispose(); // 先释放现有的VideoCapture类
            videoCapture = new VideoCapture("http://gcalic.v.myalicdn.com/gc/sgns01_1/index.m3u8");

            // 进行异常判断
            if (!videoCapture.IsOpened)
            {
                System.Windows.MessageBox.Show("流媒体视频加载出错!", "提示", (MessageBoxButton)MessageBoxButtons.OK);
                return;
            }

            // 开始捕捉并显示每一帧
            ComponentDispatcher.ThreadIdle += new EventHandler(ProcessFrame);
        }

        /// <summary>
        /// 显示每一帧.
        /// </summary>
        /// <param name="sender">触发事件的控件对象,就是当前的对象.</param>
        /// <param name="e">触发事件的控件对象,记录事件传递过来的额外信息.</param>
        private void ProcessFrame(object sender, EventArgs e)
        {
            try
            {
                // 进行异常判断,如果打不开流媒体,则终止视频分析
                if (videoCapture == null || videoCapture.Ptr == IntPtr.Zero || !videoCapture.IsOpened)
                {
                    return;
                }

                frame = videoCapture.QueryFrame();
                if (frame == null)
                {
                    return;
                }

                if (frame.IsEmpty)
                {
                    return;
                }
                Image1.Source = frame.ToBitmapSource();
            }
            catch (Exception)
            {
            }
        }
    }
}

总结

Emgu CV总体上使用起来比较简单,而且所有的函数都和OpenCV原始函数相差不多,接下来的文章会把几十个主要的函数,或者图像/视频处理方式介绍给大家。

原创不易,请勿抄袭。共同进步,相互学习。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sswss12345/article/details/134921699

智能推荐

python简易爬虫v1.0-程序员宅基地

文章浏览阅读1.8k次,点赞4次,收藏6次。python简易爬虫v1.0作者:William Ma (the_CoderWM)进阶python的首秀,大部分童鞋肯定是做个简单的爬虫吧,众所周知,爬虫需要各种各样的第三方库,例如scrapy, bs4, requests, urllib3等等。此处,我们先从最简单的爬虫开始。首先,我们需要安装两个第三方库:requests和bs4。在cmd中输入以下代码:pip install requestspip install bs4等安装成功后,就可以进入pycharm来写爬虫了。爬

安装flask后vim出现:error detected while processing /home/zww/.vim/ftplugin/python/pyflakes.vim:line 28_freetorn.vim-程序员宅基地

文章浏览阅读2.6k次。解决方法:解决方法可以去github重新下载一个pyflakes.vim。执行如下命令git clone --recursive git://github.com/kevinw/pyflakes-vim.git然后进入git克降目录,./pyflakes-vim/ftplugin,通过如下命令将python目录下的所有文件复制到~/.vim/ftplugin目录下即可。cp -R ...._freetorn.vim

HIT CSAPP大作业:程序人生—Hello‘s P2P-程序员宅基地

文章浏览阅读210次,点赞7次,收藏3次。本文简述了hello.c源程序的预处理、编译、汇编、链接和运行的主要过程,以及hello程序的进程管理、存储管理与I/O管理,通过hello.c这一程序周期的描述,对程序的编译、加载、运行有了初步的了解。_hit csapp

18个顶级人工智能平台-程序员宅基地

文章浏览阅读1w次,点赞2次,收藏27次。来源:机器人小妹  很多时候企业拥有重复,乏味且困难的工作流程,这些流程往往会减慢生产速度并增加运营成本。为了降低生产成本,企业别无选择,只能自动化某些功能以降低生产成本。  通过数字化..._人工智能平台

electron热加载_electron-reloader-程序员宅基地

文章浏览阅读2.2k次。热加载能够在每次保存修改的代码后自动刷新 electron 应用界面,而不必每次去手动操作重新运行,这极大的提升了开发效率。安装 electron 热加载插件热加载虽然很方便,但是不是每个 electron 项目必须的,所以想要舒服的开发 electron 就只能给 electron 项目单独的安装热加载插件[electron-reloader]:// 在项目的根目录下安装 electron-reloader,国内建议使用 cnpm 代替 npmnpm install electron-relo._electron-reloader

android 11.0 去掉recovery模式UI页面的选项_android recovery 删除 部分菜单-程序员宅基地

文章浏览阅读942次。在11.0 进行定制化开发,会根据需要去掉recovery模式的一些选项 就是在device.cpp去掉一些选项就可以了。_android recovery 删除 部分菜单

随便推点

echart省会流向图(物流运输、地图)_java+echart地图+物流跟踪-程序员宅基地

文章浏览阅读2.2k次,点赞2次,收藏6次。继续上次的echart博客,由于省会流向图是从echart画廊中直接取来的。所以直接上代码<!DOCTYPE html><html><head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /&_java+echart地图+物流跟踪

Ceph源码解析:读写流程_ceph 发送数据到其他副本的源码-程序员宅基地

文章浏览阅读1.4k次。一、OSD模块简介1.1 消息封装:在OSD上发送和接收信息。cluster_messenger -与其它OSDs和monitors沟通client_messenger -与客户端沟通1.2 消息调度:Dispatcher类,主要负责消息分类1.3 工作队列:1.3.1 OpWQ: 处理ops(从客户端)和sub ops(从其他的OSD)。运行在op_tp线程池。1...._ceph 发送数据到其他副本的源码

进程调度(一)——FIFO算法_进程调度fifo算法代码-程序员宅基地

文章浏览阅读7.9k次,点赞3次,收藏22次。一 定义这是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。该算法实现简单,只需把一个进程已调入内存的页面,按先后次序链接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量、常用函数、例程等的页面,FIFO 算法并不能保证这些页面不被淘汰。这里,我_进程调度fifo算法代码

mysql rownum写法_mysql应用之类似oracle rownum写法-程序员宅基地

文章浏览阅读133次。rownum是oracle才有的写法,rownum在oracle中可以用于取第一条数据,或者批量写数据时限定批量写的数量等mysql取第一条数据写法SELECT * FROM t order by id LIMIT 1;oracle取第一条数据写法SELECT * FROM t where rownum =1 order by id;ok,上面是mysql和oracle取第一条数据的写法对比,不过..._mysql 替换@rownum的写法

eclipse安装教程_ecjelm-程序员宅基地

文章浏览阅读790次,点赞3次,收藏4次。官网下载下载链接:http://www.eclipse.org/downloads/点击Download下载完成后双击运行我选择第2个,看自己需要(我选择企业级应用,如果只是单纯学习java选第一个就行)进入下一步后选择jre和安装路径修改jvm/jre的时候也可以选择本地的(点后面的文件夹进去),但是我们没有11版本的,所以还是用他的吧选择接受安装中安装过程中如果有其他界面弹出就点accept就行..._ecjelm

Linux常用网络命令_ifconfig 删除vlan-程序员宅基地

文章浏览阅读245次。原文链接:https://linux.cn/article-7801-1.htmlifconfigping &lt;IP地址&gt;:发送ICMP echo消息到某个主机traceroute &lt;IP地址&gt;:用于跟踪IP包的路由路由:netstat -r: 打印路由表route add :添加静态路由路径routed:控制动态路由的BSD守护程序。运行RIP路由协议gat..._ifconfig 删除vlan

推荐文章

热门文章

相关标签