Image to memory slow - c#

I mostly would like advice..
My program displays multiple Large images on the screen..
Which are between 1-4 mbs each..
150 DPI, and 6000x4000 resolution, 24 bit depth..
I have many other settings which will give me smaller images.. but I need high qualitity images..
Ok, so It's slow.. but not that slow...
The only really bad part is when I switch from 1 image to the other..
So, I have basically 2 Images for every image.. 1 Thumb, and 1 HQ..
When the user zooms into an image I load the HQ, and when the user zooms out, it switches to the thumb image..
Right at that switching point, the user has to wait about 4-8 seconds for the HQ image to load into the memory and draw to the screen..
Below is the code I use when I load the HQ image
using (DrawingContext dc = ActiveImage.imageDV.RenderOpen())
{
FileStream fs = new FileStream(ActiveImage.imagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
MemoryStream ms = new MemoryStream();
fs.CopyTo(ms);
ms.Seek(0, SeekOrigin.Begin);
fs.Close();
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
bitmap.StreamSource = ms;
bitmap.EndInit();
//bitmap.Freeze();
dc.DrawImage(bitmap, new Rect(ActiveImage.position, ActiveImage.size));
}
Below is the code I use when I load the Thumb image
using (DrawingContext dc = ActiveImage.imageDV.RenderOpen())
{
FileStream fs = new FileStream(ActiveImage.thumbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
MemoryStream ms = new MemoryStream();
fs.CopyTo(ms);
ms.Seek(0, SeekOrigin.Begin);
fs.Close();
BitmapImage bitmap = new BitmapImage();
RenderOptions.SetBitmapScalingMode(bitmap, BitmapScalingMode.NearestNeighbor);
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
bitmap.StreamSource = ms;
bitmap.EndInit();
//bitmap.Freeze();
dc.DrawImage(bitmap, new Rect(ActiveImage.position, ActiveImage.size));
}
Any way to optimize this? Is there some better way?
Remember I need a HQ crystal clear image only when the user is looking at a single image..
I suppose the MemoryStream could come out, but I added that as a kind of test..

Just a guess, but wouldn't it perhaps be the "best" solution to leave all potential optimization to WPF, and just load the image by its URI:
using (DrawingContext dc = ActiveImage.imageDV.RenderOpen())
{
var bitmap = new BitmapImage(new Uri(ActiveImage.imagePath));
// alternatively, if it is a relative path
// var bitmap = new BitmapImage(new Uri(ActiveImage.imagePath, UriKind.Relative));
dc.DrawImage(bitmap, new Rect(ActiveImage.position, ActiveImage.size));
}

Related

WPF: MemoryStream Occupying large amount of memory

I am using MemoryStram for converting Bitmap to BitmapImage and when I checked the CPU usage it is consuming more memory. I wanted to reduce the memory consumption of MemoryStream object. I used it in Using statement also and the result is same as earlier mentioned.
I am pating my code snippet please can anyone help me out to find the solution or any other alternative that can be used.
Code :
public static BitmapImage ConvertBitmapImage(this Bitmap bitmap)
{
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
System.Windows.Media.Imaging.BitmapImage bImg = new System.Windows.Media.Imaging.BitmapImage();
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(ms.ToArray());
bImg.EndInit();
return bImg;
}
}
Or
public static BitmapImage ConvertBitmapImage(this Bitmap bitmap)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
return bi;
}
There is no need for a second MemoryStream.
Just rewind the one that the Bitmap was encoded to, before decoding the BitmapImage, and set BitmapCacheOption.OnLoad to make sure that the stream can be closed after EndInit():
public static BitmapImage ConvertBitmapImage(this System.Drawing.Bitmap bitmap)
{
var bImg = new BitmapImage();
using (var ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Position = 0; // here, alternatively use ms.Seek(0, SeekOrigin.Begin);
bImg.BeginInit();
bImg.CacheOption = BitmapCacheOption.OnLoad; // and here
bImg.StreamSource = ms;
bImg.EndInit();
}
return bImg;
}
Note that there are also other ways of converting between Bitmap and BitmapImage, e.g. this: fast converting Bitmap to BitmapSource wpf

Convert WriteableBitmap to BitmapImage using BmpBitmapEncoder WPF

