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); }
}
Related
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.
I need to present a WIC Bitmap from SharpDX in a WPF application. The WIC Bitmap inherits from BitmapSource, but it's not the same BitmapSource that WPF uses, though the class names are the same. How can I convert from one to another?
What you can do is create a custom derived class from WPF's BitmapSource.
For example, for this XAML:
<Window x:Class="SharpDXWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Image Name="MyImage"></Image>
</Grid>
</Window>
This Window code uses a custom "WicBitmapSource".
public partial class MainWindow : Window
{
private WicBitmapSource _bmp;
public MainWindow()
{
InitializeComponent();
_bmp = new WicBitmapSource(#"c:\path\killroy_was_here.png");
MyImage.Source = _bmp;
}
protected override void OnClosed(EventArgs e)
{
_bmp.Dispose();
}
}
Here is some sample code for this SharpDX/Wic custom BitmapSource (some information are grabbed from here: https://blogs.msdn.microsoft.com/dwayneneed/2008/06/20/implementing-a-custom-bitmapsource/).
public class WicBitmapSource : System.Windows.Media.Imaging.BitmapSource, IDisposable
{
public WicBitmapSource(string filePath)
{
if (filePath == null)
throw new ArgumentNullException(nameof(filePath));
using (var fac = new ImagingFactory())
{
using (var dec = new SharpDX.WIC.BitmapDecoder(fac, filePath, DecodeOptions.CacheOnDemand))
{
Frame = dec.GetFrame(0);
}
}
}
public WicBitmapSource(BitmapFrameDecode frame)
{
if (frame == null)
throw new ArgumentNullException(nameof(frame));
Frame = frame;
}
public BitmapFrameDecode Frame { get; }
public override int PixelWidth => Frame.Size.Width;
public override int PixelHeight => Frame.Size.Height;
public override double Height => PixelHeight;
public override double Width => PixelWidth;
public override double DpiX
{
get
{
Frame.GetResolution(out double dpix, out double dpiy);
return dpix;
}
}
public override double DpiY
{
get
{
Frame.GetResolution(out double dpix, out double dpiy);
return dpiy;
}
}
public override System.Windows.Media.PixelFormat Format
{
get
{
// this is a hack as PixelFormat is not public...
// it would be better to do proper matching
var ct = typeof(System.Windows.Media.PixelFormat).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new[] { typeof(Guid) },
null);
return (System.Windows.Media.PixelFormat)ct.Invoke(new object[] { Frame.PixelFormat });
}
}
// mostly for GIFs support (indexed palette of 256 colors)
public override BitmapPalette Palette
{
get
{
using (var fac = new ImagingFactory())
{
var palette = new Palette(fac);
try
{
Frame.CopyPalette(palette);
}
catch
{
// no indexed palette (PNG, JPG, etc.)
// it's a pity SharpDX throws here,
// it would be better to return null more gracefully as this is not really an error
// if you only want to support non indexed palette images, just return null for the property w/o trying to get a palette
return null;
}
var list = new List<Color>();
foreach (var c in palette.GetColors<int>())
{
var bytes = BitConverter.GetBytes(c);
var color = Color.FromArgb(bytes[3], bytes[2], bytes[1], bytes[0]);
list.Add(color);
}
return new BitmapPalette(list);
}
}
}
public override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset)
{
if (offset != 0)
throw new NotSupportedException();
Frame.CopyPixels(
new SharpDX.Mathematics.Interop.RawRectangle(sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height),
(byte[])pixels, stride);
}
public void Dispose() => Frame.Dispose();
public override event EventHandler<ExceptionEventArgs> DecodeFailed;
public override event EventHandler DownloadCompleted;
public override event EventHandler<ExceptionEventArgs> DownloadFailed;
public override event EventHandler<DownloadProgressEventArgs> DownloadProgress;
protected override Freezable CreateInstanceCore() => throw new NotImplementedException();
}
I need to refresh Image on View
This my ViewPage:
<Image Grid.Row="1"
Grid.Column="1"
x:Name="OriginalImg"
Source="{Binding Orig}"
DataContext="{StaticResource MainViewModel}"/>
I'm using MVVMLibs package. And This is my ViewModel:
public class MainViewModel: ViewModelBase
{
private WriteableBitmap original = new WriteableBitmap(1280,720);
private WriteableBitmap temp = new WriteableBitmap(1280,720);
public WriteableBitmap Orig
{
get { return original; }
set
{
this.original = value;
base.RaisePropertyChanged("Orig");
}
}
public async Task<bool> ApplyEffectAsync(StorageFile file)
{
fileStream = await file.OpenAsync(FileAccessMode.Read);
temp.SetSource(fileStream);
Orig = temp;
}
}
But Image on my Page not displayed. What's my problem?
Problem is you are not really instantiating a new WriteableBitmap, just changing its source. So while it might work the first time, it certainly won't work after because the Dependency Property Manager won't know that your image changed unless its instace changes.
Try creating your temp WriteableBitmap in the ApplyEffectAsync method.
So i'm trying to loop through a folder and change the image source each 2 seconds.
I think my code is right, but I seem to be missing something since my image won't update, but I don't get an error.
The code populates my array of files so it finds the pictures, I'm just doing something wrong to set the image source.
XAML code
<Grid>
<Image x:Name="Picture" Source="{Binding ImageSource}" Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
<Grid>
C# code
private string[] files;
private System.Timers.Timer timer;
private int counter;
private int Imagecounter;
Uri _MainImageSource = null;
public Uri MainImageSource {
get
{
return _MainImageSource;
}
set
{
_MainImageSource = value;
}
}
public IntroScreen()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(this.MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
setupPics();
}
private void setupPics()
{
timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(timer_Tick);
timer.Interval = (2000);
timer.Start();
files = Directory.GetFiles("../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
Imagecounter = files.Length;
MessageBox.Show(Imagecounter.ToString());
counter = 0;
}
private void timer_Tick(object sender, EventArgs e)
{
counter++;
_MainImageSource = new Uri(files[counter - 1], UriKind.Relative);
if (counter == Imagecounter)
{
counter = 0;
}
}
Anyone know what I'm doing wrong ?
Updated code
XAML
<Image x:Name="Picture" Source="{Binding MainImageSource}" Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
C#
public partial class IntroScreen : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private string[] files;
private System.Timers.Timer timer;
private int counter;
private int Imagecounter;
Uri _MainImageSource = null;
public Uri MainImageSource
{
get
{
return _MainImageSource;
}
set
{
_MainImageSource = value;
OnPropertyChanged("MainImageSource");
}
}
public IntroScreen()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(this.MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
setupPics();
}
private void setupPics()
{
files = Directory.GetFiles("../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
Imagecounter = files.Length;
counter = 0;
timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(timer_Tick);
timer.Interval = (2000);
timer.Enabled = true;
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
counter++;
MainImageSource = new Uri(files[counter - 1], UriKind.Relative);
if (counter == Imagecounter)
{
counter = 0;
}
}
I'm not getting any error's but the image still isen't switching. I'm wondering if my paths are even working. Is there any way to test this ?
You have forgot to do notify the update to MainImageSource to the binding.
To do so, you have to implement the interface : INotifyPropertyChanged and define DataContext.
And, as written in the MSDN documentation "Setting Enabled to true is the same as calling Start, while setting Enabled to false is the same as calling Stop.".
Like this:
public partial class IntroScreen : Window, INotifyPropertyChanged
{
private string[] files;
private Timer timer;
private int counter;
private int Imagecounter;
BitmapImage _MainImageSource = null;
public BitmapImage MainImageSource // Using Uri in the binding was no possible because the Source property of an Image is of type ImageSource. (Yes it is possible to write directly the path in the XAML to define the source, but it is a feature of XAML (called a TypeConverter), not WPF)
{
get
{
return _MainImageSource;
}
set
{
_MainImageSource = value;
OnPropertyChanged("MainImageSource"); // Don't forget this line to notify WPF the value has changed.
}
}
public IntroScreen()
{
InitializeComponent();
DataContext = this; // The DataContext allow WPF to know the initial object the binding is applied on. Here, in the Binding, you have written "Path=MainImageSource", OK, the "MainImageSource" of which object? Of the object defined by the DataContext.
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
setupPics();
}
private void setupPics()
{
timer = new Timer();
timer.Elapsed += timer_Tick;
timer.Interval = 2000;
// Initialize "files", "Imagecounter", "counter" before starting the timer because the timer is not working in the same thread and it accesses these fields.
files = Directory.GetFiles(#"../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
Imagecounter = files.Length;
MessageBox.Show(Imagecounter.ToString());
counter = 0;
timer.Start(); // timer.Start() and timer.Enabled are equivalent, only one is necessary
}
private void timer_Tick(object sender, EventArgs e)
{
// WPF requires all the function that modify (or even read sometimes) the visual interface to be called in a WPF dedicated thread.
// IntroScreen() and MainWindow_Loaded(...) are executed by this thread
// But, as I have said before, the Tick event of the Timer is called in another thread (a thread from the thread pool), then you can't directly modify the MainImageSource in this thread
// Why? Because a modification of its value calls OnPropertyChanged that raise the event PropertyChanged that will try to update the Binding (that is directly linked with WPF)
Dispatcher.Invoke(new Action(() => // Call a special portion of your code from the WPF thread (called dispatcher)
{
// Now that I have changed the type of MainImageSource, we have to load the bitmap ourselves.
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(files[counter], UriKind.Relative);
bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Don't know why. Found here (http://stackoverflow.com/questions/569561/dynamic-loading-of-images-in-wpf)
bitmapImage.EndInit();
MainImageSource = bitmapImage; // Set the property (because if you set the field "_MainImageSource", there will be no call to OnPropertyChanged("MainImageSource"), then, no update of the binding.
}));
if (++counter == Imagecounter)
counter = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And your XAML does not refer to the correct property:
<Grid>
<Image x:Name="Picture" Source="{Binding MainImageSource}" Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
<Grid>
Why do you need to implement INotifyPropertyChanged?
Basically, when you define a binding, WPF will check if the class that contains the corresponding property defines INotifyPropertyChanged. If so, it will subscribe to the event PropertyChanged of the class.
I'm not seeing any use of the INotifyPropertyChanged interface, which would be required to update a UI item the way you are using it. As it is now, the UI control has no way of knowing that the value was updated.
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..