EmguCV学习笔记 C# 9.2 VideoWriter类

2024-09-02 10:20

本文主要是介绍EmguCV学习笔记 C# 9.2 VideoWriter类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。

EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。

教程VB.net版本请访问:EmguCV学习笔记 VB.Net 目录-CSDN博客

教程C#版本请访问:EmguCV学习笔记 C# 目录-CSDN博客

笔者的博客网址:https://blog.csdn.net/uruseibest

教程配套文件及相关说明以及如何获得pdf教程和代码,请移步:EmguCV学习笔记

学习VB.Net知识,请移步: vb.net 教程 目录_vb中如何用datagridview-CSDN博客

 学习C#知识,请移步:C# 教程 目录_c#教程目录-CSDN博客

 

 

9.2 VideoWriter类

VideoWriter类提供了将帧图像数据保存为视频文件的功能。

 

9.2.1 构造函数

VideoWriter类常用的1个构造函数:

public VideoWriter(

           string fileName,

                    int compressionCode,

                    double fps,

                    Size size,

           bool isColor

)

参数说明:

  1. fileName:保存的视频文件名。如果需要保存的视频文件已经存在,那么videowriter类将会删除原文件,并创建一个新的视频文件。
  2. codecId:视频编解码器的代码,详见9.2.2节【Fourcc方法】。
  3. fps:视频的帧率。
  4. size:视频的宽度和高度。
  5. isColor:是否保存彩色视频。

以下是VideoWriter构造函数的示例代码:

VideoWriter  vw  = new VideoWriter("C:\\ saved-movie2.mp4", codecId, 25, new Size(640, 480), true);

9.2.2 Fourcc方法

Four cc是一个用于指定视频编解码器的4字节代码,是一个由四个ASCII字符组成的标识符。Four cc的作用是告诉计算机如何编解码视频文件并正确地显示它。

常见的编解码器格式对应Four cc如下:

编码

Four cc

编码

Four cc

编码

Four cc

MPEG-4

DIVX

MPEG-1

PTM1

MPEG-4.2

MP42

MPEG-4.3

DIV3

H263

U263

H263I

I263

H.264

AVC1

H.265

HEVC

FLV1

FLV1

编码不同,对电脑性能要求不同,生成文件大小也不同。具体需要哪种编码,要根据实际需求进行综合考虑。

VideoWriter类提供了fourcc静态方法,通过传入的4字符返回一个编解码器的代码。声明如下:

public static int Fourcc(

           char c1,

                    char c2,

                    char c3,

           char c4

)

 

9.2.3 Write方法

Write方法用于将一帧图像写入视频文件中。该方法声明如下:

public void Write(

                    IInputArray frame

)

参数说明:

  1. frame:要写入视频文件的帧,类型为Mat。

write方法只能将一帧图像写入视频文件中。如果需要将多帧图像写入视频文件中,可以在write方法的调用中使用循环来实现。

【代码位置:frmChapter9_1】Button11_Click

        //写入视频文件

        private void Button11_Click(object sender, EventArgs e)

        {

            VideoCapture vc = new VideoCapture("C:\\learnEmgucv\\movie1.mp4");

 

            if (vc.IsOpened == false)

                return;

            int codecId;

            //Mpeg-4.2编码

            codecId = VideoWriter.Fourcc('M','P', '4', '2');

            //宽度,同源视频文件

            int width = (int)vc.Get(CapProp.FrameWidth);

            //高度,同源视频文件

            int height = (int)vc.Get(CapProp.FrameHeight);

            //帧率,同源视频文件

            Double movieFps = vc.Get(CapProp.Fps);

 

            //使用Mpeg-4.2来编码

            VideoWriter vw = new VideoWriter("C:\\learnEmgucv\\saved-movie.mp4", codecId, 50, new Size(width, height), true);

 

            Mat m = new Mat();

            while (true)

            {

                m = vc.QueryFrame();

                if (m == null)

                    break;

                if (m.IsEmpty)

                    break;

                ImageBox1.Image = m;

                ImageBox1.Refresh();

                //将帧图像输出到文件

                vw.Write(m);

                System.Threading.Thread.Sleep((int)(1000 / movieFps));

            }

            vc.Dispose();

            vw.Dispose();

            Label1.Text = "保存完毕";

        }

 