I have a problem with Convert WriteableBitmap to BitmapImage using BmpBitmapEncoder.
this is my method:
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
/*PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);*/
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmp.BeginInit();
bmp.UriSource = new Uri(MyImage.Source.ToString());
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
I'm using BmpBitmapEncoder because this is only way to Save without change size of Image(*.bmp). I want to Save Image with changed table of pixels and the specified format pixel (Bgr24). Using BmpBitmapEncoder forces to set bmp.UriSource and this is a problem. WriteableBitmap doesn't have this Property. Moreover, when I comment line //bmp.UriSource shows me an exception: "System.ArgumentNullException" in bmp.EndInit().
When I change my Method to this:
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
/*BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);*/
bmp.BeginInit();
//bmp.UriSource = new Uri(MyImage.Source.ToString());
bmp.CacheOption = BitmapCacheOption.OnLoad;
//bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
everything works fine but the result is the Image increase size and change pixel format to Bgr32 and this is not the result, what I expect. My method which Save Image is fine because I tested it on unchanged pixels and the result is good - Image don't change format and size. Plz help me with this.
For changing pixel formats WPF has the FormatConvertedBitmap class, which is a BitmapSource:
WriteableBitmap wbm;
...
var bm = new FormatConvertedBitmap(wbm, PixelFormats.Bgr24, null, 0);
It is not a BitmapImage, but you do not really need that anywhere. All WPF methods and properties (e.g. Image.Source) use either ImageSource or the derived BitmapSource as their type. There is no API that explicitly needs a BitmapImage, so conversion to BitmapImage is never necessary.
I resolve this problem. This is a code which Convert WriteableBitmap to BitmapImage using BmpBitmapEncoder. Using this method ensure us, that our Image while saving stay unchanged. Unchanged I mean - size don't increase and Format Pixel stay on Bgr24.
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = new MemoryStream(stream.ToArray()); //stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}

Update BitmapImage every second flickers

I am trying to update an image by setting the source property every second, this works however causes a flicker when updated.
CurrentAlbumArt = new BitmapImage();
CurrentAlbumArt.BeginInit();
CurrentAlbumArt.UriSource = new Uri((currentDevice as AUDIO).AlbumArt);
CurrentAlbumArt.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
CurrentAlbumArt.EndInit();
If I don't set IgnoreImageCache, the image does not update thus no flickering either.
Is there a way around this caveat?
Cheers.
The following code snippet downloads the whole image buffer before setting the Image's Source property to a new BitmapImage. This should eliminate any flicker.
var webClient = new WebClient();
var url = ((currentDevice as AUDIO).AlbumArt;
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(webClient.DownloadData(url)))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
image.Source = bitmap;
If the download takes some time, it would make sense to run it in a separate thread. You would then have to take care for proper cross-thread access by also calling Freeze on the BitmapImage and assigning Source in the Dispatcher.
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(webClient.DownloadData(url)))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
image.Dispatcher.Invoke((Action)(() => image.Source = bitmap));

wpf - Can i use System.Drawing in wpf?

i am saving the image in database. .. but how to retrieve that image from database .. when i try to use system.drawing .. it shows an error .. some of ppl saying i can't use system.drwaing in wpf .. not even dll file ..
my code is
private void btnShow_Click(object sender, RoutedEventArgs e)
{
DataTable dt2 = reqBll.SelectImage().Tables[0];
byte[] data = (byte[])dt2.Rows[0][1];
MemoryStream strm = new MemoryStream();
strm.Write(data, 0, data.Length);
strm.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(strm);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
ImgBox.Source = bi;
}
what to do now?
i used the system.drawing.dll .. now i can use system.drawing.bitmap .. but after using it shows an error at System.Drawing.Image.FromStream(strm);
error:- argument exception was unhandled by user code
Parameter is not valid.
You can use the classes in the System.Drawing namespace, but you will have to add a reference to the assembly containing the class you're interested in, by right clicking on the project, and choosing the "Add Reference..." option
Your code is fine as far as the drawing part is concerned, the problem is probably with the image data you are trying to load from the database (might be caused by mismatched data format or choosing the wrong column etc.). You might want to share the code that saves the image to the database, since there is no way to know without it.
This code sample does what you want (I commented out the database related part and substituted it with file loading):
private void btnShow_Click(object sender, RoutedEventArgs e)
{
// DataTable dt2 = reqBll.SelectImage().Tables[0];
// byte[] data = (byte[]) dt2.Rows[0][1];
// MemoryStream strm = new MemoryStream();
// strm.Write(data, 0, data.Length);
System.Drawing.Image bmp = System.Drawing.Bitmap.FromFile(#"C:\Temp\test.png");
MemoryStream strm = new MemoryStream();
bmp.Save(strm, System.Drawing.Imaging.ImageFormat.Bmp);
strm.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(strm);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
imgBox.Source = bi;
}
With that said, if this is a new application, using WPF solely is preferable to mixing Windows Forms and WPF classes and elements (as Jeff Mercado pointed out).

Why won't GDI let me delete large images?

My ASP.NET application has an image cropping and resizing features. This requires that the uploaded temporary image be deleted. Everything works fine, but when I try to delete an image larger than 80px by 80px I get a "File is locked by another process..." error, even though I've released all resources.
Here's a snippet:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone(); //advice from another forum
tempimg.Dispose();
img = resizeImage(img, 200, 200); //delete only works if it's 80, 80
img.Save(newpath);
img.Dispose();
File.Delete(temppath);
I think you are not disposing the first Image instance assigned to the img variable.
Consider this instead:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone();
tempimg.Dispose();
System.Drawing.Image img2 = resizeImage(img, 200, 200);
img2.Save(newpath);
img2.Dispose();
img.Dispose();
File.Delete(temppath);
If you create the image this way, it won't be locked:
using (FileStream fs = new FileStream(info.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byte[] data = new byte[fs.Length];
int read = fs.Read(data, 0, (int)fs.Length);
MemoryStream ms = new MemoryStream(data, false);
return Image.FromStream(ms, false, false); // prevent GDI from holding image file open
}

Categories