使用OpenCV类库,从视频中截取视频帧

news/2025/11/5 15:21:15/文章来源:https://www.cnblogs.com/xietianjiao/p/19193735

一、用途

用于构建日志报告,报告中中某软件运行的截图,有问题时,日志与视频当时的帧对应。

二、代码实现

using System;
using System.Reflection;
using OpenCvSharp;
using OpenCvSharp.Extensions;namespace PicInVideo
{/// <summary>/// 视频帧截取工具类(支持指定时间点截取)/// </summary>public static class VideoFrameCaptureTool{/// <summary>/// 从视频中截取指定时间点的帧/// </summary>/// <param name="videoPath">视频文件路径(支持FFmpeg录制的MP4/MKV/AVI等格式)</param>/// <param name="outputImagePath">输出图片路径(含文件名,如:D:\frame.jpg)</param>/// <param name="videoStartTime">视频开始的绝对时间(格式:yyyy-MM-dd HH:mm:ss)</param>/// <param name="targetCaptureTime">要截取的绝对时间(格式:yyyy-MM-dd HH:mm:ss)</param>/// <returns>是否截取成功</returns>public static bool CaptureFrameByAbsoluteTime(string videoPath,string outputImagePath,string videoStartTime,string targetCaptureTime){// 验证输入参数if (!File.Exists(videoPath)){throw new Exception($"错误:视频文件不存在 - {videoPath}");}// 解析时间戳if (!DateTime.TryParseExact(videoStartTime, "yyyy-MM-dd HH:mm:ss",System.Globalization.CultureInfo.InvariantCulture,System.Globalization.DateTimeStyles.None, out DateTime startDt)){//Console.WriteLine($"错误:视频开始时间格式无效(应为 yyyy-MM-dd HH:mm:ss) - {videoStartTime}");return false;}if (!DateTime.TryParseExact(targetCaptureTime, "yyyy-MM-dd HH:mm:ss",System.Globalization.CultureInfo.InvariantCulture,System.Globalization.DateTimeStyles.None, out DateTime targetDt)){//Console.WriteLine($"错误:目标截取时间格式无效(应为 yyyy-MM-dd HH:mm:ss) - {targetCaptureTime}");return false;}// 计算目标时间相对于视频开始的偏移秒数TimeSpan timeOffset = targetDt - startDt;if (timeOffset.TotalSeconds < 0){//Console.WriteLine("错误:目标截取时间早于视频开始时间");return false;}// 调用核心截取方法(按相对时间偏移)return CaptureFrameByRelativeTime(videoPath, outputImagePath, timeOffset.TotalSeconds);}/// <summary>/// 从视频中截取指定相对时间点的帧(相对于视频开始)/// </summary>/// <param name="videoPath">视频文件路径</param>/// <param name="outputImagePath">输出图片路径</param>/// <param name="relativeSeconds">相对于视频开始的偏移秒数(如:10.5 表示第10.5秒)</param>/// <returns>是否截取成功</returns>public static bool CaptureFrameByRelativeTime(string videoPath,string outputImagePath,double relativeSeconds){// 验证参数if (!File.Exists(videoPath)){throw new Exception($"错误:视频文件不存在 - {videoPath}");}if (relativeSeconds < 0){return false;}// 创建输出目录(如果需要)string outputDir = Path.GetDirectoryName(outputImagePath);if (!string.IsNullOrEmpty(outputDir) && !Directory.Exists(outputDir)){Directory.CreateDirectory(outputDir);}// 打开视频并定位到目标帧using (var capture = new VideoCapture(videoPath)){if (!capture.IsOpened()){//Console.WriteLine($"错误:无法打开视频文件 - {videoPath}");return false;}// 获取视频关键信息double fps = capture.Fps;int totalFrames = (int)capture.FrameCount;double totalSeconds = totalFrames / fps; // 视频总时长(秒)//Console.WriteLine($"视频信息:帧率={fps:F2} FPS,总帧数={totalFrames},总时长={totalSeconds:F2}秒");//Console.WriteLine($"目标截取时间:相对视频开始 {relativeSeconds:F2} 秒");// 检查目标时间是否超出视频时长if (relativeSeconds > totalSeconds + 0.1) // 允许0.1秒误差
                {//Console.WriteLine($"错误:目标时间超出视频时长(视频仅 {totalSeconds:F2} 秒)");return false;}// 计算目标帧序号(四舍五入取最近帧)int targetFrameIndex = (int)Math.Round(relativeSeconds * fps);targetFrameIndex = Math.Clamp(targetFrameIndex, 0, totalFrames - 1); // 确保不越界//Console.WriteLine($"定位到帧序号:{targetFrameIndex}");// 直接跳转到目标帧(高效,无需遍历所有帧)capture.PosFrames = targetFrameIndex;// 读取目标帧using (Mat frame = new Mat()){bool readSuccess = capture.Read(frame);if (!readSuccess){//Console.WriteLine("错误:读取目标帧失败");return false;}// 保存图片(支持JPG/PNG等格式,根据输出后缀自动识别)bool saveSuccess = Cv2.ImWrite(outputImagePath, frame);if (saveSuccess){//Console.WriteLine($"成功保存帧到:{outputImagePath}");return true;}else{//Console.WriteLine($"错误:保存图片失败 - {outputImagePath}");return false;}}}}/// <summary>/// 将视频前部截取成帧/// </summary>/// <param name="videoPath"></param>/// <param name="outputDir"></param>/// <param name="interval"></param>/// <param name="startDateTime"></param>public static void ExtractFramesWithTimestamp(string videoPath,string outputDir,int interval = 1,string startDateTime = null){// 创建输出目录if (!Directory.Exists(outputDir)){Directory.CreateDirectory(outputDir);}// 打开视频文件 using (var capture = new VideoCapture(videoPath)){if (!capture.IsOpened()){throw new Exception($"无法打开视频文件: {videoPath}");}// 获取视频属性double fps = capture.Fps;int totalFrames = (int)capture.FrameCount;//Console.WriteLine($"视频帧率: {fps:F2} FPS,总帧数: {totalFrames}");// 解析视频开始时间DateTime? startDt = null;if (!string.IsNullOrEmpty(startDateTime)){try{startDt = DateTime.ParseExact(startDateTime,"yyyy-MM-dd HH:mm:ss",System.Globalization.CultureInfo.InvariantCulture);//Console.WriteLine($"视频开始时间: {startDt}");
                    }catch (FormatException){throw new Exception("startDateTime格式错误(应为yyyy-MM-dd HH:mm:ss),将仅使用相对时间");}}int frameCount = 0;int savedCount = 0;Mat frame = new Mat();// 循环读取帧while (true){bool readSuccess = capture.Read(frame);if (!readSuccess) break; // 视频结束// 按间隔截取帧if (frameCount % interval == 0){// 计算相对时间(秒)double relativeTime = frameCount / fps;// 计算绝对时间string absoluteTimeStr = "";if (startDt.HasValue){DateTime absoluteTime = startDt.Value.AddSeconds(relativeTime);absoluteTimeStr = absoluteTime.ToString("yyyyMMdd_HHmmss_fff");}// 生成文件名string fileName;if (!string.IsNullOrEmpty(absoluteTimeStr)){fileName = $"{absoluteTimeStr}_frame_{frameCount:D6}.jpg";}else{fileName = $"t_{relativeTime:F3}_frame_{frameCount:D6}.jpg";}// 保存图片string savePath = Path.Combine(outputDir, fileName);Cv2.ImWrite(savePath, frame);savedCount++;//Console.WriteLine($"保存: {fileName} | 相对时间: {relativeTime:F3}秒");
                    }frameCount++;}frame.Release();}}}internal class Program{static void Main(string[] args){/*// 配置参数----从视频中每隔n帧截取帧string videoPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.mp4");// 视频文件路径string outputDir = "frames_with_timestamp"; // 输出目录int interval = 30;                          // 截取间隔(每30帧取一次)string videoStartTime = "2025-11-05 13:35:00"; // 视频开始时间(可选)// 调用截取函数VideoFrameCaptureTool.ExtractFramesWithTimestamp(videoPath,outputDir,interval,videoStartTime);*/// 配置参数string videoPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.mp4");  // FFmpeg录制的视频路径string outputImagePath = "captured_frame.jpg";  // 输出图片路径string videoStartTime = "2025-05-20 14:30:00";  // 视频开始的绝对时间string targetCaptureTime = "2025-05-20 14:30:15";// 要截取的绝对时间(视频开始后15秒)// 方式1:按绝对时间截取(推荐,与外部时间戳对应)bool result1 = VideoFrameCaptureTool.CaptureFrameByAbsoluteTime(videoPath, outputImagePath, videoStartTime, targetCaptureTime);}}}

依赖类库

OpenCvSharp4

OpenCvSharp4.Extensions

OpenCvSharp4.Windows

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

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

相关文章

2025 年最新防水堵漏服务公司推荐榜:涵盖地下室车库 / 隧道 / 水库大坝等场景,结合行业协会测评权威数据,精选技术过硬实力企业

引言 随着基础设施建设与建筑行业的蓬勃发展,防水堵漏质量愈发关键,其直接关系到工程安全与使用寿命。据中国建筑防水协会 2024 年度测评数据显示,国内近 35% 的建筑因渗漏问题需反复维修,不仅增加成本,还存在安全…

2025年中央空调品牌耐用的有哪些?中央空调安装公司哪个值得选?

2025年,随着居民对居住舒适度与能源效率要求的提升,中央空调市场迎来新一轮技术迭代与品牌竞争。用户在选购时,既关注中央空调品牌耐用的有哪些、中央空调品牌知名的有哪些,也困惑于中央空调安装公司哪个值得选——…

熔融指数仪市场揭秘:优质熔融指数仪品牌厂家的发展妙计

熔融指数仪主要用于测定聚合物熔体流动速率,是评估聚合物加工性能的关键设备。根据2025年最新行业报告,熔融指数仪主要用于塑料加工、化工生产及科研领域,市场需求与塑料制品应用增长直接相关。现有设备可满足不同测…

前端调试实战全解析,从浏览器到真机的可见化问题定位体系

本文系统讲解前端调试的全流程,从 Chrome DevTools 桌面调试到移动端 WebView 真机排查,涵盖控制台、断点、性能分析、网络监控等方法,并介绍 WebDebugX 如何帮助开发者解决 WebView 环境下的“看不见的问题”,构建…

智能体上下文引擎(Agentic Context Engine,ACE)

人工智能代理会随着每次任务的完成而变得更加智能🧠 Agentic Context Engine 会从代理的成功和失败中学习。只需接入系统,即可见证代理的改进。 如果你觉得这个仓库有用,请给它点个星⭐️!🤖 LLM快速入门将您最…

2025年鲁冠高透光农膜企业权威推荐榜单:持久高透光抗老化农膜/95%以上高透光率农膜/黄瓜大棚高透光膜源头厂家精选

随着现代农业对光照管理和作物产量要求的不断提升,高透光农膜作为优化种植环境的关键材料,其市场需求持续增长。据行业相关数据显示,2024年中国功能性农膜市场规模已突破200亿元,年增长率保持在10%以上,其中高透光…

完整教程:Linux -- 传输层协议TCP

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年tpep防腐钢管制造企业权威推荐榜单:防腐螺旋钢管/防腐无缝钢管/聚乙烯防腐钢管源头厂家精选

在市政工程、石油天然气、水利建设等领域,TPEP防腐钢管凭借其优异的耐腐蚀性能与结构强度,已成为输送管道系统的核心材料。根据行业统计,2025年全球TPEP防腐钢管市场规模预计保持稳健增长,其中中国市场的增长表现突…

2025 最新推荐移民服务机构排行榜:精选靠谱中介,提供专业澳洲美国欧洲等国移民方案葡萄牙 / 新西兰 / 新加坡 / 购房移民公司推荐

引言 随着全球化发展,移民需求持续增长,移民服务机构数量激增,但行业质量参差不齐,选择靠谱机构成为难题。为此,国际移民服务行业协会开展专项测评,结合近 3 年机构服务数据、客户满意度调查(有效样本超 2 万份…

2025年激光切割机供货商权威推荐榜单:机器人激光切割机/三维五轴激光切割机/皮秒激光切割机源头厂家精选

随着制造业智能化升级的持续推进,激光切割机作为工业加工的核心设备,其市场需求显著增长。据行业数据显示,2024年中国激光设备市场规模已突破800亿元,年增长率保持在10%以上,其中激光切割机在工业应用中的占比约4…

HT-LFCG-1525+:DC-1525M SMD-8Pin LTCC低通滤波器

HT-LFCG-1525+:DC-1525M SMD-8Pin LTCC低通滤波器在射频前端方案不断向高集成、高一致性演进的当下,HT-LFCG-1525+,以替代久经市场考验的LFCG-1525+。新品沿用8引脚表贴封装,尺寸兼容原有焊盘,无需更改PCB即可直接…

11-5 降压电路

好的,我们来一步步分析您描述的这个电路。 首先,根据您的描述,我绘制了以下电路图,以便我们更清晰地进行分析: circuit TDVCC_24V["+24V"] --> R1_240k["R1: 240kΩ"]VCC_24V --> R2_1…

【IEEE出版|上海海事大学主办】第六届智能电网与能源工程国际学术会议

第六届智能电网与能源工程国际学术会议将于2025年11月28日-30日在中国上海召开。会议旨在为智能电网、能源系统等领域的专家学者及企业发展人提供一个分享研究成果、讨论存在的问题与挑战、探索前沿科技的国际性合作交…

MySQL用户管理 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

LLM 时代,DataAgent WhaleTunnel 如何将数据库变更瞬时 “转译” 为洞察?

在软件世界中,用户的形态正在发生变化。在软件世界中,用户的形态正在发生变化。 过去,软件的使用者是工程师、分析师或运维人员;而如今,他们正在被一群“数字化身”——Agent 所取代。AI 不再只是一个算法模型,而…

windows不显示欢迎界面

当netplwiz工具中缺少取消开机密码的选项时 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device 修改键值:双击 DevicePasswordLessBuildVersion,将其数值数据从默认的 2 改为 0…

深入解析:C语言字符串安全查找 :strchr_s、strrchr_s、strstr_s

深入解析:C语言字符串安全查找 :strchr_s、strrchr_s、strstr_spre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "…

2025年靠谱的连锁泡菜加盟公司排名,泡菜加盟正规公司推荐

2025年餐饮加盟市场持续升温,泡菜品类凭借酸辣开胃的风味、灵活的消费场景,成为创业者青睐的赛道。但行业中加盟品牌鱼龙混杂:部分品牌无核心技术沉淀,产品同质化严重;部分快招公司圈钱跑路,让创业者血本无归;还…

2025年消雾装置冷却塔供货厂家权威推荐:消雾冷却塔/消雾冷却塔选型/消雾冷却塔变频源头厂家精选

随着国家环保政策收紧和工业节水需求提升,消雾装置冷却塔市场年增长率已连续三年超过15%,其中高效节水型产品占据市场份额的45%以上。 消雾装置冷却塔作为工业循环水系统的核心设备,正朝着高效消雾、节能降噪、智能…