Home > Net >  Converting from Image to Imagesource using WPF, Caliburn and C#
Converting from Image to Imagesource using WPF, Caliburn and C#

Time:12-18

I'm trying to setup a webcamstream in WPF. I had a project working, where everything was code behind. However, I'd love to make it cleaner and convert it to MVVM. My problem is that, I cannot bind the image from my stream to the view, and gets the following error:

Binding path Cameraimage, Taget Image.Source, Target Type ImageSource Without the dot

Cannot convert from type "System.Windows.Controls.Image" to "System.Windows.Media.ImageSource". Consider setting a converter on the binding.

The main deal of this part of the program is View.xaml ViewModel.cs and a utility CameraStreaming.cs - (It's obviously called something else than view and viewmodel, but I'm trying to keep it simple)

View.xaml-snippet

    <Border
        x:Name="webcamContainer"
        Grid.Column="0"
        Grid.Row="3"
        BorderBrush="Black"
        BorderThickness="1">

        <Image Source="{Binding CameraImage}"  VerticalAlignment="Top" />
        
    </Border>

ViewModel.cs-snippet

private Image _cameraImage;

    public Image CameraImage
    {
        get { return _cameraImage; }
        set { _cameraImage = value;
            NotifyOfPropertyChange(() => CameraImage);
    }
(....)
    var selectedCameraDeviceId = SelectedCamera.OpenCvId;

            if (_camera == null || _camera.CameraDeviceId != selectedCameraDeviceId)
            {
                _camera?.Dispose();
                
                _camera = new CameraStreaming(

                    imageControlForRendering: CameraImage, // See my CameraStreaming utility
                    cameraDeviceId: SelectedCamera.OpenCvId);
                
            }

            try
            {
                await _camera.StartCamera(true);
               // await _ultraSound.Start(false);

            }

CameraStreaming.cs-snippet

private readonly Image _imageControlForRendering;

public CameraStreaming(Image imageControlForRendering, int cameraDeviceId)
    {
        _imageControlForRendering = imageControlForRendering;
        CameraDeviceId = cameraDeviceId;

    }
 public async Task StartCamera(bool crop)
    {
        
        if (_previewTask != null && !_previewTask.IsCompleted)
            return;

        var initializationSemaphore = new SemaphoreSlim(0, 1);

        _cancellationTokenSource = new CancellationTokenSource();
        _previewTask = Task.Run(async () =>
        {
            try
            {
               var videoCapture = new VideoCapture();

                if (!videoCapture.Open(CameraDeviceId))
                {
                    throw new ApplicationException("Cannot connect to camera");
                }

                using (var frame = new Mat())
                {
                    while (!_cancellationTokenSource.IsCancellationRequested)
                    {
                        videoCapture.Read(frame);

                        if (!frame.Empty())
                        {
                            // Releases the lock on first not empty frame
                            if (initializationSemaphore != null)
                                initializationSemaphore.Release();


                            // _lastFrame = BitmapConverter.ToBitmap(frame);


                                _lastFrame = BitmapConverter.ToBitmap(frame.Flip(FlipMode.Y));

                            var lastFrameBitmapImage = _lastFrame.ToBitmapSource();
                            lastFrameBitmapImage.Freeze();
                            _imageControlForRendering.Dispatcher.Invoke(
                                () => _imageControlForRendering.Source = lastFrameBitmapImage);
                        }

                        
                        await Task.Delay(10);
                    }
                }

                videoCapture?.Dispose();
            }
            finally
            {
                if (initializationSemaphore != null)
                    initializationSemaphore.Release();
            }

        }, _cancellationTokenSource.Token);


        await initializationSemaphore.WaitAsync();
        initializationSemaphore.Dispose();
        initializationSemaphore = null;

        if (_previewTask.IsFaulted)
        {
            // To let the exceptions exit
            await _previewTask;
        }
    }

So.. I'm at a lost. Most of the official documentation is down due to the log4j-issue at the moment, and I think I might have become blind after looking at it for so long :P

CodePudding user response:

You must not use System.Windows.Controls.Image as the type of the image property in your view model for two reasons.

  1. It is a UIElement and as such does not belong into a view model
  2. It can not be assigned to the Source property of another Image element in the view

Change the type to ImageSource

private ImageSource _cameraImage;

public ImageSource CameraImage
{
    get { return _cameraImage; }
    set
    {
        _cameraImage = value;
        NotifyOfPropertyChange(() => CameraImage);
    }
}

and update the view model with a new camera frame:

var lastFrameBitmapSource = _lastFrame.ToBitmapSource();
lastFrameBitmapSource.Freeze();

viewModel.CameraImage = lastFrameBitmapSource;

If you don't want to pass a reference to the view model to the helper class, you would need another notification mechanism.

  • Related