I am trying to use the WriteableBitmap object because I need it for rotating images and saving images to the Isolated Storage of my app.
The problem is, it uses so much memory it eventually causes an out of memory exception.
Here is a picture of the memory usage of my App, with the picture link here for better viewing.
Here's an instance of where I use a WriteableBitmap:
WriteableBitmap picture = new WriteableBitmap(PictureCanvas, null);
using (var memoryStream = new MemoryStream())
{
picture.SaveJpeg(memoryStream, picture.PixelWidth, picture.PixelHeight, 0, 100);
using (var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(Globals.OVERLAY_FILE_NAME, FileMode.Create, myIsolatedStorage))
{
fileStream.Write(memoryStream.ToArray(), 0, memoryStream.ToArray().Length);
fileStream.Close();
}
}
}
picture = picture.Crop(0, 0, 1, 1);
I try cropping the image to make it take up less memory, but that doesn't do anything.
I am using the WriteableBitmap extensions library here and in the front page it mentions a Dispose() method, but I don't see it in my App.
If someone could please tell me how to get around this problem or point me somewhere I can find a possible solution, that would be awesome!
I'm having a similar issue and still investigating but at least a small tip I can give : if possible get rid of the MemoryStream and write directly to the fileStream like so :
WriteableBitmap picture = new WriteableBitmap(PictureCanvas, null);
using (var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(Globals.OVERLAY_FILE_NAME, FileMode.Create, myIsolatedStorage))
{
picture.SaveJpeg(fileStream, picture.PixelWidth, picture.PixelHeight, 0, 100)
}
This should buy you some memory.
Related
I'm developing a Winforms application. This code contains it to save an image to a SQL Server table. But sometimes a Out of memory exception happens when I'm returning the binary data from the table to display the image.
This is my code to convert image before save.
openImage.Filter = "Image Files(*.jpg; *.jpeg; *.gif; *.bmp)|*.jpg; *.jpeg; *.gif; *.bmp";
if (openImage.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(openImage.FileName);
pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
lblImgInfo.Text = openImage.FileName;
FRM_ImageViewer imgver = new FRM_ImageViewer(openImage.FileName);
imgver.Show();
string strFn = openImage.FileName;
FileInfo fiImage = new FileInfo(strFn);
long m_lImageFileLength =fiImage.Length;
byte[] m_barrImg = new byte[Convert.ToInt32(m_lImageFileLength)];
FileStream fs = new FileStream(strFn, FileMode.Open, FileAccess.Read, FileShare.Read);
int iBytesRead = fs.Read(m_barrImg, 0,Convert.ToInt32(m_lImageFileLength));
fs.Close();
}
and this is my code to retrieve the image from the database
// DTSelectedJobs is a DataTable in csharp.
byte[] barrImg = (byte[])DTSelectedJobs.Rows[0].ItemArray[11];
string strfn = Convert.ToString(DateTime.Now.ToFileTime());
FileStream fs2 = new FileStream(strfn, FileMode.CreateNew, FileAccess.Write);
fs2.Write(barrImg, 0, barrImg.Length);
fs2.Flush();
fs2.Close();
FRM_ImageViewer imgvwr = new FRM_ImageViewer(strfn);
imgvwr.Show();
Can someone please give me an advice how to find the error?
Bitmap inherits from Image which is Disposable, you need to dispose of your bitmap.
using(var bmp = new Bitmap(openImage.FileName))
{
pictureBox1.Image = bmp;
}
As DGibbs notes, you need to do the same for FileStream
using(var fs = new FileStream(strFn, FileMode.Open, FileAccess.Read,
FileShare.Read))
int iBytesRead = fs.Read(m_barrImg, 0,Convert.ToInt32(m_lImageFileLength));
I'm not all that familiar with winforms so bear with me but this looks incorrect to me.
You create a strfn:
string strfn = Convert.ToString(DateTime.Now.ToFileTime());
And then initialize an image viewer with... a date/time string?
FRM_ImageViewer imgvwr = new FRM_ImageViewer(strfn);
Seems like barrImg is what should be in the ctor here?
Also, FileStream implements the IDisposable interface so should be ideally wrapped in a using block:
using(FileStream fs2 = new FileStream(strfn, FileMode.CreateNew, FileAccess.Write))
{
fs2.Write(barrImg, 0, barrImg.Length);
FRM_ImageViewer imgvwr = new FRM_ImageViewer(strfn);
imgvwr.Show();
}
It's almost an year since the question was asked but let me share my thoughts since there is not an answer yet
Out of memory exception was thrown because there is not enough memory
available for your application or your application is using too much
memory than available.
I would check the following to find why the application is using too much memory
1) Make sure table returns only required data. Because size of an image is large. If your database returns a large result then, the result set may use several MB's of memory.
Assuming you are trying to view an image saved in data base, if you are displaying n images then query for the specific records.
2) Make sure you are disposing all the disposable objects like Image, Bitmap,Stream, FileStream, Pen, etc., because it may cause memory leak in your application
3) Use a memory profiler to check for memory leaks in the application.
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?
When I try to Serialize some images using the BinaryFormatter, I'll get a ExternalException - A generic error occurred in GDI+." After scratching my head for awhile, I decided to create a simple test project to narrow down the problem:
static void Main(string[] args)
{
string file = #"C:\temp\delme.jpg";
//Image i = new Bitmap(file);
//using(FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
byte[] data = File.ReadAllBytes(file);
using(MemoryStream originalms = new MemoryStream(data))
{
using (Image i = Image.FromStream(originalms))
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
// Throws ExternalException on Windows 7, not Windows XP
bf.Serialize(ms, i);
}
}
}
}
For specific images, I've tried all sorts of ways of loading the image and I could not get it to work under Windows 7, even when running the program as Administrator.
I've copied the exact same executable and image into my Windows XP VMWare instance and I have no problems.
Anyone have any idea of why for some images it doesn't work under Windows 7, but works under XP?
Here's one of the images:
http://www.2shared.com/file/7wAXL88i/SO_testimage.html
delme.jpg md5: 3d7e832db108de35400edc28142a8281
As the OP pointed out, the code provided throws an exception that seems to be occurring only with the image he provided but works fine with other images on my machine.
Option 1
static void Main(string[] args)
{
string file = #"C:\Users\Public\Pictures\delme.jpg";
byte[] data = File.ReadAllBytes(file);
using (MemoryStream originalms = new MemoryStream(data))
{
using (Image i = Image.FromStream(originalms))
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
// Throws ExternalException on Windows 7, not Windows XP
//bf.Serialize(ms, i);
i.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); // Works
i.Save(ms, System.Drawing.Imaging.ImageFormat.Png); // Works
i.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // Fails
}
}
}
}
It could be that the image in question was created with a tool that added some additional information that is interfering with the JPEG serialization.
P.S. The image can be saved to memory stream using BMP or PNG format. If changing the format is an option, then you can try out either of these or any other format defined in ImageFormat.
Option 2
If your goal is just to get the contents of the image file into a memory stream, then doing just the following would help
static void Main(string[] args)
{
string file = #"C:\Users\Public\Pictures\delme.jpg";
using (FileStream fileStream = File.OpenRead(file))
{
MemoryStream memStream = new MemoryStream();
memStream.SetLength(fileStream.Length);
fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);
}
}
Although the Bitmap class is marked as [Serializable], it does not actually support serialisation. The best you can do is serialise the byte[] containing the raw image data and then re-create it using a MemoryStream and the Image.FromStream() method.
I can't explain the inconsistent behaviour you're experiencing; for me, it fails unconditionally (although I first discovered this when trying to marshal images between different app domains, rather than manually serialising them).
I don't know for sure but I would lean towards different security models for XP vs Windows 7. Image inherits from System.MarshalByRefObject. There is probably proxying going on between application domains when serialization is performed. This proxying might be forbidden in Windows 7.
http://msdn.microsoft.com/en-us/library/system.marshalbyrefobject%28v=vs.71%29.aspx
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
i've got some binary data which i want to save as an image. When i try to save the image, it throws an exception if the memory stream used to create the image, was closed before the save. The reason i do this is because i'm dynamically creating images and as such .. i need to use a memory stream.
this is the code:
[TestMethod]
public void TestMethod1()
{
// Grab the binary data.
byte[] data = File.ReadAllBytes("Chick.jpg");
// Read in the data but do not close, before using the stream.
Stream originalBinaryDataStream = new MemoryStream(data);
Bitmap image = new Bitmap(originalBinaryDataStream);
image.Save(#"c:\test.jpg");
originalBinaryDataStream.Dispose();
// Now lets use a nice dispose, etc...
Bitmap2 image2;
using (Stream originalBinaryDataStream2 = new MemoryStream(data))
{
image2 = new Bitmap(originalBinaryDataStream2);
}
image2.Save(#"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}
Does anyone have any suggestions to how i could save an image with the stream closed? I cannot rely on the developers to remember to close the stream after the image is saved. In fact, the developer would have NO IDEA that the image was generated using a memory stream (because it happens in some other code, elsewhere).
I'm really confused :(
As it's a MemoryStream, you really don't need to close the stream - nothing bad will happen if you don't, although obviously it's good practice to dispose anything that's disposable anyway. (See this question for more on this.)
However, you should be disposing the Bitmap - and that will close the stream for you. Basically once you give the Bitmap constructor a stream, it "owns" the stream and you shouldn't close it. As the docs for that constructor say:
You must keep the stream open for the
lifetime of the Bitmap.
I can't find any docs promising to close the stream when you dispose the bitmap, but you should be able to verify that fairly easily.
A generic error occurred in GDI+.
May also result from incorrect save path!
Took me half a day to notice that.
So make sure that you have double checked the path to save the image as well.
Perhaps it is worth mentioning that if the C:\Temp directory does not exist, it will also throw this exception even if your stream is still existent.
Copy the Bitmap. You have to keep the stream open for the lifetime of the bitmap.
When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI
public static Image ToImage(this byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
using (var image = Image.FromStream(stream, false, true))
{
return new Bitmap(image);
}
}
[Test]
public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
{
var imageBytes = File.ReadAllBytes("bitmap.bmp");
var image = imageBytes.ToImage();
image.Save("output.bmp");
}
I had the same problem but actually the cause was that the application didn't have permission to save files on C. When I changed to "D:\.." the picture has been saved.
You can try to create another copy of bitmap:
using (var memoryStream = new MemoryStream())
{
// write to memory stream here
memoryStream.Position = 0;
using (var bitmap = new Bitmap(memoryStream))
{
var bitmap2 = new Bitmap(bitmap);
return bitmap2;
}
}
This error occurred to me when I was trying from Citrix. The image folder was set to C:\ in the server, for which I do not have privilege. Once the image folder was moved to a shared drive, the error was gone.
A generic error occurred in GDI+. It can occur because of image storing paths issues,I got this error because my storing path is too long, I fixed this by first storing the image in a shortest path and move it to the correct location with long path handling techniques.
I was getting this error, because the automated test I was executing, was trying to store snapshots into a folder that didn't exist. After I created the folder, the error resolved
One strange solution which made my code to work.
Open the image in paint and save it as a new file with same format(.jpg). Now try with this new file and it works. It clearly explains you that the file might be corrupted in someway.
This can help only if your code has every other bugs fixed
It has also appeared with me when I was trying to save an image into path
C:\Program Files (x86)\some_directory
and the .exe wasn't executed to run as administrator, I hope this may help someone who has same issue too.
For me the code below crashed with A generic error occurred in GDI+on the line which Saves to a MemoryStream. The code was running on a web server and I resolved it by stopping and starting the Application Pool that was running the site.
Must have been some internal error in GDI+
private static string GetThumbnailImageAsBase64String(string path)
{
if (path == null || !File.Exists(path))
{
var log = ContainerResolver.Container.GetInstance<ILog>();
log.Info($"No file was found at path: {path}");
return null;
}
var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;
using (var image = Image.FromFile(path))
{
using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
{
using (var memoryStream = new MemoryStream())
{
thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here
var bytes = new byte[memoryStream.Length];
memoryStream.Position = 0;
memoryStream.Read(bytes, 0, bytes.Length);
return Convert.ToBase64String(bytes, 0, bytes.Length);
}
}
}
}
I came across this error when I was trying a simple image editing in a WPF app.
Setting an Image element's Source to the bitmap prevents file saving.
Even setting Source=null doesn't seem to release the file.
Now I just never use the image as the Source of Image element, so I can overwrite after editing!
EDIT
After hearing about the CacheOption property(Thanks to #Nyerguds) I found the solution:
So instead of using the Bitmap constructor I must set the Uri after setting CacheOption BitmapCacheOption.OnLoad.(Image1 below is the Wpf Image element)
Instead of
Image1.Source = new BitmapImage(new Uri(filepath));
Use:
var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;
See this: WPF Image Caching
Try this code:
static void Main(string[] args)
{
byte[] data = null;
string fullPath = #"c:\testimage.jpg";
using (MemoryStream ms = new MemoryStream())
using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
using (Bitmap bm = new Bitmap(tmp))
{
bm.SetResolution(96, 96);
using (EncoderParameters eps = new EncoderParameters(1))
{
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
}
data = ms.ToArray();
}
File.WriteAllBytes(fullPath, data);
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
for (int j = 0; j < encoders.Length; ++j)
{
if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
return encoders[j];
}
return null;
}
I used imageprocessor to resize images and one day I got "A generic error occurred in GDI+" exception.
After looked up a while I tried to recycle the application pool and bingo it works. So I note it here, hope it help ;)
Cheers
I was getting this error today on a server when the same code worked fine locally and on our DEV server but not on PRODUCTION. Rebooting the server resolved it.
public static byte[] SetImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
public static Bitmap SetByteToImage(byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream, false);
mStream.Dispose();
return bm;
}