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 模拟绿幕抠图生成视频

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/878822.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『功能项目』Unity本地数据库读取进入游戏【29】

本章项目成果展示 打开上一篇28Unity连接读取本地数据库的项目&#xff0c; 本章要做的事情是通过读取本地数据库登录进入游戏场景 首先创建一个脚本文件夹&#xff1a; 新建脚本&#xff1a;MySqlAccess.cs 编写脚本&#xff1a;MySqlAccess.cs using UnityEngine; using MyS…

uniapp scroll-view滚动页面

页面滚动固定距离&#xff08;scrollTop&#xff09; <template><view><button click"Test">测试</button><scroll-view style"height: 100px;" :scroll-top"scrollTop" scroll-y"true" class"scrol…

FME教程:通过更新读模块,解决FME读取shapefile数据,提示意外输入,“在转换中,某些读取的要素与工作空间的要素类不匹配……”的问题

目录 一、问题情况 二、解决方法 一、问题情况 在使用制作好的FME模板读取shapefile数据时&#xff0c;有时候会遇到弹窗提示意外输入&#xff0c;模板无法运行&#xff0c;在日志信息中警示“在转换中&#xff0c;某些读取的要素与工作空间的要素类不匹配。可能由于读模块的…

鸿蒙轻内核M核源码分析系列十三 消息队列Queue

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 轻内核M核源码分析系列一 数据结构-双向循环链表 轻内核M核源码分析系列二 数据结构-任务就绪队列 鸿蒙轻内核M核源码分析系列三 数据结构-任务排序链表 轻…

Unity TMP (TextMeshPro) 更新中文字符集

TMP更新中文字符集 1 字符集缺失说明2 字体的字符表2.1 字符表更新模式&#xff1a;动态2.2 字符表更新模式&#xff1a;静态 3 更新字符集步骤3.1 打开纹理更新面板3.1 导入文本文件3.3 关于警告处理 4 修改TMP默认字体设置 1 字符集缺失说明 使用TMP显示中文需要用到中文字体…

数据预处理与协同过滤推荐算法——从数据清洗到个性化电影推荐

推荐系统在现代应用中占据了重要地位&#xff0c;尤其在电影、音乐等个性化内容推荐中广泛使用。本文将介绍如何使用数据预处理、特征工程以及多种推荐算法&#xff08;包括协同过滤、基于内容的推荐、混合推荐等&#xff09;来实现电影推荐系统。通过Pandas、Scikit-learn、Te…

etcdctl defrag 剔除、添加etcd节点

零、准备工作 find / -name etcdctl cp /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/12/fs/usr/local/bin/etcdctl /usr/local/bin/etcdctlalias ec"etcdctl --endpointshttps://127.0.0.1:2379 --cacert /etc/kubernetes/pki/etcd/ca.crt --…

利用正则表达式从字符串中提取浮点数

在 Python 中&#xff0c;使用正则表达式可以非常方便地从字符串中提取浮点数。Python 的 re 模块提供了正则表达式支持。下面是如何使用正则表达式提取浮点数的示例。 1、问题背景 在开发过程中&#xff0c;有时候我们需要从字符串中提取浮点数&#xff0c;例如从 HTML 代码中…

网关功能介绍

在微服务架构中&#xff0c;网关&#xff08;API Gateway&#xff09;扮演着至关重要的角色&#xff0c;它作为客户端和微服务之间的中介&#xff0c;负责路由、过滤、认证、限流等职责。以下是一些常见的网关实现&#xff1a; Spring Cloud Gateway&#xff1a; Spring Cloud …

linux系统中,计算两个文件的相对路径

realpath --relative-to/home/itheima/smartnic/smartinc/blocks/ruby/seanet_diamond/tb/parser/test_parser_top /home/itheima/smartnic/smartinc/corundum/fpga/lib/eth/lib/axis/rtl/axis_fifo.v 检验方式就是直接在当前路径下&#xff0c;把输出的路径复制一份&#xff0…

5-2 检测内存容量

