我想从MP4视频文件中提取所有帧,并在PictureBox上显示它们。
原始代码来自这个Q&A:How can I time the presentation and extraction of frames from a video file?
单击行上的开始按钮后发生异常:
var frame = videoReader.ReadVideoFrame();
消息
System.ArgumentException
HResult=0x80070057
Message=Parameter is not valid.
Source=System.Drawing
StackTrace:
at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format)
at Accord.Video.FFMPEG.VideoFileReader.DecodeVideoFrame(BitmapData bitmapData)
at Accord.Video.FFMPEG.VideoFileReader.readVideoFrame(Int32 frameIndex, BitmapData output)
at Accord.Video.FFMPEG.VideoFileReader.ReadVideoFrame()
at Extract_Frames.Form1.<GetVideoFramesAsync>d__15.MoveNext() in D:\Csharp Projects\Extract Frames\Form1.cs:line 114
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Extract_Frames.Form1.<buttonStart_Click>d__17.MoveNext() in D:\Csharp Projects\Extract Frames\Form1.cs:line 151
完整代码
using Accord.IO;
using Accord.Video;
using Accord.Video.FFMPEG;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Extract_Frames
{
public partial class Form1 : Form
{
Bitmap frame = null;
Graphics frameGraphics = null;
bool isVideoRunning = false;
IProgress<Bitmap> videoProgress = null;
private CancellationTokenSource cts = null;
private readonly object syncRoot = new object();
private static long pause = 0;
private int frameRate = 0;
private List<Bitmap> frames = new List<Bitmap>();
string fileName;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void StopPlayback(bool cancel)
{
lock (syncRoot)
{
if (cancel) cts?.Cancel();
cts?.Dispose();
cts = null;
}
}
int counter =1;
private void Updater(Bitmap videoFrame)
{
frames.Add(videoFrame);
label1.Text = "Current Frame Number : " + counter;
trackBar1.Value = counter;
counter++;
//Size size = new Size(videoFrame.Width, videoFrame.Height);
//pictureBox1.ClientSize = size;
using (videoFrame) frameGraphics.DrawImage(videoFrame, Point.Empty);
pictureBox1.Invalidate();
}
private async Task GetVideoFramesAsync(IProgress<Bitmap> updater, string fileName, int intervalMs, CancellationToken token = default)
{
using (var videoReader = new VideoFileReader())
{
if (token.IsCancellationRequested) return;
videoReader.Open(fileName);
videoReader.ReadVideoFrame(1);
trackBar1.Value = 1;
label1.Text = "Current Frame Number : " + counter.ToString();
while (true)
{
if (Interlocked.Read(ref pause) == 0)
{
var frame = videoReader.ReadVideoFrame();
if (token.IsCancellationRequested || frame is null) break;
updater.Report(frame);
}
await Task.Delay(frameRate).ConfigureAwait(false);
}
}
}
private void trackBar2_Scroll(object sender, EventArgs e)
{
frameRate = trackBar2.Value / 25;
}
private async void buttonStart_Click(object sender, EventArgs e)
{
string fileName = textBox1.Text;
if (isVideoRunning) return;
isVideoRunning = true;
using (var videoReader = new VideoFileReader())
{
videoReader.Open(fileName);
frame = new Bitmap(videoReader.Width + 2, videoReader.Height + 2);
trackBar1.Maximum = (int)videoReader.FrameCount;
}
videoProgress = new Progress<Bitmap>((bitmap) => Updater(bitmap));
cts = new CancellationTokenSource();
pictureBox1.Image = frame;
try
{
frameGraphics = Graphics.FromImage(frame);
// Set the fame rate to 25 frames per second
//int frameRate = 1000 / 25;
await GetVideoFramesAsync(videoProgress, fileName, frameRate, cts.Token);
}
finally
{
frameGraphics?.Dispose();
StopPlayback(false);
isVideoRunning = false;
}
}
private void buttonPause_Click(object sender, EventArgs e)
{
if (pause == 0)
{
buttonPause.Text = "Resume";
Interlocked.Increment(ref pause);
}
else
{
Interlocked.Decrement(ref pause);
buttonPause.Text = "Pause";
}
}
private void buttonStop_Click(object sender, EventArgs e)
{
StopPlayback(true);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (isVideoRunning) StopPlayback(true);
pictureBox1.Image?.Dispose();
base.OnFormClosing(e);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, pictureBox1.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
pictureBox1.Image = frames[trackBar1.Value];
}
private void button1_Click(object sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.Filter = "video files (*.mp4)|*.mp4|All files (*.*)|*.*";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
// Get the path of specified file
textBox1.Text = openFileDialog.FileName;
}
}
}
}
}
1条答案
按热度按时间zd287kbt1#
您可以使用名为Emgu CV的库来实现这一点,但由于Emgu CV使用名为Mat的容器来存储图像的位图,因此您需要定义一个Mat列表,并循环遍历视频中的帧并将其添加到列表中。第一步是安装一个名为Emgu.cv.runtime.windows的NuGet包,然后将下面的代码放在触发阅读过程的事件处理程序中。