2

I have a wpf application, It has mainWindow that creates _otherWindow that displays on a secondary monitor. Both windows have elements that need to change along with time. (mainWindow updates an Image and also plots a graph, finally _otherWindow updates a shape position depending on some computations).

What is my problem? well, I am reading a video frame by frame within a Thread (however I would like to allow this with stream taken with a camera). And as I update GUI every frame in a certain time, Application is getting a heavy load and is getting slow...

I realized that commenting either mainWindow updating Image, or commenting _otherWindow updating shape position codes make the application run nice, but the issue is when they run together.

Here is a detailed description

First I compute some things inside _otherWindow and compute position of a shape. Then I compute some stuff related to image and the update frame adding some stuff to bitmap Then I update position of shape inside _otherWindow Finally I plot results (the plot needs data gotten from mainWindow and _otherWindow) For this, I use tasks and wait for them.

I have this:

private Thread _camera;
private void CaptureVideo()
{
    _camera = new Thread(CaptureVideoCallback)
    {
        Priority = ThreadPriority.Highest
    };
    _camera.Start();
}


private VideoCapture _capture;
private void CaptureVideoCallback()
{
    //some computing here read from a video file...
     _capture = new VideoCapture("someVideo.mp4");
    for (var i = 0; i < _capture.FrameCount; i++)
    {
        _capture.Read(_frame);
        if (_frame.Empty()) return;

        //*************task that does heavy computation in other class
        var heavyTaskOutput1 = Task.Factory.StartNew(() =>
            {
                _otherWindow.Dispatcher.Invoke(() =>
                {
                    ResultFromHeavyComputationMethod1 = _otherWindow.HeavyComputationMethod1();
                });
            }
        );
                
        ////*************task that does heavy computation in current class  
        var heavyTaskOutput2 = Task.Factory.StartNew(() =>
        {
            ResultFromHeavyComputationMethod2 = HeavyComputationMethod2(ref _frame);
            var bitmap = getBitmapFromHeavyComputationMethod2();
            bitmap.Freeze();
            //update GUI in main thread
            Dispatcher.CurrentDispatcher.Invoke(() => ImageSource = bitmap);
        });

        ////*************wait both task to complete     
        Task.WaitAll(heavyTaskOutput1, heavyTaskOutput2 );
        
        //update _otherWindow GUI 
        var outputGui = Task.Factory.StartNew(() =>
            {
                _otherWindow.Dispatcher.Invoke(() =>
                {
                    _otherWindow.UpdateGui();
                });
            }
        );
        outputGui.Wait();

        
        ////*************plot in a char using gotten results, UPDATE GUI
        Task.Run(() =>
        {
            PlotHorizontal();
        });    
    }
} 

What would be a good way to speed this up? I mean I know that GUI stuff need to be done on main thread, but this is slowing down things.

Edit

Have changed code as Clemens suggested:

//*************task that does heavy computation in other class
var heavyTaskOutput1 = Task.Run(() =>
    {
        ResultFromHeavyComputationMethod1 = _otherWindow.HeavyComputationMethod1();
    }
);
        
////*************task that does heavy computation in current class  
var heavyTaskOutput2 = Task.Run(() =>
{
    ResultFromHeavyComputationMethod2 = HeavyComputationMethod2(ref _frame);
    var bitmap = getBitmapFromHeavyComputationMethod2();
    bitmap.Freeze();
    //update GUI in main thread
    Dispatcher.CurrentDispatcher.Invoke(() => ImageSource = bitmap);
});

////*************wait both task to complete     
Task.WaitAll(heavyTaskOutput1, heavyTaskOutput2);

//update _otherWindow GUI 
var outputGui = Task.Run(() =>
    {
        _otherWindow.Dispatcher.Invoke(() =>
        {
            _otherWindow.UpdateGui();
        });
    }
);
outputGui.Wait();
Community
  • 1
  • 1
edgarmtze
  • 24,683
  • 80
  • 235
  • 386
  • `heavyTaskOutput1` seems pointless when it does nothing but `Dispatcher.Invoke`. Perhaps call `_otherWindow.HeavyComputationMethod1` before `Invoke`. You may also want to use `Task.Run` instead of `Task.Factory.StartNew`. See e.g. here: https://stackoverflow.com/questions/38423472/what-is-the-difference-between-task-run-and-task-factory-startnew – Clemens Jun 12 '19 at 08:53
  • Ok I have added the changes suggested. Is any possibility of further improvement? – edgarmtze Jun 12 '19 at 17:24

1 Answers1

1

It's a bit hard to guess. Do you have Visual Studio? I think even the Community edition has some profiling capabilities (menu: Analyze/Performance Profiler...). That may point out some non-obvious bottlenecks.

My thoughts:

  1. getBitmapFromHeavyComputationMethod2 appears to return a new bitmap every time through. I can't infer the actual type it's returning, but it likely involves a semi-large un-managed memory allocation and implements IDisposable. You might check on whether you're disposing that appropriately.

  2. Rather than create a new bitmap for every frame, can you use a WriteableBitmap? Be sure to lock and unlock it if you do. Perhaps ping-pong (alternate) between two bitmaps if you need to.

  3. It appears you may be serializing your "heavy computation" with your I/O read (first one, then the other). Perhaps launch the read as an async as well, and wait on it in your WaitAll so that the computation and I/O can happen concurrently. Something in this shape:

var readResult = _capture.Read(_frame);
for (...) {
    // check read result
    // ...
    // launch heavy computation

    readResult = Task.Run(() => _capture.Read(nextFrame);
    Task.WaitAll(pupilOutput, outputTest, readResult);
    _frame = nextFrame;
}

Note this would read N+1 times for N frames--maybe your Read method is okay with that.

Curt Nichols
  • 2,757
  • 1
  • 17
  • 24
  • Actually for the `writeableBitmap` I apply some changes to `_frame` (something like adding circles and other shapes to some roi) then: `var bitmap = _frame.ToWriteableBitmap(PixelFormats.Bgr24); bitmap.Freeze();` this is because `_frame` needs to be converted so I can display it in `wpf Image` like: `` The suggestion about reading would be nice, the only issue I see is if in real time stream, I would need to read, then read read again and show image? – edgarmtze Jun 12 '19 at 18:46
  • As I am using Opencvsharp to capture video and manipulate frames, here is the converter https://github.com/shimat/opencvsharp/blob/master/src/OpenCvSharp.Extensions/WriteableBitmapConverter.cs – edgarmtze Jun 12 '19 at 18:54