You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Emgu CV异步方法无法向List添加视频帧的问题求助

问题修复方案

以下是针对你的Emgu CV视频帧读取问题的具体修复步骤:


1. 修正帧读取逻辑(核心问题)

你的ReadFrames方法中同时调用Grab()和QueryFrame()是错误的——QueryFrame()内部已经包含了Grab()操作,这会导致帧跳过甚至无法读取到任何帧。同时,QueryFrame()返回的Mat是复用的内部缓冲区,直接添加到列表会导致所有元素指向同一帧,最终被覆盖。

修改后的ReadFrames方法:

private async Task ReadFrames()
{
    if (video != null && video.IsOpened)
    {
        await Task.Run(() =>
        {
            // 重置视频到起始位置
            video.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.PosFrames, 0);
            frames.Clear(); // 清空旧帧

            Mat frame;
            // 直接用QueryFrame循环读取,直到返回null(视频结束)
            while ((frame = video.QueryFrame()) != null)
            {
                // 克隆帧,避免复用内部缓冲区
                frames.Add(frame.Clone());
            }
        });
    }
}

2. 正确更新帧计数与启动定时器

在Application_Idle事件中,任务完成后需要从UI线程更新frameCount,确保计数正确:

private void Application_Idle(object sender, EventArgs e)
{
    if (task != null && task.IsCompleted)
    {
        frameCount = frames.Count; // 从UI线程更新帧计数
        timer.Enabled = true;
        task = null; // 重置任务,避免重复触发
    }
}

3. 修复定时器事件的UI线程安全问题

System.Timers.Timer的Elapsed事件运行在后台线程,直接操作PictureBox会引发跨线程异常。同时需要正确处理图像释放,避免内存泄漏:

private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
{
    if (currentFrame < frameCount)
    {
        // 切换到UI线程更新控件
        pictureBox1.Invoke((MethodInvoker)delegate
        {
            using (var tempBitmap = frames[currentFrame].ToBitmap())
            {
                // 释放旧图像
                pictureBox1.Image?.Dispose();
                pictureBox1.Image = new Bitmap(tempBitmap);
            }
            currentFrame++;
        });
    }
    else
    {
        timer.Enabled = false;
        currentFrame = 0; // 重置索引,方便重播
    }
}

4. 确保任务正确启动

需要在合适的时机(比如按钮点击)启动ReadFrames任务,例如:

private void btnLoadVideo_Click(object sender, EventArgs e)
{
    // 假设此处初始化video对象
    video = new VideoCapture("你的视频路径");
    if (!video.IsOpened)
    {
        MessageBox.Show("视频打开失败");
        return;
    }

    currentFrame = 0;
    frames.Clear();
    task = ReadFrames(); // 启动异步读取任务
}

5. 添加资源清理

在窗体关闭时释放所有资源,避免内存泄漏:

protected override void OnFormClosing(FormClosingEventArgs e)
{
    base.OnFormClosing(e);
    
    // 释放所有Mat对象
    foreach (var mat in frames)
    {
        mat.Dispose();
    }
    frames.Clear();
    
    video?.Dispose();
    timer.Dispose();
}

内容的提问来源于stack exchange,提问作者user16612111

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 15:44:52