1 使用的是bios 中断&#xff0c; 每次进行检测都会返回一块 内容。并且标志上&#xff0c;这块内存是否可用。 接下来是代码&#xff1a; 首先是构建 一个文件夹&#xff0c; 两个文件。 types.h 的内容。 #ifndef TYPES_H #define TYPES_H// 基本整数类型&#xff0c;下面的…

全球圆柱锂电池行业领军者!比克电池亮相2024深圳eVTOL展

2024深圳eVTOL产业发展大会暨低空经济展览会将于9月23-25日在深圳坪山燕子湖国际会展中心举办。展会将通过“两天论坛三天展览”的形式展开&#xff0c;专注未来城市空中交通新形态、民用有人驾驶、无人驾驶航空器、城市低空物流&#xff0c;并讨论eVTOL的整机研发、设计、制造…

拍卖新纪元:Spring Boot赋能在线拍卖解决方案

需求分析 1.1技术可行性&#xff1a;技术背景 在线拍卖系统是在Windows操作系统中进行开发运用的&#xff0c;而且目前PC机的各项性能已经可以胜任普通网站的web服务器。系统开发所使用的技术也都是自身所具有的&#xff0c;也是当下广泛应用的技术之一。 系统的开发环境和配置…

Python+selenium web测试入门基础!

自动化测试框架 from selenium import webdriver 获取浏览器对象 我这里是edge浏览器&#xff0c;用的是edge的webdriver # webdriver获取浏览器对象  driver webdriver.Edge() 尝试打开网站并关闭​​​​​​​ # 准备一个网址  url "https://www.baidu.com/"…

包拯断案 | 数据库从库GTID在变化 为何没有数据写入@还故障一个真相

提问&#xff1a;作为DBA运维的你是否遇到过这些烦恼 1、数据库从库复制链路如何正确配置表过滤信息&#xff1f; 2、数据库从库的GTID在变化&#xff0c;实际却没有数据写入&#xff0c;究竟是什么原因&#xff1f; 心中有章&#xff0c;遇事不慌 作为DBA的你&#xff0c;…

如何构建短视频矩阵?云微客开启多账号协同作战

你有没有疑惑过&#xff0c;为什么有些账号每一次发布视频&#xff0c;都要艾特一下其他账号呢&#xff1f;那些被艾特的账号&#xff0c;你有点进去关注过吗&#xff1f;其实做过运营的都或多或少的接触过矩阵&#xff0c;短视频矩阵的玩法现在也逐步成为了趋势。企业通过多账…

pyautogui进行点击失效,pyautogui.click()失效

背景&#xff1a;在Pycharm里&#xff0c;使用pythonpyautogui调用 .exe程序文件时候&#xff0c;当程序界面出来之后&#xff0c;鼠标失去反应&#xff0c;用pyautogui进行点击。后面尝试使用图片相似也无法实行点击。 解决方法&#xff1a;运行Pycharm或者其他ide的时候选择…

黑马点评2——商户查询缓存(P37店铺类型查询业务添加缓存练习题答案)redis缓存、更新、穿透、雪崩、击穿、工具封装

文章目录 什么是缓存&#xff1f;添加Redis缓存店铺类型查询业务添加缓存练习题 缓存更新策略给查询商铺的缓存添加超时剔除和主动更新的策略 缓存穿透缓存空对象布隆过滤 缓存雪崩解决方案 缓存击穿解决方案基于互斥锁方式解决缓存击穿问题基于逻辑过期的方式解决缓存击穿问题…

极市开发平台yolov8训练无人机数据集样例数据流程

先进入vscode&#xff0c;进入src_repo文件夹。 第一步&#xff0c;克隆一个比较好的博主的库&#xff1a; GitHub - Incalos/YOLO-Datasets-And-Training-Methods: This project involves making custom datasets for the YOLO series and model training methods for YOLO.…

关于蓝屏查看日志分析原因

一、前提 虽然电脑经常蓝屏&#xff0c;或者发生了蓝屏现象&#xff0c;但是仍然可以进入系统&#xff0c;并且可以进行桌面操作。 二、查看蓝屏日志 1.按下win键&#xff0c;搜索计算机管理。 2.依次点击&#xff1a;系统工具->事件查看器->Windows日志->系统 3.在…