I am currently trying to display and play an animated gif in WPF without much luck
I have tried all the solutions found here including the one in the question
Here is what i currently have
<Window x:Class="Sooooothing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Attached="clr-namespace:Sooooothing"
Title="MainWindow" Height="327.861" Width="525">
<Grid>
<MediaElement x:Name="gif" MediaEnded="myGif_MediaEnded" UnloadedBehavior="Manual"
Source="Images\7c6516d73fc8643abf1df969fcc1a72c.gif"
LoadedBehavior="Play" Stretch="None"/>
</Grid>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Sooooothing
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//gif.Play();
}
private void myGif_MediaEnded(object sender, RoutedEventArgs e)
{
gif.Position = new TimeSpan(0, 0, 1);
gif.Play();
}
}
}
When the application starts all i get is the white background for the window, at first i though it was the gif not loading but i let it sit for a couple minutes without any change.
Here is my solution
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Sooooothing
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
GifBitmapDecoder decoder2;
int ImageNumber = 0;
bool mouseIn = true;
double opasity = 0;
List<Thread> threadList = new List<Thread>();
bool programClosed = false;
public MainWindow()
{
InitializeComponent();
Classes.DataHouse.load(); //Im storing all my gif links in this List<string> so i can
// loop through them with buttons on the window
#region thread gif frame changer
Uri myUri = new Uri(Classes.DataHouse.images[ImageNumber], UriKind.RelativeOrAbsolute);
decoder2 = new GifBitmapDecoder(myUri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource2; // Decode the gif using GifBitmapDecoder then start a thread to
int frameCount = decoder2.Frames.Count;// loop through all the images and put them one after
Thread th = new Thread(() => {// another into an image on your window
while (!programClosed)
{
for (int i = 0; i < frameCount; i++)
{
try{
this.Dispatcher.Invoke(new Action(delegate()
{
frameCount = decoder2.Frames.Count;
if (frameCount >= i)
{
bitmapSource2 = decoder2.Frames[i];
image.Source = bitmapSource2;
}
}));
}
catch
{
//the thread was probably aborted
}
System.Threading.Thread.Sleep(100);
}
}
});
threadList.Add(th);
th.Start();
#endregion
}
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
private void img_forward_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (ImageNumber+1 >= Classes.DataHouse.images.Count)
ImageNumber = 0;
else
ImageNumber++;
Uri myUri = new Uri(Classes.DataHouse.images[ImageNumber], UriKind.RelativeOrAbsolute);
decoder2 = new GifBitmapDecoder(myUri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
}
private void Grid_MouseEnter(object sender, MouseEventArgs e)
{
mouseIn = true;
Thread th = new Thread(() =>
{
while (opasity < 100 && mouseIn)
{
System.Threading.Thread.Sleep(25);
opasity++;
try
{
this.Dispatcher.Invoke(new Action(delegate()
{
img_forward.Opacity = opasity / 100;
img_exit.Opacity = opasity / 100;
img_backward.Opacity = opasity / 100;
}));
}
catch
{
//the thread was probably aborted
}
}
});
threadList.Add(th);
th.Start();
}
private void Grid_MouseLeave(object sender, MouseEventArgs e)
{
mouseIn = false;
Thread th = new Thread(() =>
{
while (opasity > 0 && !mouseIn)
{
System.Threading.Thread.Sleep(25);
opasity--;
try{
this.Dispatcher.Invoke(new Action(delegate()
{
img_forward.Opacity = opasity / 100;
img_exit.Opacity = opasity / 100;
img_backward.Opacity = opasity / 100;
}));
}
catch
{
//the thread was probably aborted
}
}
});
threadList.Add(th);
th.Start();
}
private void img_backward_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (ImageNumber - 1 < 0)
ImageNumber = Classes.DataHouse.images.Count-1;
else
ImageNumber--;
Uri myUri = new Uri(Classes.DataHouse.images[ImageNumber], UriKind.RelativeOrAbsolute);
decoder2 = new GifBitmapDecoder(myUri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
}
private void img_exit_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
foreach (Thread th in threadList)
{
while(th.ThreadState == ThreadState.Running)
th.Abort();
}
programClosed = true;
this.Close();
}
}
}
Unfortunately, animated GIF images were somewhat of an oversight when Microsoft developed WPF, so there is no 'built in' functionality that handles this. There are several working examples found online, but you should still be aware that code needs to be run on the UI thread to animate these images.
Therefore, certain situations, such as trying to use a 'busy' GIF during loading will not work because the UI thread will already be busy.
Having announced that disclaimer, you can find some working code that will do what you want in Thomas Levesque's GitHub project at the link below:
thomaslevesque/WpfAnimatedGif
Using a Thread
public MainWindow()
{
InitializeComponent();
Uri myUri = new Uri(#"Images\2b3601fe2b62e87481bd301da52a2182.gif", UriKind.RelativeOrAbsolute);
GifBitmapDecoder decoder2 = new GifBitmapDecoder(myUri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource2;
int frameCount = decoder2.Frames.Count;
Thread th = new Thread(() => {
while (true)
{
for (int i = 0; i < frameCount; i++)
{
this.Dispatcher.Invoke(new Action(delegate()
{
bitmapSource2 = decoder2.Frames[i];
image.Source = bitmapSource2;
}));
System.Threading.Thread.Sleep(100);
}
}
});
th.Start();
}
This only works with physical files that aren't compiled as Resources, etc.
Try to change your image resource Build Action to Content or something similar, instead of Resource (And activate Copy to Output Directory, maybe?).
I managed to get a sample working that way. The key is that you must reference a physical file in some folder or internet URL.
For more info, check this out: https://social.msdn.microsoft.com/Forums/vstudio/en-US/93d50a97-0d8d-4b18-992e-cd3200693337/how-to-use-an-animated-gif?forum=wpf
Related
So basically, I m making a program where 25 tasks write the same random task to one file until it reaches a certain size, and I would like to dynamically display the file size of the selected file in 0.5 seconds interval, so I made a timer hooked with Timer_Elapsed which should be executed every 0.5 seconds and display on UI which I specify on mainWindow.xaml on the textblock x:Name="fileSize", so I placed the createTimer function in to btnGo_Click function in mainwindow.xaml.cs so the event would extract the right fileInfo of the selectedFile. Any advice for my wrong would be appreciated. I'm also sharing the FileIO class in case it is needed, so they are full solutions. Even aside from the questions I asked, any general advice to better my code would be appreciated because I need to get a grasp of the good code example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.Threading;
namespace WriteFile
{
internal class FileIO
{
private object _lock = new object();
public volatile bool sizeReached = false;
private StreamWriter sw = null;
Mutex mut = null;
public FileIO()
{
if (!Mutex.TryOpenExisting("MyMutex", out mut))
{
mut = new Mutex(true, "MyMutex");
mut.ReleaseMutex();
}
}
internal void WriteFile(string FilePath)
{
while (!sizeReached)
{
mut.WaitOne();
try
{
using (sw = new StreamWriter(FilePath, true))
{
sw.WriteLine(Guid.NewGuid());
}
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message);
}
finally
{
if (sw != null)
{
sw.Close();
}
}
mut.ReleaseMutex();
}
}
internal void SizeMonitor(string FPath, int MaxSize, Task[] tasks)
{
FileInfo fi = null;
while (!sizeReached)
{
if (File.Exists(FPath))
{
fi = new FileInfo(FPath);
if (fi.Length >= MaxSize)
{
sizeReached = true;
}
}
if (sizeReached)
{
foreach (Task task in tasks)
{
task.Wait();
}
}
Thread.Sleep(1);
}
MessageBox.Show(fi.Length.ToString());
MessageBox.Show("Done");
}
}
}
mainWindow.xaml
<Window x:Class="WriteFile.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WriteFile"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock x:Name="fileSize"/>
<TextBox Name ="TargetSize" VerticalAlignment="Center" FontSize="20">
</TextBox>
<Label Content="Target Size" HorizontalAlignment="Left" Margin="92,150,0,0" VerticalAlignment="Top"/>
<Button Name ="btnGo" Content="Write to file" HorizontalAlignment="Left" Margin="92,267,0,0" VerticalAlignment="Top" Width="100" Click="btnGo_Click"/>
</Grid>
</Window>
mainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using Microsoft.Win32;
using System.ComponentModel;
using System.Timers;
using System.Threading;
using System.Runtime.CompilerServices;
namespace WriteFile
{
public partial class MainWindow : Window
{
System.Timers.Timer timer = new System.Timers.Timer();
Task[] tasks;
Task MonitorTask;
static FileIO fio = new FileIO();
static string fPath;
static FileInfo fileInfo;
public MainWindow()
{
InitializeComponent();
CreateTimer();
}
public void CreateTimer()
{
var timer = new System.Timers.Timer(500); // fire every 0.5 second
timer.Enabled = true;
timer.Elapsed += Timer_Elapsed;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
fileSize.Text = fileInfo.Length.ToString();
}
private void btnGo_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.ShowDialog();
Stream myStream;
saveFileDialog.FilterIndex = 2;
saveFileDialog.RestoreDirectory = true;
if (File.Exists(saveFileDialog.FileName))
{
File.Delete(saveFileDialog.FileName);
}
if ((myStream = saveFileDialog.OpenFile()) != null)
{
StreamWriter sw = new StreamWriter(myStream);
sw.Write(" your text");
myStream.Close();
}
int NoOfTasks = 25;
int Target = Convert.ToInt32(TargetSize.Text);
fPath = saveFileDialog.FileName;
tasks = new Task[NoOfTasks];
fio.sizeReached = false;
fileInfo = new FileInfo(fPath);
for (int i = 0; i < NoOfTasks; i++)
{
tasks[i] = new Task(() => fio.WriteFile(fPath));
tasks[i].Start();
}
MonitorTask = new Task(() => fio.SizeMonitor(fPath, Target, tasks));
MonitorTask.Start();
}
}
}
As mentioned by others, FileInfo.Length value is pre-cached. If the associated file has changed, the FileInfo requires to refresh those cached values. To accomplish this, you must explicitly call the FileInfo.Refresh method. FileInfo does not monitor the associated file.
Furthermore, System.Threading.Timer executes the callback on a background thread. In WPF you can only reference a DispatcherObject (for example TextBlock) from a dispatcher thread (UI thread). For this reason you should use the DispatcherTimer. DispatcherTimer executes the callback on the dispatcher thread.
Additionally, you would want to stop the timer once the writing to the file is completed.
You should not use Task.Start. Instead of Task.Start and a Mutex you should simply await the Task. In your context, you can await a collection of Task objects using Task.WhenALl.
And instead of creating Task explicitly, you should use the async API of the StreamWriter.
You actually don't have concurrent code (neither would you benefit from it): the Mutex, declaration of a volatile field and the lock object are redundant. Also closing the StreamWriter explicitly in a finally block is not needed as you already use a using-block to handle the lifetime of the instance.
There are some more flaws in your code (some logical issues for instance). I have refactored your version to eliminate some of the issues for example by using the asynchronous StreamWriter API and async/await.
Because it is difficult to make any sense of your code (due to the lack of context) it is not possible to further improve it. For example, creating a single concatenated string (consisting of e.g. 25 values) would result in a single file write (and resource allocation), which would significantly improve the overall performance.
FileIO.cs
namespace WriteFile
{
internal class FileIO
{
public bool IsSizeReached { get; private set; }
public FileIO()
{
}
internal async Task WriteToFileAsync(string filePath)
{
if (this.IsSizeReached)
{
return;
}
using (var sw = new StreamWriter(filePath, true))
{
await sw.WriteLineAsync(Guid.NewGuid());
}
}
internal async Task SizeMonitorAsync(string fPath, int maxSize, IEnumerable<Task> tasks)
{
FileInfo fi = new FileInfo(fPath);
this.IsSizeReached = fi.Length >= maxSize;
if (this.IsSizeReached)
{
return;
}
await Task.WhenAll(tasks);
MessageBox.Show(fi.Length.ToString());
MessageBox.Show("Done");
}
}
}
MainWindow.xaml.cs
namespace WriteFile
{
public partial class MainWindow : Window
{
private DispatcherTimer Timer { get; }
private FileIO Fio { get; }
private FileInfo DestinationFileInfo { get; }
public MainWindow()
{
InitializeComponent();
this.Fio = new FileIO();
}
public void StartTimer()
{
this.Timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(500),
DispatcherPriority.Background,
OnTimerElapsed,
this.Dispatcher);
this.Timer.Start();
}
public void StopTimer() => this.Timer.Stop();
private void OnTimerElapsed(object sender, EventArgs e)
{
this.fileSize.Text = this.DestinationFileInfo.Length.ToString();
}
private async void btnGo_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.ShowDialog();
saveFileDialog.FilterIndex = 2;
saveFileDialog.RestoreDirectory = true;
if (File.Exists(saveFileDialog.FileName))
{
File.Delete(saveFileDialog.FileName);
}
Stream myStream;
if ((myStream = saveFileDialog.OpenFile()) != null)
{
// Dispose StreamWriter and underlying Stream
using (var sw = new StreamWriter(myStream))
{
await sw.WriteAsync(" your text");
}
}
int noOfTasks = 25;
// TODO::You must validate the input to prevent invalid or unreasonable values.
// Currently, this line will throw and crash the application if the input is invalid (not numeric).
int target = Convert.ToInt32(TargetSize.Text);
string fPath = saveFileDialog.FileName;
var tasks = new List<Task>();
this.DestinationFileInfo = new FileInfo(fPath);
StartTimer();
for (int i = 0; i < noOfTasks; i++)
{
Task writeToFileTask = this.Fio.WriteToFileAsync(fPath);
tasks.Add(writeToFileTask);
}
await this.Fio.SizeMonitorAsync(fPath, target, tasks));
StopTimer();
}
}
}
Timers.Timer works asynchronously and it raises the Elapsed event on the pool thread if SynchronizingObject is not set.
And work with WPF UI elements can only be done on the thread of their Dispatcher (almost always this is the main UI thread of the application).
Two solutions out of many possible:
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
// Asynchronously getting the right data
string text = fileInfo.Length.ToString();
// Using the Window's Dispatcher
// to setting ready-made data to UI elements.
Dispatcher.BeginInvoke(() => fileSize.Text = text);
}
The second option is without using a timer:
public MainWindow()
{
Initialized += RefreshAsync;
InitializeComponent();
}
public async void RefreshAsync(object? sender, EventArgs e)
{
while(true)
{
string text = await Task.Run(() => fileInfo.Length.ToString());
fileSize.Text = text;
await Task.Delay(500);
}
}
I'm learning wpf and at the same time developing an app with it. I'm having a hard time figuring out how i can run something when a doubleanimation (Or other sorts) is done. For instance:
DoubleAnimation myanim = new DoubleAnimation();
myanim.From = 10;
myanim.To = 100;
myanim.Duration = new Duration(TimeSpan.FromSeconds(3));
myview.BeginAnimation(Button.OpacityPropert, myanim);
//Code to do something when animation ends
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
namespace app
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
DoubleAnimation widthbutton = new DoubleAnimation();
widthbutton.From = 55;
widthbutton.To = 100;
widthbutton.Duration = new Duration(TimeSpan.FromSeconds(1.5));
button1.BeginAnimation(Button.HeightProperty, widthbutton);
DoubleAnimation widthbutton1 = new DoubleAnimation();
widthbutton1.From = 155;
widthbutton1.To = 200;
widthbutton1.Duration = new Duration(TimeSpan.FromSeconds(1.5));
button1.BeginAnimation(Button.WidthProperty, widthbutton1);
widthbutton.Completed += new EventHandler(myanim_Completed);
}
private void myanim_Completed(object sender, EventArgs e)
{
//your completed action here
MessageBox.Show("Animation done!");
}
}
}
How is this accomplishable? I have read quite a few other posts about this, but they all explain it using xaml, however i would like to do it using c# code. Thanks!
You can attach an event handler to the DoubleAnimation's Completed event.
myanim.Completed += new EventHandler(myanim_Completed);
private void myanim_Completed(object sender, EventArgs e)
{
//your completed action here
}
Or, if you prefer it inline, you can do
myanim.Completed += (s,e) =>
{
//your completed action here
};
Remember to attach the handler before starting the animation otherwise it won't fire.
I'm working on a eBook program in WPF recently, and the pages of the book are all saved as .jpg format. Since the images are all in high quality, the program will lag while switching pages.
I tried to cache a few images after the current image which the user is seeing in another thread. But it gives me an error.
An unhandled exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll
Additional information: The calling thread cannot access this object because a different thread owns it.
I can fix the error by adding Invoke, but the performance is like without the memory cache.
But without Invoke, the programs shows the error above. And I don't know how to fix it.
I tried to make the code as simple as I could.
Any help is appreciated.
Full Code:
using System;
using System.Collections.Specialized;
using System.IO;
using System.Runtime.Caching;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
namespace ThreadedMemoryCache_Test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
MemoryCache memcache;
int num = 0;
Image img;
int BufferPages = 2;
public MainWindow()
{
InitializeComponent();
//Change Content
img = new Image();
Content = img;
//Mouse Events
MouseDown += (s, e) =>
{
int add = 0;
if (e.LeftButton == MouseButtonState.Pressed)
add--;
else if (e.RightButton == MouseButtonState.Pressed)
add++;
else
return;
num += add;
img.Source = GetBitmap(num); //Error here
Buffer();
};
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Initiallize MemoryCache
NameValueCollection CacheSettings = new NameValueCollection(3);
CacheSettings.Add("CacheMemoryLimitMegabytes", Convert.ToString(1));
CacheSettings.Add("physicalMemoryLimitPercentage", Convert.ToString(10));
CacheSettings.Add("pollingInterval", Convert.ToString("00:00:10"));
memcache = new MemoryCache("BitmapCache", CacheSettings);
//Get First Photo
img.Source = GetBitmap(num);
Buffer();
}
BitmapImage GetBitmap(int num)
{
String id = num.ToString();
BitmapImage cachebmp = memcache[id] as BitmapImage;
if (cachebmp == null)
{
CacheItemPolicy policy = new CacheItemPolicy();
String name = num.ToString() + ".jpg";
FileInfo file = new FileInfo(name);
if(!file.Exists)
{
num = 0;
return GetBitmap(num);
}
cachebmp = new BitmapImage(new Uri(file.FullName));
memcache.Set(id, cachebmp, policy);
}
return cachebmp;
}
void Buffer()
{
//Start Thread
Thread cachethread = new Thread(() =>
{
for (int i = 1; i <= BufferPages; i++)
{
//Adding invoke fixes the error, but it seems to delay the main thread and cause lag.
//this.Dispatcher.Invoke((Action)(() =>
//{
GetBitmap(num + i);
//}));
}
});
cachethread.IsBackground = true;
cachethread.Start();
}
}
}
You have to freeze the BitmapImage to make it cross-thread accessible:
cachebmp = new BitmapImage(new Uri(file.FullName));
cachebmp.Freeze();
memcache.Set(id, cachebmp, policy);
That said, your code could be improved in many ways. Better use a ThreadPool thread or a BackgroundWorker or a Task to perform the background task.
Besides that you shouldn't call GetBitmap(num) recursively with num = 0 in case an image file doesn't exist. What happens if the 0.jpg is also missing?
You should use BackgroundWorker class.
Load some initial images at startup in MainThread, then start BackgroundWorker for rest of images.
Useful links : How to: Use a Background Worker, BackgroundWorker In Wpf.
I am working on a project which involves extracting features from color and depth frames from Kinect Camera. The problem I am facing is that whenever I try displaying 2 Images, the UI hangs. When I tried debugging, the depthFrame and colorFrame were coming as null. If enable only the color steam then both colorImage and featureImage1 are displayed properly and if I enable only the depth stream, it works as it should. But when I enable them both, the UI hangs. I have no idea what is causing the problem. I have the following the following code for my Kinect Application. What is the cause of this problem and how can I fix it?
Config: Windows 8 Pro 64bit, 2Ghz Core2Duo, VisualStudio 2012 Ultimate, EmguCV 2.4.0.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
using Emgu.CV;
using Emgu.CV.WPF;
using Emgu.CV.Structure;
using Emgu.Util;
namespace features
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private Image<Bgra, Byte> cvColorImage;
private Image<Gray, Int16> cvDepthImage;
private int colorWidth = 640;
private int colorHeight = 480;
private int depthWidth = 640;
private int depthHeight = 480;
private static readonly int Bgr32BytesPerPixel = (PixelFormats.Bgr32.BitsPerPixel + 7) / 8;
private byte[] colorPixels;
private byte[] depthPixels;
private short[] rawDepthData;
private bool first = true;
private bool firstDepth = true;
Image<Bgra, byte> image2;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
kinectSensorChooser.KinectSensorChanged += new DependencyPropertyChangedEventHandler(kinectSensorChooser_KinectSensorChanged);
}
void kinectSensorChooser_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e)
{
KinectSensor oldSensor = (KinectSensor)e.OldValue;
KinectStop(oldSensor);
KinectSensor _sensor = (KinectSensor)e.NewValue;
_sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
_sensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
_sensor.DepthFrameReady += new EventHandler<DepthImageFrameReadyEventArgs>(_sensor_DepthFrameReady);
_sensor.ColorFrameReady += new EventHandler<ColorImageFrameReadyEventArgs>(_sensor_ColorFrameReady);
_sensor.DepthStream.FrameHeight);
try
{
_sensor.Start();
}
catch
{
kinectSensorChooser.AppConflictOccurred();
}
}
void KinectStop(KinectSensor sensor)
{
if (sensor != null)
{
sensor.Stop();
}
}
private void Window_Closed(object sender, EventArgs e)
{
KinectStop(kinectSensorChooser.Kinect);
}
void _sensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame == null) return;
if (first)
{
this.colorPixels = new Byte[colorFrame.PixelDataLength];
first = false;
}
colorFrame.CopyPixelDataTo(this.colorPixels); //raw data in bgrx format
processColor();
}
}
void _sensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
{
using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
{
if (depthFrame == null) return;
if (firstDepth)
{
this.rawDepthData = new short[depthFrame.PixelDataLength];
firstDepth = false;
}
depthFrame.CopyPixelDataTo(rawDepthData);
processDepth();
}
}
private void processColor(){...}
private void processDepth(){...}
}
}
The processDepth function is as follows. I am just making an Image from the RAW depth data.
private void processDepth() {
GCHandle pinnedArray = GCHandle.Alloc(this.rawDepthData, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
cvDepthImage = new Image<Gray, Int16>(depthWidth, depthHeight, depthWidth << 1, pointer);
pinnedArray.Free();
depthImage.Source = BitmapSourceConvert.ToBitmapSource(cvDepthImage.Not().Bitmap);
}
The processColor function is as follows. Here just for the sake of it, I am trying to display the cloned image instead of extracting features, just to check the lag. When both streams are enabled (color and depth) the following function displays the colorImage properly, but as soon as I uncomment the commented lines, the UI hangs.
private void processColor() {
GCHandle handle = GCHandle.Alloc(this.colorPixels, GCHandleType.Pinned);
Bitmap image = new Bitmap(colorWidth, colorHeight, colorWidth<<2, System.Drawing.Imaging.PixelFormat.Format32bppRgb, handle.AddrOfPinnedObject());
handle.Free();
cvColorImage = new Image<Bgra, byte>(image);
image.Dispose();
BitmapSource src = BitmapSourceConvert.ToBitmapSource(cvColorImage.Bitmap);
colorImage.Source = src;
//image2 = new Image<Bgra, byte>(cvColorImage.ToBitmap()); //uncomment and it hangs
//featureImage1.Source = BitmapSourceConvert.ToBitmapSource(image2.Bitmap); //uncomment and it hangs
}
I see code that do a lot of work in event handlers. I almost certain that handlers is called in GUI thread. I suggest you to extract your code to the background thread routine. Don't forget that updating of the Form's controls (depthImage and controlImage) should be done using BeginInvoke method of the parent form,
I've been searching the internet and I haven't had any luck. I'm using an Xbox Kinect with the Kinect SDK v1.0. I want to take the raw depth data and convert it into a text document so I can use the depth data. I found something on this site but it was for the Beta2 and I need to use v1.0. Any help is appreciated but I am new to coding so sample code would be best.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
using System.Diagnostics;
using System.IO;
namespace DepthTextStream
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
const float MaxDepthDistance = 4095; // max value returned
const float MinDepthDistance = 850; // min value returned
const float MaxDepthDistanceOffset = MaxDepthDistance - MinDepthDistance;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
kinectSensorChooser1.KinectSensorChanged += new DependencyPropertyChangedEventHandler(kinectSensorChooser1_KinectSensorChanged);
}
void kinectSensorChooser1_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var oldSensor = (KinectSensor)e.OldValue;
//stop the old sensor
if (oldSensor != null)
{
oldSensor.Stop();
oldSensor.AudioSource.Stop();
}
//get the new sensor
var newSensor = (KinectSensor)e.NewValue;
if (newSensor == null)
{
return;
}
//turn on features that you need
newSensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
newSensor.SkeletonStream.Enable();
//sign up for events if you want to get at API directly
newSensor.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(newSensor_AllFramesReady);
try
{
newSensor.Start();
}
catch (System.IO.IOException)
{
//this happens if another app is using the Kinect
kinectSensorChooser1.AppConflictOccurred();
}
}
void newSensor_AllFramesReady(object sender, AllFramesReadyEventArgs e)
{
short[] depthData;
using (DepthImageFrame depthFrame = e.OpenDepthImageFrame()) //create a new frame every time one is ready
{
//assign a value to depthData
depthData = new short[depthFrame.PixelDataLength];
}
}
private void SaveDepthData(short[] depthData)
{
//initialize a StreamWriter
StreamWriter sw = new StreamWriter(#"C:/Example.txt");
//search the depth data and add it to the file
for (int i = 0; i < depthData.Length; i++)
{
sw.WriteLine(depthData[i] + "\n"); //\n for a new line
}
//dispose of sw
sw.Close();
SaveDepthData(depthData);
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
StopKinect(kinectSensorChooser1.Kinect);
}
private void StopKinect(KinectSensor sensor)
{
if (sensor != null)
{
if (sensor.IsRunning)
{
//stop sensor
sensor.Stop();
//stop audio if not null
if (sensor.AudioSource != null)
{
sensor.AudioSource.Stop();
}
}
}
}
}
}
This is pretty simple using version 1.5.0.1, which is practically the same as version 1.0 and will work on it. All you need to complete this is A)a short[] to hold the depth data B)a DepthImageFrame to move the data to the array, and C)A StreamWriter to save the data.
Add a short[] to store your depth data, and inside of your DepthFrameReadyEventArgs (or AllFramesReadyEventArgs) you "use" a DepthImageFrame by doing:
short[] depthData;
...
using (DepthImageFrame depthFrame = e.OpenDepthImageFrame(()) //create a new frame every time one is ready
{
//assign a value to depthData
depthData = new short[depthFrame.PixelDataLength];
}
Then you can add the depth from each frame to depthData using DepthImageFrame.CopyPixelDataTo
using (DepthImageFrame depthFrame = e.OpenDepthImageFrame(()) //create a new frame every time one is ready
{
//assign a value to depthData
depthData = new short[depthFrame.PixelDataLength];
//add raw depth data to depthData
depthFrame.CopyPixelDataTo(depthData);
}
Then we can write a method to save our data using a StreamWriter.
private void SaveDepthData(short[] depthData)
{
//initialize a StreamWriter
StreamWriter sw = new StreamWriter(#"C:/Example.txt");
//search the depth data and add it to the file
for (int i = 0; i < depthData.Length; i++)
{
sw.WriteLine(depthData[i] + "\n"); //\n for a new line
}
//dispose of sw
sw.Close();
}
...
SaveDepthData(depthData);
Hope this helps!