在 C# 中将所有视频帧提取为图像的最快方法是什么?
What is the fastest way to extract all frames of video into images in C#?
我创建了一个应用程序来提取视频中的每一帧。它还不完美,并且有很多问题。问题之一是需要多长时间。 3分钟的视频提取帧花了整整2个小时,原来的视频是4分钟,但在视频快结束时给我一个记忆错误:
'OpenCV: Failed to allocate 2764800 bytes'
所以,完成任务需要很长时间,而且有一些错误
我正在使用 Emgu.CV 和 MediaToolkit
这是我的代码:
using Emgu.CV;
using MediaToolkit;
using MediaToolkit.Model;
string file;
private void CreateFrames()
{
int i = 0;
VideoCapture _video = new VideoCapture(file);
var videoInfo = new MediaFile { Filename = file };
using (var engine = new Engine()) { engine.GetMetadata(videoInfo); }
while (i <= videoInfo.Metadata.Duration.Milliseconds * videoInfo.Metadata.VideoData.Fps)
{
Mat m = new Mat();
_video.Read(m);
_video.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.PosFrames, i);
preview.Image = _video.QueryFrame().ToBitmap();
Bitmap bitmap = _video.QueryFrame().ToBitmap();
bitmap.Save(String.Format(@"C:\frames\videoframe-{0}.png", i),
ImageFormat.Png);
i++;
}
}
private async void browse_Click(object sender, EventArgs e)
{
DialogResult result = openFileDialog.ShowDialog();
if (result == DialogResult.OK)
{
file = openFileDialog.FileName;
var videoInfo = new MediaFile { Filename = file };
using (var engine = new Engine()) { engine.GetMetadata(videoInfo); }
label1.Text = videoInfo.Metadata.VideoData.Fps + " \n" +
videoInfo.Metadata.Duration.ToString();
// must be run on a second thread else it will not work and it will freeze
Task getframes = new Task(CreateFrames);
getframes.Start();
}
}
如果有人有解决方案来加快速度并可能解决内存问题,我将不胜感激。
首先,检查没有 "using" 的 IDisposable 对象。例如,Bitmap 继承自 Image,它实现了 IDisposable。所以,当使用bimap时,你的代码应该是这样的:
using (Bitmap bitmap = _video.QueryFrame().ToBitmap())
{
bitmap.Save(String.Format(@"C:\frames\videoframe-{0}.png", i), ImageFormat.Png);
}
同时检查 Mat class 是否实现了 IDisposable。如果是这样,另一种用法可以帮助您解决内存问题。
对于性能问题,我真的没办法,但是3分钟的视频,60fps,是10800帧。 2小时是7200秒。所以每帧 0.6 秒。这真的很慢吗?随便问问,我不知道...
这是一个仅使用 Emgu.CV
的实现(我不熟悉 MediaToolKit
)。
using (var video = new VideoCapture(file))
using (var img = new Mat())
{
while (video.Grab())
{
video.Retrieve(img);
var filename = Path.Combine(outputDirectory, $"{i}.png");
CvInvoke.Imwrite(filename, img);
i++;
}
}
我在全高清 mp4
视频上对此进行了测试。不知道确切的编解码器。我的 CPU (i7-8700k) 每帧大约需要 65 毫秒。但是请考虑是否需要使用 png
格式,因为它的编码速度很慢。如果您不希望有任何压缩损失,并且您的硬盘速度足够快且足够大 bmp
,那么每帧只需 13 毫秒。 jpg
需要 40 毫秒。
我创建了一个应用程序来提取视频中的每一帧。它还不完美,并且有很多问题。问题之一是需要多长时间。 3分钟的视频提取帧花了整整2个小时,原来的视频是4分钟,但在视频快结束时给我一个记忆错误:
'OpenCV: Failed to allocate 2764800 bytes'
所以,完成任务需要很长时间,而且有一些错误 我正在使用 Emgu.CV 和 MediaToolkit
这是我的代码:
using Emgu.CV;
using MediaToolkit;
using MediaToolkit.Model;
string file;
private void CreateFrames()
{
int i = 0;
VideoCapture _video = new VideoCapture(file);
var videoInfo = new MediaFile { Filename = file };
using (var engine = new Engine()) { engine.GetMetadata(videoInfo); }
while (i <= videoInfo.Metadata.Duration.Milliseconds * videoInfo.Metadata.VideoData.Fps)
{
Mat m = new Mat();
_video.Read(m);
_video.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.PosFrames, i);
preview.Image = _video.QueryFrame().ToBitmap();
Bitmap bitmap = _video.QueryFrame().ToBitmap();
bitmap.Save(String.Format(@"C:\frames\videoframe-{0}.png", i),
ImageFormat.Png);
i++;
}
}
private async void browse_Click(object sender, EventArgs e)
{
DialogResult result = openFileDialog.ShowDialog();
if (result == DialogResult.OK)
{
file = openFileDialog.FileName;
var videoInfo = new MediaFile { Filename = file };
using (var engine = new Engine()) { engine.GetMetadata(videoInfo); }
label1.Text = videoInfo.Metadata.VideoData.Fps + " \n" +
videoInfo.Metadata.Duration.ToString();
// must be run on a second thread else it will not work and it will freeze
Task getframes = new Task(CreateFrames);
getframes.Start();
}
}
如果有人有解决方案来加快速度并可能解决内存问题,我将不胜感激。
首先,检查没有 "using" 的 IDisposable 对象。例如,Bitmap 继承自 Image,它实现了 IDisposable。所以,当使用bimap时,你的代码应该是这样的:
using (Bitmap bitmap = _video.QueryFrame().ToBitmap())
{
bitmap.Save(String.Format(@"C:\frames\videoframe-{0}.png", i), ImageFormat.Png);
}
同时检查 Mat class 是否实现了 IDisposable。如果是这样,另一种用法可以帮助您解决内存问题。
对于性能问题,我真的没办法,但是3分钟的视频,60fps,是10800帧。 2小时是7200秒。所以每帧 0.6 秒。这真的很慢吗?随便问问,我不知道...
这是一个仅使用 Emgu.CV
的实现(我不熟悉 MediaToolKit
)。
using (var video = new VideoCapture(file))
using (var img = new Mat())
{
while (video.Grab())
{
video.Retrieve(img);
var filename = Path.Combine(outputDirectory, $"{i}.png");
CvInvoke.Imwrite(filename, img);
i++;
}
}
我在全高清 mp4
视频上对此进行了测试。不知道确切的编解码器。我的 CPU (i7-8700k) 每帧大约需要 65 毫秒。但是请考虑是否需要使用 png
格式,因为它的编码速度很慢。如果您不希望有任何压缩损失,并且您的硬盘速度足够快且足够大 bmp
,那么每帧只需 13 毫秒。 jpg
需要 40 毫秒。