【代码位置:frmChapter9_1】Button12_Click、Button13_Click

        //是否停止录制视频标记

        Boolean stopRecord;

 

        //开始录制摄像头视频

        private void Button12_Click(object sender, EventArgs e)

        {

            VideoCapture vc = new VideoCapture(0);

 

            if (vc.IsOpened == false)

                return;

            int codecId;

            //Mpeg-4.2编码

            codecId = VideoWriter.Fourcc('M', 'P', '4', '2');

 

            //使用Mpeg-4.2来编码

            VideoWriter vw = new VideoWriter("C:\\learnEmgucv\\saved-movie1.mp4", codecId, 25, new Size(640, 480), true);

 

            Mat m = new Mat();

 

            stopRecord = false;

            while (stopRecord == false)

            {

                m = vc.QueryFrame();

                if (m == null)

                    break;

                if (m.IsEmpty)

                    break;

                Mat mout = new Mat();

                CvInvoke.Canny(m, mout, 160, 250, 3);

 

                ImageBox1.Image = mout;

                ImageBox1.Refresh();

 

                //输出到文件

                vw.Write(mout);

 

                //需要增加doevents,否则会出现不响应

                System.Windows.Forms.Application.DoEvents();

            }

 

            //必须释放资源

            vc.Dispose ();

            vw.Dispose();

            Label1.Text = "保存完毕";

        }

        //停止录制摄像头视频

        private void Button13_Click(object sender, EventArgs e)

        {

            stopRecord = true;

        }

 

事实上,在录制摄像头视频时,即使在循环中加了Application.DoEvents(),程序运行时也会出现卡顿的情况。在实际中最好是在ImageGrabbed事件中进行处理。

【代码位置:frmChapter9_1】Button14_Click、vc3_ImageGrabbed、Button15_Click

        VideoCapture vc3;

        VideoWriter vw3;

        //是否停止录制

        Boolean stopRecord3 = false;

 

        //调用ImageGrabbed进行录制视频

        private void Button14_Click(object sender, EventArgs e)

        {

            vc3 = new VideoCapture(0);

            if (vc3.IsOpened == false)

            {

                MessageBox.Show("打开摄像头失败");

                return;

            }

            //stopRecord3 = false

 

            int codecId;

            //Mpeg-4.2编码

            codecId = VideoWriter.Fourcc('M', 'P', '4', '2');

 

            vw3 = new VideoWriter("C:\\learnEmgucv\\saved-movie2.mp4", codecId, 25, new Size(640, 480), true);

            //添加ImageGrabbed事件

            vc3.ImageGrabbed += vc3_ImageGrabbed;

            //启动

            vc3.Start();

        }

 

        //在ImageGrabbed事件里面进行录制视频

        private void vc3_ImageGrabbed(object sender, EventArgs e)

        {

            Mat nextframe = new Mat();

            if (stopRecord3 == true)

            {

                //取消事件

                vc3.ImageGrabbed -= vc3_ImageGrabbed;

                //释放资源

                vc3.Dispose();

                vw3.Dispose();

                Label1.Text = "录制结束";

            }

            else

            {

                //获得视频图像

                vc3.Retrieve(nextframe);

                //输出

                vw3.Write(nextframe);

 

                ImageBox1.Image = nextframe;

                System.Threading.Thread.Sleep(40);

            }

    }

 

        private void Button15_Click(object sender, EventArgs e)

        {

            stopRecord3 = true;

        }

 

