How to correctly show webcam stream in a WPF application with OpenCvSharp

1

I'm working with .NET Framework 4.8 and NuGet libraries: OpenCvSharp4, OpenCvSharp4.runtime.win, OpenCvSharp4.Windows and I'm trying to code a simple WebcamStreaming class that takes as input a WPF System.Windows.Controls.Image and exposes some Start and Stop methods to show the camera output.

I did it, and it works, but there are two things I'm not sure about:

  1. CPU gets very high while streaming, around 30/40%. I think it is the VideoCapture read frames loop, so I put an await Task.Delay(10) to free some resources on each cycle and it is working. Now the CPU is always < 10%. But is this the correct solution? Am I doing something wrong earlier?

  2. When I close the application, I get always this exception: Managed Debugging Assistant 'DisconnectedContext' : 'Transition into COM context 0x12f6648 for this RuntimeCallableWrapper failed with the following error: The object invoked has disconnected from its clients. (Exception from HRESULT: 0x80010108 (RPC_E_DISCONNECTED)). This is typically because the COM context 0x12f6648 where this RuntimeCallableWrapper was created has been disconnected or it is busy doing something else. Releasing the interfaces from the current COM context (COM context 0x12f6700). This may cause corruption or data loss. To avoid this problem, please ensure that all COM contexts/apartments/threads stay alive and are available for context transition, until the application is completely done with the RuntimeCallableWrappers that represents COM components that live inside them.'. Do you know what is it and what I'm doing wrong?


The code

You can find the whole example on a GitHub repository I've created.

The interesting part of the frames fetch loop:

_previewTask = Task.Run(async () =>
{
    using (var frame = new Mat())
    {
        while (!_cancellationTokenSource.IsCancellationRequested)
        {
            _videoCapture.Read(frame);

            if (!frame.Empty())
            {
                _lastFrame = BitmapConverter.ToBitmap(frame);
                var lastFrameBitmapImage = _lastFrame.ToBitmapSource();
                lastFrameBitmapImage.Freeze();

                _imageControlForRendering.Dispatcher.Invoke(() => _imageControlForRendering.Source = lastFrameBitmapImage);
                await Task.Delay(10);
            }
        }
    }
}, _cancellationTokenSource.Token);

The disposal process (I call it when the application gets closed):

public void Dispose()
{
    _cancellationTokenSource?.Cancel();
    if (_videoCapture?.IsDisposed == false)
    {
        _videoCapture?.Dispose();
        _videoCapture = null;
    }
    _lastFrame?.Dispose();
}

Update for the second point

The problem was caused by the fact that the thread in which I was calling Dispose wasn't the same thread in which I created VideoCapture.

c#
wpf
opencv
opencvsharp
asked on Stack Overflow Jun 1, 2020 by Francesco Bonizzi • edited Jun 4, 2020 by Francesco Bonizzi

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0