【问题标题】:Exception when release resources from bitmap used for dynamic PictureBox从用于动态 PictureBox 的位图释放资源时出现异常
【发布时间】:2015-08-23 15:41:26
【问题描述】:

我想使用尽可能少的 RAM - 使用 Windows 窗体和 Aforge.Net 来捕获视频。 问题是,当我尝试例如“.Dispose()”某些元素时出现异常:

    static void Main()
    {
            Console.WriteLine("Main");
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());  <----- here VS showing me an exception (An unhandled exception of type 'System.InvalidOperationException' occurred in System.Drawing.dll)
    }

执行此操作的代码:

    public void setLocalCamera(string cameraName)
    {
        videoSource = new VideoCaptureDevice(cameraName);
        videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
        videoSource.Start();
    }


    Bitmap bitmap;
    private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        if (bitmap != null) 
            bitmap.Dispose(); <--- here is the problematic code

        bitmap = (Bitmap)eventArgs.Frame.Clone();
        pB_Camera.Image = bitmap;

    }

此代码会导致随机时间异常(我不知道究竟是什么触发了它)。我还尝试了一些我发现但没有任何工作/帮助的其他解决方案(比如在 pB_Camera.Image = bitmap; 之后 make bitmap.Dispose(); 但这也是一个例外)

如何解决此问题并尽可能多地释放 RAM?

【问题讨论】:

  • 您必须处理您不再需要的 old 位图。不是你要展示的那个。所以只需使用 if (pB_Camera.Image != null) pB_Camera.Image.Dispose();
  • 当我尝试您的代码时,我在同一个地方遇到了异常:System.Drawing.dll 中发生了“System.InvalidOperationException”类型的未处理异常附加信息:对象当前正在其他地方使用。
  • 您可以先将pB_Camera.Image设置为NULL,然后处理位图并重新分配给pB_Camera
  • 我在开头添加了这样的内容: pB_Camera.Image = null;if (bitmap != null) bitmap.Dispose();我没有看到任何错误,RAM 使用量约为 100 mb(OK)。但是有个小问题。每隔几秒钟我注意到位图上闪烁的白色图像(不是来自相机的图像)。

标签: c# winforms bitmap aforge


【解决方案1】:

同时避免异常和闪烁的正确顺序是:

Bitmap bitmap;
void SetBitmap(Bitmap value)
{
    pB_Camera.Image = value;
    if (bitmap != null) bitmap.Dispose();
    bitmap = value;
}

而且用法很简单:

private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    SetBitmap((Bitmap)eventArgs.Frame.Clone());
}

编辑:但是,您的具体情况略有不同,因为该事件是在非 UI 线程上引发的。虽然一个简单的Control.Invoke 可以工作,但如果框架仅用于显示,我认为最好实现一点“单项生产者/消费者”非阻塞模式,以便让工作线程在你的同时继续处理正在消费图像,如下所示:

private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    PushNextFrame((Bitmap)eventArgs.Frame.Clone());
}

Bitmap nextFrame;

void PushNextFrame(Bitmap value)
{
    var prevFrame = Interlocked.Exchange(ref nextFrame, value);
    if (prevFrame != null)
    {
        // previous frame has not been consumed, so just discard it.
        // no need to notify the consumer, because it should have been done already.
        prevFrame.Dispose();
    }
    else
    {
        // previos frame has been consumed and we just set a new one, so we need to notity the consumer.
        BeginInvoke((Action)OnNextFrameAvailable);
    }
}

Bitmap PullNextFrame() { return Interlocked.Exchange(ref nextFrame, null); }

void OnNextFrameAvailable()
{
    DisplayFrame(PullNextFrame());
}

Bitmap currentFrame;

void DisplayFrame(Bitmap value)
{
    pb_Camera.Image = value;
    if (currentFrame != null) currentFrame.Dispose();
    currentFrame = value;
}

【讨论】:

  • 现在我测试这个解决方案,但我改变了 [pB_Camera.Image = value;]-> this.Invoke((MethodInvoker)delegate {pB_Camera.Image = value; });因为有与@up“对象当前在其他地方使用”相同的例外。这使得静态 ram Usage (52.6) 并且现在没有错误。我不认为我需要在该对象存在的主 Form1 中使用调用方法。我仍然会进行测试以确保此解决方案一切正常。如果有任何其他更好的解决方案或我的错误,请告诉我。我会检查一切。
  • 现在我将检查您的第二个选项。现在它还可以(只是更多的 RAM 使用量 - 但不多)。如果我的所有测试都是肯定的,或者我会发现其他问题,我会更新这个主题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多