【 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

智能推荐

苹果快捷键怎么调出来_原来还有这么好用的CAD快捷键,文末附赠快捷键鼠标垫!留言走起...-程序员宅基地

文章浏览阅读141次。▼相信大家都看过一些大神做CAD,那个图纸真是做的又快又好看!当然大神们其实也就基础好一点,把快捷键记得过目不忘,所以接下来小编就教大家一个非常Skr的方法,保证你对这些快捷键过目不忘,文末更有免费鼠标垫领取,千万别错过哦!这个方法就是建立我们的思维导图了,文字看了可能会忘记,但是通过导图的方式,就会变成思维图形,更加符合我们大脑的思考习惯,就可以牢牢记住这些快捷键啦:▼例如我们看到下面的就是绘图..._苹果cad快捷键

MATLAB的GUI 程序设计_制作一个曲面光照效果的演示界面,如图所示,三个弹出式菜单分别用于选择曲面形式、-程序员宅基地

文章浏览阅读7.2k次,点赞7次,收藏60次。第七章 MATLAB的GUI 程序设计Chapter 8: Design of MATLAB of GUI programGUI(Graphical User Interfaces):由各种图形对象组成的用户界面,在这种用户界面下,用户的命令和对程序的控制是通过“选择”各种图形对象来实现的。目前90%以上的应用程序和软件都是在GUI下运行的。MATLAB有两种GUI用户界面控件的创建方式,基于命令行的方式用程序来制作和基于GUI的方式制作。这里主要介绍基于GUI的方式。MATLAB 的._制作一个曲面光照效果的演示界面,如图所示,三个弹出式菜单分别用于选择曲面形式、

MT7628开发环境搭建_undefined reference to `llseek-程序员宅基地

文章浏览阅读2.1k次。参考openwrt 快速入门1.环境搭建1.1Ubuntu dockerhttps://www.runoob.com/docker/ubuntu-docker-install.html​1.1.1使用官方安装脚本自动安装安装命令如下:curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun​ps: 我已经放弃用deepin编译旧版openwrt ,修复了十几个bug还是有bug,无敌下载14.04版本docke_undefined reference to `llseek

13 Kubectl命令概览_kube_ps1关闭-程序员宅基地

文章浏览阅读81次。kubectx:用于切换kubernetes context kube-ps1:为命令行终端增加$PROMPT字段 kube-shell:交互式带命令提示的kubectl终端kube-shell开源项目kube-shell可以为kubectl提供自动的命令提示和补全,使用起来特别方便,推荐给大家。Kube-shell有以下特性:命令提示,给出命令的使用说明 自动补全,列出可选命令并可以通过tab键自动补全,支持模糊搜索 高亮 使用tab键可以列出可选的对象 vim模式M..._kube_ps1关闭

ensp各种报错积累(以及解决方法)_ensp配置路由地址时错误-程序员宅基地

文章浏览阅读1k次,点赞11次,收藏9次。此报错的意思是请续订默认配置,就是让你去一级一级的删除,首先删除你设置的允许vlan通过的命令,然后去取消掉更改的端口类型命令(就是在配置命令前面加一个undo),再去更改端口类型就成功了。此报错的意思是已经加入了接口,不能在修改模式,所以需要先去把端口全部删除,在修改模式即可成功。他的意思就是说这个IP地址已经配置了,不需要在配置了。2.修改链路聚合模式的时候。3.更改IP地址的时候。_ensp配置路由地址时错误

经典JS-序列号_ucfp:74a28a8b-b3fb-4602-ca5f-0ebdf880c1ff-16927960-程序员宅基地

文章浏览阅读10w+次。3D0E1D4E75686FA0FF1C6F6F626F6F6F6F6F6F6F8F381B2FFF2D6FEF396F6F6A1B6E6F6D762B39E3021B282C725C726F4F6F6F6F5F5F5C330E1F06251C335B5E0D580E5E095D575B0E56560B5F0B41051C6F9F4FCC60636EBE7FBE7A3E637EB613394327_ucfp:74a28a8b-b3fb-4602-ca5f-0ebdf880c1ff-1692796091560

随便推点

Accessing static Data and Functions in Legacy C_setyearanddayofyear-程序员宅基地

文章浏览阅读1.1k次。http://www.renaissancesoftware.net/blog/archives/430http://www.renaissancesoftware.net/blog/archives/450It’s a new year; last year was a leap year; so the quadrennial reports of leap y_setyearanddayofyear

vue把字符串分割成等长的若干字符串,根据特定字符分割字符串_vue 分割字符串-程序员宅基地

文章浏览阅读1.6w次,点赞2次,收藏20次。把字符串分割成等长的若干字符串,根据特定字符分割字符串_vue 分割字符串

朴素贝叶斯分类器的例子_朴素贝叶斯分类器 例子-程序员宅基地

文章浏览阅读1.1k次。一、病人分类的例子让我从一个例子开始讲起,你会看到贝叶斯分类器很好懂,一点都不难。某个医院早上收了六个门诊病人,如下表。  症状  职业   疾病  打喷嚏 护士   感冒   打喷嚏 农夫   过敏   头痛  建筑工人 脑震荡   头痛  建筑工人 感冒   打喷嚏 教师   感冒   头痛  教师   脑震荡现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?根据贝叶斯..._朴素贝叶斯分类器 例子

当mysql数据库转换为sqlserver数据库时常见报错_mysql 数据导出在sqlserver不能用-程序员宅基地

文章浏览阅读527次。↵下面是我在把mysql数据库转换为sqlserver数据库时候遇到过的一些错,踩过的坑,把它总结下来防止以后再出错。报错 1:com.microsoft.sqlserver.jdbc.SQLServerException: 仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'user_student'中的标识列指定显式值。出错原因:当mysql数据库转换为sqlserver数据库时,如果第一个id设置为自动递增,那么String sql = "..._mysql 数据导出在sqlserver不能用

LeetCode 刷题常用数据结构(Java 中的实现)_javalist集合map组合刷题 leetcode-程序员宅基地

文章浏览阅读1.9k次,点赞4次,收藏27次。记录常用数据结构(栈、队列、数组、列表、字符串、集合等),在 Java 中如何使用它的实现类。_javalist集合map组合刷题 leetcode

Visual Studio Code 设置成中文_visualstudiocode中文-程序员宅基地

文章浏览阅读6.7k次,点赞5次,收藏4次。Visual Studio Code 编辑器设置成中文 分2个步骤1、【查看】--【扩展】(Ctrl+Shift+X) 在扩展:商店中输入Chinese,搜索到 适用于 VS Code 的中文(简体)语言包,点击安装; 2、Ctrl+Shift+P,调出搜索框,输入 Configure Display Language,选中此配置项; 把配置页面中的"..._visualstudiocode中文

推荐文章

热门文章

相关标签