How to display webcam images captured with Emgu? - c#

I'm currently working on a project that use Facial Recognition.
I therefore need a way to display the webcam images to the user so he can adjust his face.
I've been trying a lot of things to get images from the webcam using as less CPU as possible:
VideoRendererElement
WPFMediaKit
DirectShow-Lib
But none of them were fine... Either way too slow or too CPU resources consuming.
Then I tried the Emgu library and I felt great about it.
At first, I tried it in a Windows Form project and was updating the image in a Picture Box.
But then, when I tried to integrate it in my WPF Project I got stuck on how to pass my image to my Image control..
Right now, I've the following source code:
<Window x:Class="HA.FacialRecognition.Enroll.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Width="800" Height="600"
Loaded="Window_Loaded" Closing="Window_Closing">
<Grid>
<Image x:Name="webcam" Width="640" Height="480" >
<Image.Clip>
<EllipseGeometry RadiusX="240" RadiusY="240">
<EllipseGeometry.Center>
<Point X="320" Y="240" />
</EllipseGeometry.Center>
</EllipseGeometry>
</Image.Clip>
</Image>
</Grid>
</Window>
And the code behind:
private Capture capture;
private System.Timers.Timer timer;
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
capture = new Capture();
capture.FlipHorizontal = true;
timer = new System.Timers.Timer();
timer.Interval = 15;
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
using (Image<Bgr, byte> frame = capture.QueryFrame())
{
if (frame != null)
{
var bmp = frame.Bitmap;
// How do I pass this bitmap to my Image control called "webcam"?
}
}
}
private void Window_Closing(object sender, CancelEventArgs e)
{
if (capture != null)
{
capture.Dispose();
}
}
My guess was to use BitmapSource/WriteableBitmap but I did not get them working...
Thanks!

Image Class has got a UriSource property that you may be looking for

Look on the Emgu wiki -> Tutorials -> Examples -> WPF (Windows Presentation Foundation)
It contains the following code snippet to convert your IImage to a BitmapSource, which you can directly apply to your control.
using Emgu.CV;
using System.Runtime.InteropServices;
...
/// <summary>
/// Delete a GDI object
/// </summary>
/// <param name="o">The poniter to the GDI object to be deleted</param>
/// <returns></returns>
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
/// <summary>
/// Convert an IImage to a WPF BitmapSource. The result can be used in the Set Property of Image.Source
/// </summary>
/// <param name="image">The Emgu CV Image</param>
/// <returns>The equivalent BitmapSource</returns>
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}

I reckon all you're looking for is this:
Image<Bgr, Byte> frame = capture.QueryFrame();
pictureBox1.Image = image.ToBitmap(pictureBox1.Width, pictureBox1.Height);

If you are using WPF and MVVM here is how you would do it using EMGU.
View:
<Window x:Class="HA.FacialRecognition.Enroll.Views.PhotoCaptureView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Width="800" Height="600">
<Grid>
<Image Width="640" Height="480" Source="{Binding CurrentFrame}">
<Image.Clip>
<EllipseGeometry RadiusX="240" RadiusY="240">
<EllipseGeometry.Center>
<Point X="320" Y="240" />
</EllipseGeometry.Center>
</EllipseGeometry>
</Image.Clip>
</Image>
</Grid>
Viewmodel:
namespace HA.FacialRecognition.Enroll.ViewModels
{
public class PhotoCaptureViewModel : INotifyPropertyChanged
{
public PhotoCaptureViewModel()
{
StartVideo();
}
private DispatcherTimer Timer { get; set; }
private Capture Capture { get; set; }
private BitmapSource _currentFrame;
public BitmapSource CurrentFrame
{
get { return _currentFrame; }
set
{
if (_currentFrame != value)
{
_currentFrame = value;
OnPropertyChanged();
}
}
}
private void StartVideo()
{
Capture = new Capture();
Timer = new DispatcherTimer();
//framerate of 10fps
Timer.Interval = TimeSpan.FromMilliseconds(100);
Timer.Tick += new EventHandler(async (object s, EventArgs a) =>
{
//draw the image obtained from camera
using (Image<Bgr, byte> frame = Capture.QueryFrame())
{
if (frame != null)
{
CurrentFrame = ToBitmapSource(frame);
}
}
});
Timer.Start();
}
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ptr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}
/// <summary>
/// Delete a GDI object
/// </summary>
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
//implementation of INotifyPropertyChanged, viewmodel disposal etc
}

