I have found a very usefull class on this link: images caching - that help me to make logic for caching images. But in my case I have this:
private void DetailView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SaveAndLoadImage(feedItem);
}
And in this method I save and load image from isolated storage. But I can't load file imidiately because of some permission (Operation not permitted on IsolatedStorageFileStream.). How can I correct my logic to save and load images immediately?
public void SaveAndLoadImage(MediaItemViewModel curItem)
{
string url = string.Empty;
if (!string.IsNullOrEmpty(curItem.ThumbUrl))
{
url = curItem.ThumbUrl;
}
if ((string.IsNullOrEmpty(curItem.ThumbUrl)) && (!string.IsNullOrEmpty(curItem.MediaUrl)))
{
url = curItem.MediaUrl;
}
if ((!string.IsNullOrEmpty(url)) && (CacheImageFile.GetInstance().IsOnStorage(new Uri(url)) == false))
{
CacheImageFile.DownloadFromWeb(new Uri(url));
}
curItem.ImageSource = CacheImageFile.ExtractFromLocalStorage(new Uri(url)) as BitmapImage;
}
have a look at below link
http://www.windowsphonegeek.com/tips/All-about-WP7-Isolated-Storage---Read-and-Save-Images
for loading images from isolated storage. using streams
BitmapImage bi = new BitmapImage();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile("logo.jpg", FileMode.Open, FileAccess.Read))
{
bi.SetSource(fileStream);
this.img.Height = bi.PixelHeight;
this.img.Width = bi.PixelWidth;
}
}
this.img.Source = bi;
You are getting the image asynchronously from the web, but immediately go to the next line to read the file that hasn't even been written to isolated storage. This is what's causing you the exception.
You could try editing the caching library you found on github, using ManualResetEvent. Notice that you will have to make the method calls on another thread!
For example:
public class CacheImageFileConverter : IValueConverter
{
...
private static ManualResetEvent mre = new ManualResetEvent(true);
private static object DownloadFromWeb(Uri imageFileUri)
{
mre.Reset();
WebClient m_webClient = new WebClient(); //Load from internet
BitmapImage bm = new BitmapImage();
m_webClient.OpenReadCompleted += (o, e) =>
{
if (e.Error != null || e.Cancelled) return;
WriteToIsolatedStorage(IsolatedStorageFile.GetUserStoreForApplication(), e.Result, GetFileNameInIsolatedStorage(imageFileUri));
bm.SetSource(e.Result);
e.Result.Close();
mre.Set();
};
m_webClient.OpenReadAsync(imageFileUri);
return bm;
}
private static object ExtractFromLocalStorage(Uri imageFileUri)
{
mre.WaitOne();
string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storage
using (var sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open, FileAccess.Read))
{
BitmapImage bm = new BitmapImage();
bm.SetSource(sourceFile);
return bm;
}
}
.... other methods
}
Notice the use of Reset, Set and WaitOne for signaling.
You can use JetImageLoader, I created it for application, where we need to load, cache and show big amount of logos, icons and so on.
It can be used as binding converter, so you should not even change your code! Just update your XAMLs!
Please, check out samples in repository, you'll love it ;)
Features:
Caching on disk
Caching in memory
Fully asynchronous
Available as binding converter or programmatically from your code
Fully open source, fork and improve it!
Here is the example:
<Image Source="{Binding ImageUrl, Converter={StaticResource MyAppJetImageLoaderConverter}}"/>
P.S. I am sorry, that I copying my answer from another questions, but image caching on windows phone is huge problem and I want to share my solution, so everybody can use it and improve for developers community
Related
What is the correct way to get the thumbnails of images when using C#? There must be some built-in system method for that, but I seem to be unable find it anywhere.
Right now I'm using a workaround, but it seems to be much heavier on the computing side, as generating the thumbnails of 50 images, when using parallel processing takes about 1-1,5 seconds, and during that time, my CPU is 100% loaded. Not to mention that it builds up quite some garbage, which it later needs to collect.
This is what my class currently looks like:
public class ImageData
{
public const int THUMBNAIL_SIZE = 160;
public string path;
private Image _thumbnail;
public string imageName { get { return Path.GetFileNameWithoutExtension(path); } }
public string folder { get { return Path.GetDirectoryName(path); } }
public Image image { get
{
try
{
using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(stream))
{
var memoryStream = new MemoryStream(reader.ReadBytes((int)stream.Length));
return new Bitmap(memoryStream);
}
}
catch (Exception e) { }
return null;
}
}
public Image thumbnail
{
get
{
if (_thumbnail == null)
LoadThumbnail();
return _thumbnail;
}
}
public void LoadThumbnail()
{
if (_thumbnail != null) return;
Image img = image;
if (img == null) return;
float ratio = (float)image.Width / (float)image.Height;
int h = THUMBNAIL_SIZE;
int w = THUMBNAIL_SIZE;
if (ratio > 1)
h = (int)(THUMBNAIL_SIZE / ratio);
else
w = (int)(THUMBNAIL_SIZE * ratio);
_thumbnail = new Bitmap(image, w, h);
}
I am saving up the thumbnail once generated, to save up some computing time later on. Meanwhile, I have an array of 50 elements, containing picture boxes, where I inject the thumbnails into.
Anyways... when I open a folder, containing images, my PC certainly doesn't use up 100% CPU for the thumbnails, so I am wondering what is the correct method to generate them.
Windows pregenerates the thumbnails and stores them in the thumbs.db-File (hidden) for later use.
So unless you either access the thumbs.db file and are fine with relying on it being available or cache the thumbnails yourself somewehere you always will have to render them in some way or another.
That being said, you can probably rely on whatever framework you are using for your UI to display them scaled down seeing as you load them into memory anyway.
I have created a test project just for the case. I have one Image control in my .xaml like that:
<Image x:Name="img" />
I have tested the project with 6 pics, and all of them from the same web Site. Size of images which are shown is approximately 50 - 90 KB. And the image which isn't showing is 294 KB.
And I am setting the source of image like that:
img.Source = new BitmapImage(new Uri(imageURI));
What could be a problem?
Thanks.
UPDATE1:
Also, I have ckecked ImageFailed event. It is throwing AG_E_NETWORK_ERROR exception.
UPDATE2:
Here is the source of image which is not showing:
(deleted)
The image in question have hot-link protection on.
That is most likely the culprit preventing you from being able to download it. And given the hotlink protection, I'd guess you don't have the necessary rights to use it in a application either.
If you wish to work around this, use the HttpWebRequest class and set the HttpWebRequest.Referer property.
Thanks to #Claus Jørgensen, I have learned that some web sites can use hot-link protection to prevent other websites from directly linking to files and pictures on your website.
So I create an AttachedProperty for binding the source of Image to URI and download it asynchronously.
Here is .xaml:
<Image AttachedProperties:ImageProperties.SourceWithCustomReferer="{Binding Image, Mode=TwoWay}"/>
And AttachedProperty:
public static class ImageProperties
{
#region SourceWithCustomReferer Property
public static Dictionary<Uri, BitmapImage> imageCache = new Dictionary<Uri, BitmapImage>();
public static readonly DependencyProperty SourceWithCustomRefererProperty =
DependencyProperty.RegisterAttached(
"SourceWithCustomReferer",
typeof(Uri),
typeof(ImageProperties),
new PropertyMetadata(OnSourceWithCustomRefererChanged));
private static void OnSourceWithCustomRefererChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var image = (Image)o;
var uri = (Uri)e.NewValue;
if (DesignerProperties.IsInDesignTool)
{
// for the design surface we just load the image straight up
image.Source = new BitmapImage(uri);
}
else
{
if (imageCache.ContainsKey(uri))
{
image.Source = imageCache[uri];
return;
}
image.Source = null;
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Headers["Referer"] = "http://www.WEBSITE.com"; // or your custom referer string here
request.BeginGetResponse((result) =>
{
try
{
Stream imageStream = request.EndGetResponse(result).GetResponseStream();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.CreateOptions = BitmapCreateOptions.BackgroundCreation;
bitmapImage.SetSource(imageStream);
image.Source = bitmapImage;
imageCache.Add(uri, bitmapImage);
});
}
catch (WebException)
{
// add error handling
}
} , null);
}
}
public static Uri GetSourceWithCustomReferer(Image image)
{
if (image == null)
{
throw new ArgumentNullException("Image");
}
return (Uri)image.GetValue(SourceWithCustomRefererProperty);
}
public static void SetSourceWithCustomReferer(Image image, Uri value)
{
if (image == null)
{
throw new ArgumentNullException("Image");
}
image.SetValue(SourceWithCustomRefererProperty, value);
}
#endregion
}
I'm working on a project using WPF to display the Kinect ColorImageFrame and a skeleton representation. I also have to record those two videos.
I'm able to display and record (using EmguCV) those two images, but I have some performance issues. It seems that this part of my code is the reason of my loss of performance.
private void DrawSkeleton(Skeleton[] skeletons)
{
using (System.Drawing.Bitmap skelBitmap = new System.Drawing.Bitmap(640, 480))
{
foreach (Skeleton S in skeletons)
{
if (S.TrackingState == SkeletonTrackingState.Tracked)
{
DrawBonesAndJoints(S,skelBitmap);
}
else if (S.TrackingState == SkeletonTrackingState.PositionOnly)
{
}
}
_videoArraySkel.Add(ToOpenCVImage<Bgr, Byte>(skelBitmap));
BitmapSource source = ToWpfBitmap(skelBitmap);
this.skeletonStream.Source = source;
}
}
and more precisely from the ToWpfBitmap which allows me to display it in my Window:
public static BitmapSource ToWpfBitmap(System.Drawing.Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Position = 0;
BitmapImage result = new BitmapImage();
result.BeginInit();
// According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
// Force the bitmap to load right now so we can dispose the stream.
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = stream;
result.EndInit();
result.Freeze();
return result;
}
}
The loss of performance is characterized by:
- The videos displayed on the Window are not fluent anymore
- The video recording seems to miss some frames which leads to a video going faster/lower than the normal.
Can you help me by telling me where this problem may come from?
Try to use RecyclableMemoryStream instead of MemoryStream. It was designed for solving some issue with memory.
Check out this article for details - Announcing Microsoft.IO.RecycableMemoryStream
Have you tried doing the memory write i/o in a separate thread, while maintaining the data in a buffer like a queue?
I'm developing an application that uses a mobile device to take a photo and send it using a webservice. But after I've taken 4 photos I am getting an OutOfMemoryException in the code below. I tried calling GC.Collect() but it didn't help either. Maybe someone here could be give me an advice how to handle this problem.
public static Bitmap TakePicture()
{
var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
};
dialog.ShowDialog();
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(dialog.FileName))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(dialog.FileName);
File.Delete(dialog.FileName);
return bitmap;
}
The function is called by an event handler:
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
var image = Camera.TakePicture();
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
I suspect you are holding onto references. As a minor cause, note that dialogs don't dispose themselves when using ShowDialog, so you should be using the dialog (although I would expect GC to still collect an undisposed but non-referenced dialog).
Likewise, you should probably be using the image, but again: not sure I'd expect this to make-or-break; worth a try, though...
public static Bitmap TakePicture()
{
string filename;
using(var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
}) {
dialog.ShowDialog();
filename = dialog.FileName;
}
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(filename))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(filename);
File.Delete(filename);
return bitmap;
}
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
using(var image = Camera.TakePicture()) {
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
}
I'd also be a little cautious of the CutBitmap etc, to ensure that things are released ASAP.
Your mobile device usually does not have any memory swapping to disk option, so since you choose to store your images as bitmaps in memory rather than files on disk, you quickly consume your phone's memory. Your "new Bitmap()" line allocates a large chunk of memory, so it is very likely to throw the exception there. Another contender is your Camera.ImageToByteArray that will allocate a large amount of memory. This probably isn't large to what you're used to with your computer, but for your mobile this is gigantic
Try keeping the pictures on disk until you use them, i.e. until sending them to the webservice. For displaying them, use your built-in controls, they are probably the most memory efficient and you can usually point them to the image files.
Cheers
Nik
Here is what I am trying to do: Thread B will download some images and store those images in a shared resource: Static ArrayList IMBuffer; thread A will take an image from IMBuffer and do something with it. The following is what I got:
Thread B:
// do something
System.Net.WebClient myWebClient = new System.Net.WebClient();
try
{ myWebClient.DownloadFile(pth, "BufferImg"); }
catch
{ // some stuff }
// add new dled image to IMBuffer
fs = new FileStream("BufferImg", FileMode.Open, FileAccess.Read);
Image img = Image.FromStream(fs);
lock (IMBuffer)
{ IMBuffer.Add(img); }
img.Dispose();
lock (IMRequest) { IMRequest.RemoveAt(0); }
myWebClient.Dispose();
//fs.Dispose();
// File.Delete("BufferImg");
// do something else
Thread A:
// do something
Image nextImg;
lock (IMBuffer)
{
nextImg = (Image)IMBuffer[0];
nextImg.Save(DLedIM);
}
// do something else
and here is the problem I am running to; since the images in IMBuffer was opened using a filestream, when the stream is disposed, the line: nextImg.Save(DLedIM); is causing "file is been used by another process" error. However if fs.Dispose(); line is commented out, then the program is locking up "BufferImg", as the result, it won't be able to download image to "BufferImg" after the first time. What should I do to fix this problem? Or is there a simpler way to accomplish what I am trying to do?
This should work:
byte[] buffer;
using (FileStream fs = new FileStream("BufferImg", FileMode.Open, FileAccess.Read))
{
buffer = new byte[fs.Length];
fs.Read(buffer, 0, (int)fs.Length);
}
using(Image img = Image.FromStream(new MemoryStream(buffer))
{
//...
}
Using a MemoryStream you avoid having to hold on to the FileStream - at this point the image does not have any connection at all to the file and hence you shouldn't have any problem with file locking.
Instead of implementing your own multithread producer/consumer workflow (and then having to debug it), why not just use an existing threadsafe (nay, concurrent) queue provided by .NET? Could save you a lot of trial and even more errors.
Details here: Thread-safe blocking queue implementation on .NET