I have a problem with out of memory when I'm trying load a few images into one picturebox.
public void button2_Click(object sender, EventArgs e)
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
dialog.ShowDialog();
string selected = dialog.SelectedPath;
string[] imageFileList = Directory.GetFiles(selected);
int iCtr = 0,zCtr = 0;
foreach(string imageFile in imageFileList)
{
if (Image.FromFile(imageFile) != null)
{
Image.FromFile(imageFile).Dispose();
}
PictureBox eachPictureBox = new PictureBox();
eachPictureBox.Size = new Size(100,100);
// if (iCtr % 8 == 0)
//{
// zCtr++;
// iCtr = 0;
//}
eachPictureBox.Location = new Point(iCtr * 100 + 1, 1);
eachPictureBox.Image = Image.FromFile(imageFile);
iCtr++;
panel1.Controls.Add(eachPictureBox);
}
}`enter code here`
if (Image.FromFile(imageFile) != null)
{
Image.FromFile(imageFile).Dispose();
}
Bad. You're loading the image from the file, checking to see if the result is null...then loading it again into a new result so that you can dispose it. While the latter portion is silly, it isn't harmful. The first portion is, however, as the resulting Image is never properly disposed of (if/when the GC collects it, the finalizer on the Image type should dispose of the unmanaged resources, but this is not a wise thing to rely on).
Incidentally, Image.FromFile will never return null. If it cannot read the image, then it will throw an OutOfMemoryException.
The code also appears to do nothing, since there's no else block and nothing meaningful is done in the if block.
My guess is that your OutOfMemoryException is coming from the fact that one or more of the files in that directory is stored in a corrupted or unsupported image format, or isn't an image at all.
Try replacing the code in your foreach with this:
try
{
Image image = Image.FromFile(imageFile);
PictureBox eachPictureBox = new PictureBox();
eachPictureBox.Size = new Size(100,100);
eachPictureBox.Location = new Point(iCtr * 100 + 1, 1);
eachPictureBox.Image = Image.FromFile(imageFile);
iCtr++;
panel1.Controls.Add(eachPictureBox);
}
catch(OutOfMemoryException) { } // skip the file
The picture box internally holds a reference to the bitmap that you place in it. Unless you get rid of the picture box, it's holding a reference to every bitmap you load into it.
Something you have to consider that regardless of the type of picture stored on disk, when you open it for display the picture will become a bitmap and require 4 bytes per displayed pixel.
Your code seems to suggest an attempt at a thumbnail operation. You are in fact loading 70 files into memory and regardless of the display size, in memory they will be very large.
For example let's say you have 70 jpegs at 32bit color depth and say 1920x1080 pixels in size. Your memory requirement to load that many images all at once then is:
70 pics x 1920 pixels x 1080 pixels x 4 bytes/pixel = 580,608,000 bytes!
And that's a fairly low estimate.
You could consider loading many fewer pictures or trying for a real thumbnailing solution.
Related
I am trying to create an application that generates a bitmap image every frame based on user actions and have it display that image to the screen. I would like the application to also be able to update that image in unity in real time as soon as the user makes another action.
I have created an application that does this and it works. However, it is veryyyy slow. My Update() method is attached below.
My idea was:
Capture user data (mouse location).
Convert that data into a special signal format that another program recognizes.
Have that program return a bitmap image.
Use that bitmap as a texture and update the existing texture with the new image.
Code:
UnityEngine.Texture2D oneTexture;
Bitmap currentBitmap;
private int frameCount = 0;
void Update()
{
// Show mouse position in unity environment
double xValue = Input.mousePosition.x;
double yValue = Screen.height - Input.mousePosition.y;
myPoints = "" + xValue + "," + yValue + Environment.NewLine;
// Show heatmap being recorded.
signals = Program.ConvertStringToSignalsList(myPoints);
currentBitmap = Program.CreateMouseHeatmap(Screen.width, Screen.height, signals);
// Update old heatmap texture.
UpdateTextureFromBitmap();
ri.texture = oneTexture;
ri.rectTransform.sizeDelta = new Vector2(Screen.width, Screen.height);
frameCount++;
// Write points to Database.
StartCoroutine(WriteToDB(xValue, yValue)); // <<<<< Comment out when playback.
}
private void UpdateTextureFromBitmap()
{
// Convert Bitmap object into byte array instead of creating actual
// .bmp image file each frame.
byte[] imageBytes = ImageToBytes(currentBitmap);
BMPLoader loader = new BMPLoader();
BMPImage img = loader.LoadBMP(imageBytes);
// Only initialize the Texture once.
if (frameCount == 0)
{
oneTexture = img.ToTexture2D();
}
else
{
Color32[] imageData = img.imageData;
oneTexture.SetPixels32(imageData);
oneTexture.Apply();
}
}
I was wondering if someone could help me improve the rate at which the image updates to the screen? I know that it is possible to make this program much faster but I am so new to unity and C# that I don't know how to make that happen. Also if there is a completely different way that I should be going about doing this then I am open to that too. Any help would be appreciated. Thanks!
Also, below is a screenshot of the Profiler showing the breakdown of CPU Usage. Currently it looks like every frame is taking about 500ms.
I am making a small application that shows a live webcam feed in a windows form, and also stores watermarked images to drive at a specified interval (Creating a timelapse video is the end goal).
I am using the AForge library for image and video processing.
I have problems were there seems to be a memory leak, even though i try to make sure to use "using" statements at every location where image processing occurs.
Below is the code were the image processing takes place (The NewFrame event)
private void Video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (ImageProcessing) // If the previous frame is not done processing, let this one go
return;
else
ImageProcessing = true;
using (Bitmap frame = (Bitmap)eventArgs.Frame)
{
// Update the GUI picturebox to show live webcam feed
Invoke((Action)(() =>
{
webcam_PictureBox.Image = (Bitmap)frame.Clone();
}));
// During tests, store images to drive at a certain interval
if (ImageStoreTimer.Elapsed.TotalSeconds > ImageStoreTime)
{
DateTime dt = DateTime.Now;
using (Graphics graphics = Graphics.FromImage(frame))
{
PointF firstLocation = new PointF(frame.Width / 2, frame.Height / 72);
PointF secondLocation = new PointF(frame.Width / 2, frame.Height / 15);
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
using (Font arialFont = new Font("Arial", 15))
{
graphics.DrawString(dt.ToString(), arialFont, Brushes.Red, firstLocation, drawFormat);
graphics.DrawString(Pressure.ToString("F0") + " mbar", arialFont, Brushes.Red, secondLocation, drawFormat);
}
}
// Place images in a folder with the same name as the test
string filePath = Application.StartupPath + "\\" + TestName + "\\";
// Name images by number 1....N
string fileName = (Directory.GetFiles(filePath).Length + 1).ToString() + ".jpeg";
frame.Save(filePath + fileName, ImageFormat.Jpeg);
ImageStoreTimer.Restart();
}
}
//GC.Collect(); <----- I dont want this
}
catch
{
if (ProgramClosing == true){}
// Empty catch for exceptions caused by the program being closed incorrectly
else
throw;
}
finally
{
ImageProcessing = false;
}
}
Now, when running the program, i see memory usage going up and down, usually it gets to about 900MB before dropping. But occasionally it will rise to 2GB. Occasionally, i even get an out of memory exception at this line:
Graphics graphics = Graphics.FromImage(frame)
So after spending an hour or so to trying to reshape the code and looking for my memory leak, i at last tried the GC.Collect line that is commented out in the code (Shame). After that, my memory usage stays constant, at less than 60MB. And i can run the program for 24 hours without any problems.
So i read a bit about GC.Collect, and how bad it is, for example that it could take a lot of processing power to do it to often in a program. But when i compare the CPU power used by my program, it does not really change regardless if i comment the line out or leave it. But the memory problem is gone if i collect at the end of the new frame event.
I would like to find a solution to my problem that does not involve the GC.collect function, as i know it is bad programming practice and i should instead find the underlying problem source.
Thank you all in advance!
I'm not good with win forms but I think that this line:
webcam_PictureBox.Image = (Bitmap)frame.Clone();
Will leave previous image undisposed, which leaks memory (unmanaged memory hold by Bitmap). Since Bitmap has finalizer - it will be reclaimed by GC at some future time (or when you call GC.Collect), but as you already understand - it's not a good practice to rely on GC in such case. So try to do it like this instead:
if (webcam_PictureBox.Image != null)
webcam_PictureBox.Image.Dispose();
webcam_PictureBox.Image = (Bitmap)frame.Clone();
Reasonable comment by Larse: it might be better to not dispose image while it's still being assigned to PictureBox.Image, because who knows, maybe PictureBox control does anything with old image when you are assigning a new one. So alternative is then:
var oldImage = webcam_PictureBox.Image;
webcam_PictureBox.Image = (Bitmap)frame.Clone();
if (oldImage != null)
oldImage.Dispose();
This worked well for us, https://stackoverflow.com/a/70914235/10876657
before that, we tried using statements and all sorts but one solution might work on one machine, but not on the other, but by referencing the image currently set in picturebox and disposing of it afterwords has worked well, not entirely sure why picture.image is not disposed of automatically when a new image is set (frustrating!) but hey, at least this simple workaround exists
When I want to pick photo from my library, and initially do something with this photo I want to crop it and resize to 300x300. Everything is OK, until I choose a big photo.
When I pick a big image (10000x6000) PhotoChooserTask does nothing (from the user's point of view), PhotoChooserTask just crashes (not the application). Then when I try picking another one I get "Not allowed to call Show() multiple times before an invocation returns" exception.
It seems PhotoChooserTask still has the previous object inside, and I don't know how to dispose or clear PhotoChooserTask.
PS. without setting
chooser.PixelHeight = 300;
chooser.PixelWidth = 300;
Photo will set, and everything is ok.
PS2.
Samsung ATIV S does not have a problem. Only nokia 1320 ,520 and 530
PhotoChooserTask chooser = new PhotoChooserTask();
try
{
chooser.ShowCamera = true;
chooser.PixelHeight = 300;
chooser.PixelWidth = 300;
chooser.Completed += (s, result) =>
{
if (result.Error != null){ return; }
if (result.ChosenPhoto != null)
{
var bitmap = new BitmapImage();
bitmap.SetSource(result.ChosenPhoto);
Service.uploadPhoto(receiver, (ImageSource)bitmap);
}
};
chooser.Show();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
When the photo is big and height is set the debugger doesn't enters inside chooser.Completed
The image size is probably the problem.
An image of 10000x6000 will take approximately 240MB in memory itself (10000 * 6000 * 4 bytes per pixel). That ammount of memory is probably causing the app using the PhotoChooserTask to crash and not to return anything to your app.
A possible solution is to first save the chosen photo into a jpeg of lower quality or in the same resolution but compressing it using High Compression methods like SavePng() method in Cimbalino Phone Toolkit and then crop the Image.
I use it to convert 240mb Image to 8mb then Apply the Effects on it.
I wrote a WPF app that should swap (fast) between a large set of images (600+, 190Kb average size), but I'm finding some difficulties.
private int appendImages(Canvas c, int start, int end)
{
int tot = 0;
for (int i = start; i < end; i++)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
//bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(appFolder+#"/"+imgFolder+"/"+filename(i)+".jpg");
bi.EndInit();
Image img = new Image
{
Width = imgWidth,
Height = imgHeight,
Source = bi,
Name = name(i),
Visibility = i == startImg ? Visibility.Visible : Visibility.Hidden
};
c.Children.Add(img);
tot++;
}
}
Apparently the inizialization is fine, but if I try to swap the images like this:
private void changeImageTo(int n)
{
Image img = findImage(n);
Image old = findImage(prevImg);
if (img != null)
{
img.Visibility = Visibility.Visible;
if (old != null && old != img)
old.Visibility = Visibility.Hidden;
prevImg = n;
}
}
..then the app shows the first 200/300 images (depending on the sources I use), and the others are just empty/blank (i can see the canvas underneath).
I suspect it's a memory issue, but I'm not really sure what causes it.
By the way, if I uncomment the commented line (BitmapCacheOption.OnLoad) sometimes I get a vshost error when launching the app.
Any help would be MUCH appreciated, since I couldn't find anything useful browsing around.
Thanks in advance!
It looks like you're loading all the images at once, and putting them into WinForms/WPF controls. That is a very bad idea with that many images, as each one takes resources even if it's not shown.
Rough back of the envelope calculation, assuming 640x480 images, 24bpp being the native GDI+ format, shows a bit over 2gb for loading all the images at once, and that would, of course, increase exponentially with image size.
What I would do instead, is have only one Image. Move the actual image loading code into your changeImageTo function, build the file name based on n, and set the loaded image to the Image there.
private void LoadImageList()
{
string filepath = Application.StartupPath + #"\Content\Textures\Tiles\PlatformTiles.png";
Bitmap tileSheet = new Bitmap(filepath);
int tilecount = 0;
for (int y = 0; y < tileSheet.Height / TileGrid.TileHeight; y++)
{
for (int x = 0; x < tileSheet.Width / TileGrid.TileWidth; x++)
{
Bitmap newBitmap = tileSheet.Clone(
new System.Drawing.Rectangle(
x * TileGrid.TileWidth,
y * TileGrid.TileHeight,
TileGrid.TileWidth,
TileGrid.TileHeight),
System.Drawing.Imaging.PixelFormat.DontCare);
imgListTiles.Images.Add(newBitmap);
string itemName = "";
if (tilecount == 0)
{
itemName = "Empty";
}
if (tilecount == 1)
{
itemName = "White";
}
listTiles.Items.Add(new ListViewItem(itemName, tilecount++));
}
}
}
All I did was update PlatformTiles.png with a newer one and the next time I ran the program it doesn't load. I placed a breakpoint at int tilecount = 0; and it doesn't ever reach it. Every thing after it doesn't load either. Any ideas?
Is that Bitmap referring to System.Drawing.Bitmap? If so your code has a memory leak. You're creating hundreds (thousands?) of Bitmap objects without disposing them. Each Bitmap object encapsulates a GDI Bitmap surface which must be explicitly disposed. Use a C# using(Bitmap bmp) { } block to ensure they're disposed when they fall out of scope, or ensure that you dispose of the bitmaps when the parent class object is disposed.
This may be related to your problem (generally it's a bad idea to create too many bitmaps in case you hit the GDI Object limit).
I got rid of the string and replaced filepatth, Bitmap tileSheet = new Bitmap(filepath);, with the path of the image on my hard-drive and it worked. Solution, but could use some explaining.
If the file is part of your content project and it's Build Action is set to Compile, the file that's in that folder at run time does not have a .png extension so the Bitmap will never be created. Content assets have a .xnb extension as they've been compiled to a binary format. Change the Build Action to None and the Copy to Output Direction to Copy if newer