I believe you have to use interop (source):
using System.Windows.Interop;
using System.Windows.Media.Imaging;
public static ImageSource AsImageSource<TColor, TDepth>(
this Image<TColor, TDepth> image) where TColor : IColor, new()
{
return Imaging.CreateBitmapSourceFromHBitmap(image.Bitmap.GetHbitmap(),
IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
Which could be used like this:
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
using (Image<Bgr, byte> frame = capture.QueryFrame())
{
if (frame != null)
{
var bmp = frame.AsImageSource();
}
}
}
If the interop doesn't perform well enough, take a look at the source of Image.ToBitmap and Image.get_Bitmap to see how you could implement your own WriteableBitmap.

http://blogs.microsoft.co.il/blogs/tamir/archive/2008/04/23/webcam-control-with-wpf-or-how-to-create-high-framerate-player-with-directshow-by-using-interopbitmap-in-wpf-application.aspx

Try this.
http://easywebcam.codeplex.com/
I used it and it was excellent..

Related

Blackmagic decklink CapturePreviewCSharp fails to capture video

I have run the project CapturePreviewCSharp provided as a sample. However, it seems to be able to acquire the device status, etc., but the image is not output to the preview screen.
There are no errors, etc.
It seems that the rendering to D3DImage is not working, but I don't know how to solve this problem.
Does anyone else have a similar problem?
I have asked on the Blackmagic forum but have not gotten an answer.
Development environment.
Windows 11.
Visual Studio 2022
NET framework 4.8
DeckLink mini-recorder 4K
Here is the code on the Xaml side
<Window x:Class="CapturePreviewCSharp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interop;assembly=PresentationCore"
<!--excerpt code-->
<GroupBox x:Name="groupBoxPreview" Header="Preview" >
<Grid x:Name="gridPreview" Margin="5">
<Image x:Name="imagePreview">
<Image.Source>
<i:D3DImage x:Name="d3dPreview" />
</Image.Source>
</Image>
</Grid>
</GroupBox>
</DockPanel>
</Window>
Code to output images to D3DImage.
public void RenderD3DImage(object sender, EventArgs e)
{
UpdateUIElement(d3dPreview, new Action(() =>
{
var actualWidth = gridPreview.RenderSize.Width;
var actualHeight = gridPreview.RenderSize.Height;
if (d3dPreview.IsFrontBufferAvailable)
{
IntPtr surface = IntPtr.Zero;
if (actualWidth > 0 && actualHeight > 0)
{
new MTAAction(() =>
{
m_previewCallback.PreviewHelper.SetSurfaceSize((uint)actualWidth, (uint)actualHeight);
m_previewCallback.PreviewHelper.GetBackBuffer(out surface);
});
}
if (surface != IntPtr.Zero)
{
d3dPreview.Lock();
d3dPreview.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface);
new MTAAction(() => m_previewCallback.PreviewHelper.Render());
d3dPreview.AddDirtyRect(new Int32Rect(0, 0, d3dPreview.PixelWidth, d3dPreview.PixelHeight));
d3dPreview.Unlock();
}
}
}));
}
MTAAction class.
namespace CapturePreviewCSharp
{
class MTAAction
{
private ManualResetEvent doneEvent;
private readonly Action action;
public MTAAction(Action action)
{
this.action = action;
doneEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(MTAActionThreadPoolCallback, 0);
doneEvent.WaitOne();
}
public void MTAActionThreadPoolCallback(Object threadContext)
{
action();
doneEvent.Set();
}
}
class MTAFunc<T> where T : struct
{
private T value;
private readonly Func<T> getValueFunc;
private ManualResetEvent doneEvent;
public MTAFunc(Func<T> func)
{
value = default(T);
getValueFunc = func;
doneEvent = new ManualResetEvent(false);
}
public T Value
{
get
{
ThreadPool.QueueUserWorkItem(MTAFuncThreadPoolCallback, 0);
doneEvent.WaitOne();
return (T)value;
}
}
public void MTAFuncThreadPoolCallback(Object threadContext)
{
value = getValueFunc();
doneEvent.Set();
}
}
}
Thank you.