【代码位置:frmChapter9_1】Button16_Click、getMask

        //模拟实现绿幕视频和其他视频合并并输出

        //1、为了简化说明,未采用在ImageGrabbed事件中进行处理

        //2、为了取得更好的效果,在实际中还需要考虑对扣取的部分进行边缘处理

        private void Button16_Click(object sender, EventArgs e)

        {

            //前景是一个绿幕视频

            VideoCapture vc1 = new VideoCapture("c:\\learnEmgucv\\前景.wmv");

            if (vc1.IsOpened == false)

            {

                MessageBox.Show("打开前景文件失败");

                return;

            }

            //获得前景视频的帧率

            Double fps1 = vc1.Get(CapProp.Fps);

            //获得前景文件的帧数

            int frames1 = (int)vc1.Get(CapProp.FrameCount);

 

            //背景视频

            VideoCapture vc2 = new VideoCapture("c:\\learnEmgucv\\背景.mp4");

            if (vc2.IsOpened == false)

            {

                MessageBox.Show("打开背景文件失败");

                return;

            }

            //获得背景视频的帧率

            Double fps2 = vc2.Get(CapProp.Fps);

            //获得背景文件的帧数

            int frames2 = (int)vc2.Get(CapProp.FrameCount);

 

            //输出编码,使用MPEG-4.3

            int vfourcc = Emgu.CV.VideoWriter.Fourcc('D', 'I', 'V', '3');

            VideoWriter vw = new VideoWriter("c:\\learnEmgucv\\output-movie.avi", vfourcc, 25, new Size(vc1.Width, vc1.Height), true);

 

            //输出帧数为两个视频帧数相比较最小的

            int maxframecount = frames1 > frames2 ? frames2 : frames1;

            for (int i = 0; i < maxframecount; i++)

            {

                Console.WriteLine("处理:" + i);

                //读取前景视频的一帧

                Mat m1 = new Mat();

                vc1.Read(m1);

                Mat mmask1 = new Mat();

                mmask1 = getMask(m1);

 

                Mat mfront = new Mat();

                CvInvoke.BitwiseAnd(m1, mmask1, mfront);

 

                //读取背景视频的一帧

                Mat m2 = new Mat();

                vc2.Read(m2);

                Mat mmask2 = new Mat();

                mmask2 = ~mmask1;

 

                Mat mback = new Mat();

                CvInvoke.BitwiseAnd(m2, mmask2, mback);

 

                Mat mout = new Mat();

                mout = mfront + mback;

                vw.Write(mout);

 

                //代码会不定位置出现错误提示:

                //OpenCV: Failed to allocate xxxxx bytes”

                //错误的原因主要是提供的内存不足,无法加载更多数据。

                //解决方法:

                //有些网站提出需要切换到64位编译

                //但是经过测试仍然会出现上述错误

                //最好是把所有资源都释放了(如下)。经测试没有发生错误。

                mmask1.Dispose();

                mmask2.Dispose();

                mfront.Dispose();

                mback.Dispose();

                mout.Dispose();

 

                System.Threading.Thread.Sleep(40);

            }

            vc1.Dispose();

            vc2.Dispose();

 

            Label1.Text = "输出视频完成";

        }

        //将获得的图像根据颜色范围二值化。

        private Mat getMask(Mat inputMat)

        {

            Mat mhsv = new Mat();

            CvInvoke.CvtColor(inputMat, mhsv, ColorConversion.Bgr2Hsv);

 

            //这里测试的是在这两个颜色范围之间

            ScalarArray lower = new ScalarArray(new MCvScalar(35, 43, 46));

            ScalarArray upper = new ScalarArray(new MCvScalar(77, 255, 255));

 

            //提取图像中某个颜色范围内的像素

            //颜色值在范围内,则将其设置为白色(255),否则将其设置为黑色(0)

            Mat mmask = new Mat();

            CvInvoke.InRange(mhsv, lower, upper, mmask);

 

            //根据实际需要判断是否反转颜色

            Mat mreversalmask = new Mat();

            mreversalmask = ~mmask;

 

            //以下代码输出二值图作为mask的彩色图,也就是原图去除了绿色背景

            Mat m3channel = new Mat();

            CvInvoke.CvtColor(mreversalmask, m3channel, ColorConversion.Gray2Bgr);

 

            return m3channel;

        }

输出结果如下图所示:

ebfb6b052de24b03b18f9a4f39b07ccf.png 

图9-4 模拟绿幕抠图生成视频

 

 

这篇关于EmguCV学习笔记 C# 9.2 VideoWriter类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1129694

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识