I am trying to save a file(BitmapImage) to a certain location, but as soon as I use async & await I get an error saying the file is in use:
The process cannot access the file 'C:\Users\ ... \image1.jpg' because
it is being used by another process.
My coding:
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
await Task.Run(() => encoder.Save(filestream)); //Error here
When I use the code without the await, the code works perfectly fine. I think it might be because another thread might be using it, but can anyone help or explain to me a work around for my issue? Thank you.
In your case when you use Task with await another thread is used to save your encoder. But your encoder is also used by your main thread so new thread can't use it.
Change your code a little:
await Task.Run(() =>
{
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
{
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(filestream);
}
}
Now you create and save your encoder in the same task and it will use only one thread.
I think you should move the code inside the Task.Run, because it gets called inside another thread.
You could encode to a MemoryStream, get the byte array, use WriteAsync on the FileStream, and avoid using Task.Run altogether.
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
using (var mem = new MemoryStream())
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
{
encoder.Save(mem);
var data = mem.ToArray();
await filestream.WriteAsync(date, 0, data.Length);
}
Note that this will block your thread during the encoding and will use more memory.
Related
The calling thread cannot access this object because a different thread owns it even after adding Dispatcher.Invoke.
The problem is still intact even after adding Dispatcher.Invoke.
async Task capturePredict()
{
await Dispatcher.Invoke( async () =>
{
PngBitmapEncoder image = new PngBitmapEncoder();
image.Frames.Add(BitmapFrame.Create(bitmap));
using (Stream stream = File.Create(#"E:\ImageClassificationTraining\image.png"))
{
await Task.Run(() => image.Save(stream));
}
});
}
In contrast to decoding a BitmapSource (which can be frozen to make it cross-thread accessible), encoding can seemingly not be done in a thread other than the UI thread.
You may however separate the encoding step from writing the file, by something like this:
public async Task SaveImageAsync(BitmapSource bitmap, string path)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
memoryStream.Position = 0;
using (var fileStream = File.Create(path))
{
await memoryStream.CopyToAsync(fileStream);
}
}
}
I am trying to read read a BitmapImage from local storage. The path is selected by FileOpenPicker. But when I try to convert it into Byte[], it fails. It seems like image is not downloaded at all, because image.PixelWidth returns '0'.
StorageFile storageFile = args.Files[0];
//var stream = await storageFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
//var bitmapImage = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
//await bitmapImage.SetSourceAsync(stream);
var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
BitmapImage wb = new BitmapImage(new Uri(storageFile.Path));
byte[] pixeBuffer = null;
using (MemoryStream ms = new MemoryStream())
{
WriteableBitmap wbit = new WriteableBitmap(wb.PixelWidth,wb.PixelHeight);
Stream s1 = wb.PixelBuffer.AsStream();
s1.CopyTo(ms);
pixeBuffer = ms.ToArray();
}
The program crashes at
WriteableBitmap wbit = new WriteableBitmap(wb.PixelWidth,wb.PixelHeight);
I have checked that storageFile.Path points to right path, I have tried URL from internet too but same results.
I am new to C# and don't know how to trigger exception handling for windows phone, please guide me if it may help.
After calling the decoder you should be doing something like:
try
{
PixelDataProvider provider = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, new BitmapTransform(), ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
byte[] pixelBuffer = provider.DetachPixelData();
}
catch(Exception ex)
{
Debug.WriteLine(ex);
}
The try/catch block is the way you would want to catch exceptions in your code so I included it too.
I am using below code to save a remote image in Windows Phone 8. But i keep hitting with System.NotSupportedException: Specified method is not supported. exception at SaveJpeg() method call.
I tried different combinations of method call (you can see commented line). I couldn't able to figure out what i am doing incorrectly.
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(imageUrl);
await Task.Run(async () =>
{
if (response.IsSuccessStatusCode)
{
// save image locally
Debug.WriteLine("Downloading image..." + imageName);
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!myIsolatedStorage.DirectoryExists("Images"))
myIsolatedStorage.CreateDirectory("Images");
string path = imageName;
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(path);
var buffer = await response.Content.ReadAsStreamAsync();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
BitmapImage bitmap = new BitmapImage { CreateOptions = BitmapCreateOptions.None };
bitmap.SetSource(buffer);
WriteableBitmap wb = new WriteableBitmap(bitmap);
//System.Windows.Media.Imaging.Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 100);
wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 98);
});
fileStream.Close();
}
}
});
}
By putting the code block in BeginInvoke block you are calling the SaveJpeg on a different thread (the "UI thread") to the code which calls fileStream.Close().
In effect this means it is very likely that the call to fileStream.Close() will be called before wb.SaveJpeg.
If you move the fileStream.Close() inside the BeginInvoke block, after wb.SaveJpeg() it should work.
I am trying to make images load in different thread but image is never updated.
public class MyImage : System.Windows.Controls.Image
{
public MyImage()
{
this.Loaded += new RoutedEventHandler(MyImage_Loaded);
}
void MyImage_Loaded(object sender, RoutedEventArgs e)
{
//mypath=#"c:\test.jpg";
var t = Task<ImageSource>.Factory.StartNew(() => GetImage(mypath));
Source = t.Result;
}
Following works but it is the UI thread:
Source = GetImage(mypath);
I tried the same with BackgroundWorker but the result is the same.
Is it possible to do it like this without MVVM?
Because you create ImageSource on different thread then the one you want to use it on you get
The calling thread cannot access this object because a different thread owns it
exception and to solve your issue you should call Freeze() on your ImageSource. However even though you load image on different thread your code blocks UI thread until it's ready:
var t = Task<ImageSource>.Factory.StartNew(() => GetImage(mypath));
Source = t.Result; //this line blocks UI thread until Result is ready
If you want to avoid that change it to:
Task.Factory.StartNew(() =>
{
var source = GetImage(mypath);
source.Freeze();
this.Dispatcher.Invoke(() => this.Source = source);
});
This should load image on different thread and update Source when it's ready without blocking UI thread
Try returning a byte[] or a stream from your GetImage (or from its result), then use Dispatcher.Invoke to call a helper on the UI thread that reconstructs the image from the byte[] or stream and updates MyImage's Source.
This is my working POC. You want to examine it for proper disposal of the stream and so on.
(Note: disp is the Dispatcher to "post" to the UI)
Task.Factory.StartNew(() =>
{
var result = GetImage();
var bitmap = new BitmapImage();
var stream = result;
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
stream.Dispose();
disp.Invoke(new Action(()=> MyImage.Source = bitmap));
});
And the GetImage():
private MemoryStream GetImage()
{
System.Threading.Thread.Sleep(35000);
var filePath = #"C:\temp\2013-01-08-235341.jpg";
MemoryStream memoryStream = null;
if (File.Exists(filePath))
{
memoryStream = new MemoryStream();
byte[] fileBytes = File.ReadAllBytes(filePath);
memoryStream.Write(fileBytes, 0, fileBytes.Length);
memoryStream.Position = 0;
}
return memoryStream;
}
I am trying to add some text on top of a photo take by the camera, and here is the method I am using, but unfortunately I either get a closedStream error, or that there is cross-thread access when I try use the dispatcher. Could someone please explain me what is going wrong?
void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
{
DateTime dt = DateTime.Now;
string fileName = dt.Year.ToString() + dt.Month.ToString() + dt.Day.ToString() + dt.Hour.ToString() + dt.Minute.ToString() + dt.Second.ToString() + ".jpg";
try
{
// Save picture to the library camera roll.
library.SavePictureToCameraRoll(fileName, e.ImageStream);
// Set the position of the stream back to start
e.ImageStream.Seek(0, SeekOrigin.Begin);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// load photo to writable bitmap
WriteableBitmap writeableBitmap = PictureDecoder.DecodeJpeg(e.ImageStream);
writeableBitmap.Invalidate();
var renderText = new TextBlock
{
Text = "Hello World",
FontSize = 72,
Foreground = new SolidColorBrush(Colors.White),
FontWeight = FontWeights.Black,
Width = 500,
Height = 100
};
writeableBitmap.Render(renderText, new TranslateTransform() { X = 100, Y = 300 });
writeableBitmap.Invalidate();
using (var ms = new MemoryStream())
{
writeableBitmap.SaveJpeg(ms, 1024, 768, 0, 100);
ms.Seek(0, SeekOrigin.Begin);
library.SavePicture("x" + fileName, ms);
}
// e.ImageStream.Close();
});
// Save picture as JPEG to isolated storage.
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
{
// Initialize the buffer for 4KB disk pages.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the image to isolated storage.
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
targetStream.Write(readBuffer, 0, bytesRead);
}
}
}
}
finally
{
// Close image stream
e.ImageStream.Close();
}
}
With the code above I get the following error: Cannot access a closed Stream.
If i remove the Dispatcher, I get this error: Invalid cross-thread access.
Thanks.
place ui less code outside dispatcher scope.
or save your e.ImageStream to other stream can be gotted in dispatcher scope.
First, what is occurring?
Dispatcher.BeginInvoke postpones your code and tells the UI thread to execute it whenever it's available. Therefore, your e.ImageStream.Close(); line is executed before the code inside of the BeginInvoke. So when you're trying to read the contents of the stream, it's already closed.
Two ways to solve that:
Remove the e.ImageStream.Close(); from the finally block. But I don't know if the stream will remain open anyway.
If 1. doesn't work, copy the contents of the stream to a MemoryStream, then use this stream to create the WriteableBitmap:
var stream = new MemoryStream();
e.ImageStream.CopyTo(stream);
In both case, don't forget to close the stream when you're done creating the WriteableBitmap.