EmguCV: BitmapSource isn't updated on GUI after PropertyChanged Event:

I want to simply capture and display a camera picture on my view, updated every second. However the image container, which is bound to my Bitmapsource CurrentFramestays blank during runtime.
This is my code so far (mostly adopted from an answer of another thread with similar topic:
public class CameraViewModel : ViewModelBase
{
public CameraViewModel()
{
StartVideo();
}
private DispatcherTimer Timer { get; set; }
private VideoCapture Capture { get; set; }
private BitmapSource currentFrame;
public BitmapSource CurrentFrame
{
get { return currentFrame; }
set
{
if (currentFrame != value)
{
currentFrame = value;
SetProperty(ref currentFrame, value);
}
}
}
private void StartVideo()
{
//CurrentFrame = new BitmapImage(new Uri("C:\\Users\\Johannes\\Pictures\\Camera Roll\\asdf.bmp")) as BitmapSource;
Capture = new VideoCapture();
Timer = new DispatcherTimer();
//framerate of 10fps
Timer.Interval = TimeSpan.FromMilliseconds(1000);
Timer.Tick += new EventHandler(async (object s, EventArgs a) =>
{
//draw the image obtained from camera
using (Image<Bgr, byte> frame = Capture.QueryFrame().ToImage<Bgr, byte>())
{
if (frame != null)
{
CurrentFrame = ToBitmapSource(frame);
}
}
});
Timer.Start();
}
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ptr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}
/// <summary>
/// Delete a GDI object
/// </summary>
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
}
A few thing for better understanding:
The ViewModelBase class incoorperates and handles the
INotifyPropertyChange events.
Databinding is working! I have tested
it, by assigning a bmp-file to CurrentFrame in the
StartVideo()Method - and the image shows up in the GUI at runtime.
The SetProperty()Method fires every 1000ms as expected.
When I assigned a file to CurrentFrame to test the databinding, I
saw that it seemed to be of type BitmapImage- maybe that's where the
problem lies?? However from the information I could gather,
BitmapSource should work and show in WPF views...
The captured frame from the camera is not empty. I tried to write it
directly to a image file and it shows the correct content as
expected.
Edit:
For completeness here is also the responsible part of the view:
<UserControl.Resources>
<ResourceDictionary>
<local:CameraViewModel x:Key="vm" />
</ResourceDictionary>
</UserControl.Resources>
<Grid DataContext="{StaticResource vm}">
<Image Source="{Binding CurrentFrame}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
Edit2: Link to Github repository to view code
You must not set
currentFrame = value;
before calling
SetProperty(ref currentFrame, value);
because the check
if (Object.Equals(storage, value)) return;
will always be true then.
Implement the property like this:
public BitmapSource CurrentFrame
{
get { return currentFrame; }
set { SetProperty(ref currentFrame, value); }
}

Display progress during matrix loop?

