0

I am writing a C# WPF application, I am using the AForge library for video streaming. I wanted to deploy the application, because everything worked on the first pc.

Than I deployed it and on the other pc I get the following Error:

"Must create DependencySource on same Thread as the DependencyObject even by using Dispatcher"

This is the source Code, I am calling it on every new Frame which I get from the WebCam

private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)

{ if (StreamRunning) { try { using (var bitmap = (Bitmap)eventArgs.Frame.Clone()) {

                    Image = ToBitmapImage(bitmap);
                }
                Image.Freeze();
            }
            catch (Exception e)
            {
                UIMessages = "Error: NewFrame " + e.Message;
            }
        }

}

ToBitmapImage Method:

        private BitmapImage ToBitmapImage(Bitmap bitmap)
    {

        var start = 420;
        var end = 1920 - 2* 420;
        BitmapImage bi = new BitmapImage();
        bi.BeginInit();
        MemoryStream ms = new MemoryStream();
        Bitmap source = bitmap;
        Bitmap CroppedImage = source.Clone(new System.Drawing.Rectangle(start, 0, end, 1080), source.PixelFormat);          
        CroppedImage.Save(ms, ImageFormat.Bmp);

        ms.Seek(0, SeekOrigin.Begin);
        bi.StreamSource = ms;
        bi.EndInit();
        return bi;
    }

Further code:

 private BitmapImage _image;
 public BitmapImage Image {
        get => _image;
        set
        {
            _image = value;
            OnPropertyChanged();
        }
    }

The start of the camera:

            if (SelectedDevice != null)
            {
                _videoSource = new VideoCaptureDevice(SelectedDevice.MonikerString);
                //var test = _videoSource.VideoCapabilities;
                _videoSource.NewFrame += video_NewFrame;
                _videoSource.Start();


            }

UI:

 <Image Height="400" Width="400" Source="{Binding Image, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,0"/>

I also tried this out:

 Dispatcher.CurrentDispatcher.Invoke(() => Image = ToBitmapImage(bitmap));
Yessir
  • 183
  • 3
  • 13
  • 1
    Is Image a dependency property? As a general note, you should clean up your code. Why mix `var` and explict variable types? Why mix camelCase and PascalCase identifier names? Why do you call Freeze outside the ToBitmapImage method? – Clemens Jun 04 '20 at 16:52
  • It seems also pointless to call `Frame.Clone()` when you later call `source.Clone()`. The first call is totally redundant. – Clemens Jun 04 '20 at 17:13
  • And `Dispatcher.CurrentDispatcher` is also wrong. It should be `Dispatcher.Invoke` when you are in the method of a DependencyObject, otherwise `Application.Current.Dispatcher.Invoke`. – Clemens Jun 04 '20 at 17:16
  • The second source.Clone is because I only want to use a part of the image – Yessir Jun 04 '20 at 22:31
  • Sure, that's obvious, but the first call is still redundant. What about the Image property? Is it a dependency property? – Clemens Jun 05 '20 at 05:50
  • "The calling thread cannot access this object because a different thread owns it" , this error occurs, if I us Application.Current.Dispatcher.Invoke – Yessir Jun 05 '20 at 07:25
  • The strange thing is, that it is working on my other pc without any problems. – Yessir Jun 05 '20 at 07:30
  • As you said, if I call Freeze in the ToBitMap image function it works, but I don't know why. It is somehow very slow, the camera source is not updated live, it is about one second later. I think I messed up something – Yessir Jun 05 '20 at 07:43
  • My other laptop is not as powerful and this is the cause, I read it here: https://stackoverflow.com/questions/2006055/implementing-a-webcam-on-a-wpf-app-using-aforge-net/41895924 the answer of Patratacus – Yessir Jun 05 '20 at 08:30
  • Please see my answer for how the method should look like. Always set BitmapCacheOption.OnLoad and dispose of the source Stream. – Clemens Jun 05 '20 at 08:42

1 Answers1

0

When loading a BitmapImage from a Stream, you would usually close the Stream as soon as possible and make sure the BitmapImage is loaded immediately, by setting BitmapCacheOption.OnLoad.

private static BitmapImage ToBitmapImage(Bitmap bitmap)
{
    var start = 420;
    var end = 1920 - 2 * 420;

    var croppedBitmap = bitmap.Clone(
        new System.Drawing.Rectangle(start, 0, end, 1080),
        bitmap.PixelFormat);          

    var bi = new BitmapImage();

    using (var ms = new MemoryStream())
    {
        croppedBitmap.Save(ms, ImageFormat.Bmp);
        ms.Seek(0, SeekOrigin.Begin);

        bi.BeginInit();
        bi.CacheOption = BitmapCacheOption.OnLoad;
        bi.StreamSource = ms;
        bi.EndInit();
    }

    bi.Freeze();

    return bi;
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • This fixed my problem, but it still takes a lot of CPU, do you know a way to improve it? – Yessir Jun 05 '20 at 09:08
  • Something like this: https://stackoverflow.com/a/30729291/1136211. It would avoid encoding and decoding a BMP, and you may perhaps avoid bitmap.Clone. – Clemens Jun 05 '20 at 09:11
  • But there is the problem that I get a BitmapSource and not an BitmapImage – Yessir Jun 05 '20 at 09:25
  • Most certainly you don't need a BitmapImage, but just the base class BitmapSource. For assigning it to the Source property of an Image element, even the base class ImageSource would be sufficient. Just declare your Image property with type ImageSource of BitmapSource. – Clemens Jun 05 '20 at 09:54
  • Thanks, it really helped a lot and is now much more efficient :) – Yessir Jun 05 '20 at 10:08