I'm making a chessboard, I want to see progress while it's done.
Chessboard is not classical, it contains millions of fields, the process of creation itself takes time. Sometimes the creation process takes up to 2 minutes. I want to visually see when the process itself will be over. It does not have to be a progress bar, it can be any control that will not slow down the process itself.
When I use Progress.Dispatcher.Invoke (()... I actually slow down the creation process and it takes 5 times longer than usual. When I use BackgroundWorker and ReportProgress ... I also slow down the creation process and it takes 5 to 8 times more than usual.
I just want to show the user progress by using any control or class that will not slow down the process. Some idea?
Rectangle[,] square = new Rectangle[x, x];
for (int row = 0; row < x; row++)
for (int col = 0; col < x; col++)
{
square[row, col] = new Rectangle()
{
Height = squareSize,
Width = squareSize
};
Grid.SetColumn(square[row, col], col);
Grid.SetRow(square[row, col], row);
if ((row + col) % 2 == 0)
{
square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(233, 223, 191));
}
else
{
square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(112, 42, 44));
}
LayoutRoot.Children.Add(square[row, col]);
// Watch process of creation in real time
if (cbCreationProcess.IsChecked == true)
Progress.Dispatcher.Invoke(() => Progress.Value = x, DispatcherPriority.Background);
}
This solution has worked for me in the past.
ProgressWindowControl.xaml
<Window
x:Class="YourNamespace.ProgressWindowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
WindowStartupLocation="CenterScreen" ShowInTaskbar="False" ResizeMode="NoResize"
SizeToContent="WidthAndHeight" WindowStyle="None"
mc:Ignorable="d">
<Window.Style>
<Style TargetType="Window">
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="Background" Value="#00FFFFFF"/>
</Style>
</Window.Style>
<Grid>
<Grid Width="450" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0">
<Grid x:Name="Back">
<Border Background="Black" CornerRadius="3" Opacity="0.15"/>
<Border CornerRadius="2" Margin="1" Background="White"/>
</Grid>
<Grid x:Name="Content_Area" Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="Info" TextWrapping="Wrap"
Text="{Binding Path=State,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Grid.Row="0" Margin="12,12,12,0" Foreground="#FF2D2D2D"/>
<ProgressBar Height="12"
Grid.Row="1"
Margin="12"
IsIndeterminate="{Binding Path=IsIndeterminate,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Value="{Binding Path=Progress,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Maximum="{Binding Path=MaxProgress,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<Button x:Name="uxCancelBtn" Grid.Row="2" Height="22" Width="85" HorizontalAlignment="Right" Margin="0 0 12 0"
Click="CancelButton_Click" IsEnabled="False" Content="{x:Static resx:Strings.Cancel}">
</Button>
</Grid>
</Grid>
</Grid>
</Window>
ProgressWindowControl.cs
public sealed partial class ProgressWindowControl : Window
{
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register("Progress", typeof(double), typeof(ProgressWindowControl), new PropertyMetadata(0d));
public static readonly DependencyProperty MaxProgressProperty =
DependencyProperty.Register("MaxProgress", typeof(double), typeof(ProgressWindowControl), new PropertyMetadata(100d));
public static readonly DependencyProperty IsIndeterminateProperty =
DependencyProperty.Register("IsIndeterminate", typeof(bool), typeof(ProgressWindowControl), new PropertyMetadata(true));
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(string), typeof(ProgressWindowControl), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IsCancelAllowedProperty =
DependencyProperty.Register("IsCancelAllowed", typeof(bool), typeof(ProgressWindowControl), new PropertyMetadata(false));
private ProgressWindowControl()
{
InitializeComponent();
}
public double Progress
{
get
{
return (double)GetValue(ProgressProperty);
}
set
{
SetValue(ProgressProperty, value);
}
}
public double MaxProgress
{
get
{
return (double)GetValue(MaxProgressProperty);
}
set
{
SetValue(MaxProgressProperty, value);
}
}
public bool IsIndeterminate
{
get
{
return (bool)GetValue(IsIndeterminateProperty);
}
set
{
SetValue(IsIndeterminateProperty, value);
}
}
public string State
{
get
{
return (string)GetValue(StateProperty);
}
set
{
SetValue(StateProperty, value);
}
}
public Action OnProgressWindowCancel { get; set; }
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (OnProgressWindowCancel != null)
{
uxCancelBtn.IsEnabled = false;
uxCancelBtn.Content = Strings.Cancelling;
OnProgressWindowCancel();
}
}
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
private const int GWL_HWNDPARENT = -8;
private static ProgressWindowControl _progressWindowControl;
private static bool _isVisible;
private static Window _owner;
private static ResizeMode? _ownerResizeMode;
private static bool _ownerIsHitTestVisible;
private static bool _ownerFocusable;
public static void ShowProgressWindow(Window owner = null)
{
if (!_isVisible)
{
IntPtr ownerHandle = IntPtr.Zero;
if (owner != null)
{
_owner = owner;
ownerHandle = GetHandler(_owner);
//Block owner window input while the progress bar is opened
_ownerResizeMode = _owner.ResizeMode;
_ownerIsHitTestVisible = _owner.IsHitTestVisible;
_ownerFocusable = _owner.Focusable;
_owner.ResizeMode = ResizeMode.NoResize;
_owner.IsHitTestVisible = false;
_owner.Focusable = false;
_owner.PreviewKeyDown += Owner_PreviewKeyDown;
_owner.PreviewMouseDown += Owner_PreviewMouseDown;
_owner.Closing += Owner_Closing;
}
//Run window in its own thread
Thread thread = new Thread(new ThreadStart(() =>
{
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
_progressWindowControl = new ProgressWindowControl();
// Shutdown the dispatcher when the window closes
_progressWindowControl.Closed += (s, e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
// When the progress window has loaded, if an owner has been specified, attach it to the window, otherwise set Topmost = true
ProgressWindowControl._progressWindowControl.Loaded += (s, e) =>
{
if (owner != null)
{
IntPtr ownedWindowHandle = GetHandler(_progressWindowControl);
SetOwnerWindowMultithread(ownedWindowHandle, ownerHandle);
}
else
{
_progressWindowControl.Topmost = true;
}
};
_progressWindowControl.Show();
_isVisible = true;
System.Windows.Threading.Dispatcher.Run();
}));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
}
}
private static void Owner_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
}
private static void Owner_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
private static void Owner_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
e.Handled = true;
}
private static void SetOwnerWindowMultithread(IntPtr windowHandleOwned, IntPtr intPtrOwner)
{
if (windowHandleOwned != IntPtr.Zero && intPtrOwner != IntPtr.Zero)
{
SetWindowLong(windowHandleOwned, GWL_HWNDPARENT, intPtrOwner.ToInt32());
}
}
private static IntPtr GetHandler(Window window)
{
var interop = new WindowInteropHelper(window);
return interop.Handle;
}
public static void CloseProgressWindow()
{
if (_progressWindowControl != null && _isVisible)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.Close();
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(_progressWindowControl.Close));
}
if (_owner != null)
{
//Unblock owner input
_owner.ResizeMode = _ownerResizeMode ?? ResizeMode.CanResize;
_owner.IsHitTestVisible = _ownerIsHitTestVisible;
_owner.Focusable = _ownerFocusable;
_owner.PreviewKeyDown -= Owner_PreviewKeyDown;
_owner.PreviewMouseDown -= Owner_PreviewMouseDown;
_owner.Closing -= Owner_Closing;
}
//Reset fields
_ownerResizeMode = null;
_ownerIsHitTestVisible = false;
_ownerFocusable = false;
_progressWindowControl = null;
_owner = null;
_isVisible = false;
}
}
public static void SetProgress(double progress, double maxProgress)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.IsIndeterminate = false;
_progressWindowControl.Progress = progress;
_progressWindowControl.MaxProgress = maxProgress;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.IsIndeterminate = false;
_progressWindowControl.Progress = progress;
_progressWindowControl.MaxProgress = maxProgress;
}));
}
}
}
public static void SetIsIndeterminate(bool isIndeterminate)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.IsIndeterminate = isIndeterminate;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.IsIndeterminate = isIndeterminate;
}));
}
}
}
public static void SetState(string state)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.State = state;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.State = state;
}));
}
}
}
public static void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.OnProgressWindowCancel = progressWindowCancel;
_progressWindowControl.uxCancelBtn.IsEnabled = isCancelAllowed;
_progressWindowControl.uxCancelBtn.Content = Strings.Cancel;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.OnProgressWindowCancel = progressWindowCancel;
_progressWindowControl.uxCancelBtn.IsEnabled = isCancelAllowed;
_progressWindowControl.uxCancelBtn.Content = Strings.Cancel;
}));
}
}
}
}
A helper class to open the window:
public static class ProgressWindowHelper
{
public static void Show(Window owner = null)
{
ProgressWindowControl.ShowProgressWindow(owner);
}
public static void Close()
{
ProgressWindowControl.CloseProgressWindow();
}
public static void SetProgress(double progress, double maxProgress)
{
ProgressWindowControl.SetProgress(progress, maxProgress);
}
public static void SetIsIndeterminate(bool isIndeterminate)
{
ProgressWindowControl.SetIsIndeterminate(isIndeterminate);
}
public static void SetState(string state)
{
ProgressWindowControl.SetState(state);
}
public static void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
{
ProgressWindowControl.SetIsCancelAllowed(isCancelAllowed, progressWindowCancel);
}
}
A service so you can use Dependency Injection (I didn't include the interface, just create one as needed):
public class ProgressWindowService : IProgressWindowService
{
public void Show(Window owner = null)
{
ProgressWindowHelper.Show(owner);
}
public void Close()
{
ProgressWindowHelper.Close();
}
public void SetProgress(double progress, double maxProgress)
{
ProgressWindowHelper.SetProgress(progress, maxProgress);
}
public void SetIsIndeterminate(bool isIndeterminate)
{
ProgressWindowHelper.SetIsIndeterminate(isIndeterminate);
}
public void SetState(string state)
{
ProgressWindowHelper.SetState(state);
}
public void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
{
ProgressWindowHelper.SetIsCancelAllowed(isCancelAllowed, progressWindowCancel);
}
}
And finally in your ViewModel (assuming you have injected the service):
ProgressBarService.SetProgress(current, total);
ProgressBarService.SetState(state);
ProgressBarService.Show();
You can also pass a window to the Show method and then the window will be attached to it and input will be blocked while the progress window is shown, if no window is provided, the progress bar is shown on top of any other window (TopMost=true):
ProgressBarService.Show(YourWindow);
You could also use a messenger to trigger the progress window.
EDIT
Removed DevExpress dependency.
obviously any addition of functionality is going to slow it down as you're well aware.
what I would do if I was told to use the above code is likely create a timer and poke into where-ever you have your current row and col increments and its up to you what you do with your calculation.
All you would have to do is handle the Timer.Elapsed event (whatever kind of timer) and calculate/report current progress in percent or such.
Otherwise, you could write up a separate thread and run it in a loop that sleeps N-milloseconds until drawing is complete—essentially tandem with the idea of using a timer. Generally, this is what we would do to report progress on a playing mp3 or some such object manipulated on a separate thread.
there is a lot of room for you to be creative here and milk some wisdom out of this scenario to optimize results but before considering such...
bare in mind that WPF is a beast when it comes to how it deals with drawing things since it relies heavily on the video hardware, memory, etc... I tend to think of WPF as like html for OpenGL or DirectX. I hope your GPU has a good connection to the heat-sync and doesn't get too hot and/or that you're not working on a laptop with an embedded GPU. I'm being aggressive here, but only because I've blown pleanty of hardware over the years writing software in the write/compile/run/and-repeat cycle. I'd have gotten much more life out of lots of hardware if I played a bit safer. not to mention recent years are getting harder and harder on our hardware.
for the sake of being as creative as possible here
since we know how many squares there are and can be sure of the resulting grid's dimensions, have you thought rendering a custom pattern as a background and explicitly setting the size of the grid? Once that's done, you could be creative when it comes to further populating needed or spin up some interesting transitions, etc
learning/using System.Threading.Thread(ThreadStart) will enable you to set process-priority to a higher value than standard defaults — as opposed to using BackgroundWorker or some other such... but I haven't quite wrapped my mind around an implementation with such as this.
build up some objects outside the UI such as a List<> and render at the end of a row, column or perhaps every N incremenet
we know that we have x*x boxes in the end, so if we're explicit about the size, there is so much room for creativity here. I hadn't tinkered with WPF in a while, but this would be a good case for studying and looking at different ways to lay such a thing out (eg: CustomLayoutPanel tut). If you're stuck on the notion of a Grid, there is only so much you can do---I would instinctively be working with Drawing methods for doing this, and would likely come up with a way to draw only what is visible on the screen as needed... or
generally #dymanoid's response is poking you into the correct direction but it breaks the logic of the question being asked, therefore overshoots the scope of the question... but we want to enlighten here and generally yes any rendering process stuff in WPF should be thought out using existing optimization strategies such as MVVM and styles/templates/transitions.

IBasicVideoEffect processes only form 3 to 8 first frames

I created a simple test UWP app with one MainPage, that has MediaPlayer:
public sealed partial class MainPage
{
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var mediaPlayer = new MediaPlayer
{
Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/preview.mp4")),
AutoPlay = true
};
mediaPlayer.AddVideoEffect(typeof(VideoEffect).FullName, true, null);
}
}
and WinRT component with IBasicVideoEffect inherited class that notifies me how many frames were processed:
public sealed class VideoEffect : IBasicVideoEffect
{
public IReadOnlyList<VideoEncodingProperties> SupportedEncodingProperties => new List<VideoEncodingProperties>();
public bool IsReadOnly => false;
public MediaMemoryTypes SupportedMemoryTypes => MediaMemoryTypes.Gpu;
public void SetProperties(IPropertySet configuration) { }
public bool TimeIndependent => false;
public void Close(MediaEffectClosedReason reason) { }
public void DiscardQueuedFrames() { }
private int _frameCounter;
public void ProcessFrame(ProcessVideoFrameContext context)
{
_frameCounter++;
Debug.WriteLine("Frame #" + _frameCounter);
}
public void SetEncodingProperties(VideoEncodingProperties encodingProperties, IDirect3DDevice device)
{
Debug.WriteLine("SetEncodingProperties");
}
}
If I run it - only 3 frames will be processed no matter what video file will be.
If I set breakpoint where _frameCounter increments I'll manage to hit F5 for 8 frames.
Why and how can I get all the frames to be processed?
I can solve it using MediaClip and MediaComposition as many examples say, but in this case frames are processed by CPU not GPU video engine which is not my goal.
The problem here is that you are playing a MediaPlayer without displaying it in XAML. So only the first few frames was processed as the media is not rendering on UI, there is no need to process other frames.
To make your VideoEffect work, you can use MediaPlayerElement control with MediaPlayerElement.SetMediaPlayer method to to render the media.
XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<MediaPlayerElement x:Name="mediaPlayerElement" />
</Grid>
Code-behind:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var mediaPlayer = new MediaPlayer
{
Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/preview.mp4")),
AutoPlay = true
};
mediaPlayer.AddVideoEffect(typeof(VideoEffect).FullName, true, null);
mediaPlayerElement.SetMediaPlayer(mediaPlayer);
}
After this, you will see _frameCounter increases while the media is playing.
with MediaPlayer you have to add effect before set source.
effect.AddVideoEffect(_player);
_player.Source = MediaSource.CreateFromMediaStreamSource(source);

How to release memory used by MediaElement

I am using MediaElement to show video clips in a loop for long period of time. After some time (hours for Win 7 / 4 GB RAM) the program crashes with exception of type "Insufficient memory". I have monitored the memory used while playing with Process Explorer-Sysinternals and also logged it using System.Diagnostics.Process methods. Both ways show gradually increasing of used memory.
Here is the code:
XAML:
<Grid Name="GridTest">
<MediaElement x:Name="MediaPlayer"
LoadedBehavior="Manual"
MediaEnded="VideoControl_MediaEnded"
MediaOpened="MediaPlayer_MediaOpened"
Source="{Binding Mode=OneWay,
Path=MySource}" />
</Grid>
.cs:
public partial class MainWindow : Window
{
public MainViewModel model = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.GridTest.DataContext = model;
// fill in model.MediaFilesUris:
...
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// choose the next media file
...
MediaPlayer.Play();
}
private void VideoControl_MediaEnded(object sender, RoutedEventArgs e)
{
// choose the next media file
...
model.OnPropertyChanged("MySource");
MediaPlayer.Play();
}
}
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public Uri[] MediaFilesUris = null;
public int crn = 0;
public Uri MySource { get { if (MediaFilesUris != null && MediaFilesUris.Count()>0) return MediaFilesUris[crn]; else return null; } }
}
I have also tested the case when MediaElement object is created dynamically, destroyed (together with all unsubscribing from events, etc.) after several clips and created again. Memory got consumed increasingly again.
Any suggestions would be appreciated!
Try to specify MediaElement UnloadingBehavior="Close"property in your XAML.
According to MSDN MediaState::Close indicates that
All media resources are released (including video memory).
My proposal is to make the following:
private void VideoControl_MediaEnded(object sender, RoutedEventArgs e)
{
// choose the next media file
...
//make the following explicitly
MediaPlayer.Stop();
MediaPlayer.Source = null;
model.OnPropertyChanged("MySource");
MediaPlayer.Play();
}

Categories