Right now I have an application that loads a bunch of thumbnail images into PictureBox controls. Sometimes over a hundred at a time. The images are created using Image.FromStream(MemoryStream), and the memory stream represents each JPG file.
Currently, as soon as I am calling Image.FromStream, I assign the reference to the picture box, then immediately after I Flush() and Dispose() the stream.
According to documentation, I should not release the stream until I'm done with the image. However, I have not had any errors doing this. My PictureBox controls can repaint themselves all day long with the thumbnail images even after the memory streams have long been disposed.
However, if I try to access these images in any way, such as calling the Save function, it throws an error.
If a Bitmap has a dependency on a memory stream or file stream, will it dispose of the source when you dispose of it? Or is this only when it creates the stream itself using Bitmap.FromFile?
Is disposing of the MemoryStream immediately like I am doing considered OK? The PictureBox must be caching the image in some way. I've never had an error because I don't need to touch the image once it has been set.
I also notice that it takes my PictureBoxes a long time to paint on their parent control. I am not sure if this is because I have too many controls, or because there is a delay the first time an image is painted in a PictureBox.
I know that when you create a new Bitmap with a file parameter, it doesn't actually load the file contents until it is first needed. My issue is, I don't want each OnPaint operation to be delayed by a file read, I want to make sure the data is preloaded before I try to paint the controls, otherwise I get flickering and slow painting.
Is there a good way to force an image to be preloaded? What are the discrepancies between Image.FromFile, Image.FromStream, new Bitmap(fileName), etc? Do any of these load all the bytes right away, or is it delayed until they are first needed?
Here's my code:
MemoryStream ms = new MemoryStream(bytes); // byte[] array
pictureBox.Image = Image.FromStream(ms);
ms.Flush();
ms.Dispose();
GDI+ which underlies the Image classes require that the source stream remain open while the Image exists to work properly. It's also a bad idea to write changes to the source stream while Image is using it. Changes should be saved to a different stream.
Everything derived from Image uses a Stream under the covers. Their loading behaviors are the same.
Image does not Close or Dispose a stream that was provided to it via FromStream. You have to manage that yourself.
Also, calling Flush on a Stream only has an effect if the Stream has been written to.
If you're looking to improve UI behavior for your PictureBoxes, try this:
Set InitialImage to a small spinner GIF.
Set WaitOnLoad to false.
Specify the image by setting ImageLocation. (You can use a filespec or a URL in it.)
Load the images asynchronously using the LoadAsync method.
1)
If a Bitmap has a dependency on a memory stream or file stream, will
it dispose of the source when you dispose of it? Or is this only when
it creates the stream itself using Bitmap.FromFile?
Take a look at Image.FromStream Method (Stream, Boolean):
You must keep the stream open for the lifetime of the Image.
2)
The PictureBox must be caching the image in some way. I've never had
an error because I don't need to touch the image once it has been set.
You just should not dispose the Image instance since it has been assigned to the PictureBox (i.e. picture box is using it) and while PictureBox uses it. Otherwise, PictureBox will try using disposed image and it should lead to some "not good" consequences.
PictureBox.Image Property:
Note If you want to use the same image in multiple PictureBox
controls, create a clone of the image for each PictureBox. Accessing
the same image from multiple controls causes an exception to occur.
Update:
Sample with practically the same scenario: HOW TO: Copy a Picture from a Database Directly to a PictureBox Control with Visual C#.
Related
I have a very fast loop which renders animation in a Bitmap buffer and adds filter to it (by using LockBits/UnlockBits to access to the raw data and Marshaling changes to it.) in an independent thread.
I wanted to figure out a way to display the render on the Form, real-time, so I created a PictureBox and linked its Image to the bitmap I created. Everytime immediately after the bitmap is unlocked, I refreshed the PictureBox (using delegate, to do cross-threading) so that the rendering is updated properly.
It's totally fine and works very fast, but one big problem came out as I tried dragging the form to the border of the screen, to see if any bug would appear, and oops, the app collapse..saying 'the bitmap is being locked' This happens when either there's other window moving above the PictureBox or the PictureBox is dragged partially out of the screen. I suspice it because PictureBox can refresh itself when redraw is neccessary, and it does when the bitmap is still being locked. So...any way to sovle this problem? Or anyother ways to render the my animation better?
One of possible solutions could be is create your custom MyPictureBox : PictureBox (say) class which override OnPaintBackground, like this:
protected override OnPaintBackground(...)
{
// nothing, an empty method
}
But I'm not very sure that this will work, you should to check this by yourself.
What I would do, personally, considering your comment:
I have a very fast loop which renders animation in a Bitmap buffer and
adds filter to it (by using LockBits/UnlockBits to access to the raw
data and Marshaling changes to it.) in an independent thread
just forget about PictureBox, cause I found it, personally, too generic and non suitable for high performance rendering. Just write a simple class that handles the drawing of specified bitmap on specified surface.
Imo, this is a best choice.
You can't do that.
Instead, you should copy the image (on the background thread) and put the copy in the PictureBox.
For better performance, you can swap between two images to avoid creating too many images.
I am trying to let a user capture an image and add it onto the screen using an Image. However, I also need to resize this image down to about half size due to memory restrictions (12x 5MP images is never good on a phone...)
I am launching the camera task fine and it calls the Completed event. However, when I try and use DecodeJpeg I get a "The parameter is incorrect." exception.
Here is my code for resizing, where mx and my are int for dimensions. I have verified that there is something in the e.ChosenPhoto with a length of about ~5500:
WriteableBitmap bitmap = PictureDecoder.DecodeJpeg(e.ChosenPhoto, mx, my);
Image img = new Image();
img.Source = bitmap;
The first line is where the app crashes. Any ideas?
EDIT:
This also occurs with the result from the PhotoChooserTask....
Try using the System.Windows.Media.Imaging - Extensions.LoadJpeg method instead of PictureDecoder.DecodeJpeg. Also make sure that the stream is positioned at the beginning of the stream. If you have already used the stream you will need to reset it using:
MyImageStream.Seek(0, System.IO.SeekOrigin.Begin)
I had a lot of problems trying to get access to the original image, especially since BitmapImage automatically resizes images over 2000x2000. If you want an image larger than 2000x2000 you have to have access to the original stream and load it into a WriteableBitmap object
If you want to see some more complex image handling code including detecting resolution from image stream using ExifLib and rotating stream using the WriteableBitmap Extensions check out the BarcodeCaptureResult class for the Silverlight ZXing Library.
UPDATE: Since all you want is to resize an image given the e.ChosenPhoto result I pulled the code from The Silverlight ZXing library. This should work:
WriteableBitmap wbBarcodeImage = new WriteableBitmap(mx, my);
Extensions.LoadJpeg(wbBarcodeImage, e.ChosenPhoto);//Load JPEG from stream into our re-sized writeable bitmap
Note that you will need to use the correct height/width ratio, otherwise you will have a black bar at the bottom or side of the image. You can use ExifLib to detect the original image size and use that to scale (see GetWriteableBitmap method in BarcodeCaptureResult linked above)
I have a list of high-resolution images obtained from a web server. I need to populate them in the surface SDK scatterview item. To show the images I am using Image control for each image.
Code Logic:
User has identity tags which being placed on the surface table will fetch a list of high-resolution images associated with that tag. The fetching of the images is run in background to avoid jamming the UI. The code to obtain JPEG images in the background is
public BitmapSource FetchImage(string URLlink)
{
JpegBitmapDecoder decoder = null;
BitmapSource bitmapSource = null;
try
{
decoder = new JpegBitmapDecoder(new Uri(URLlink, UriKind.Absolute), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
}
catch (Exception)
{
decoder = new JpegBitmapDecoder(new Uri("pack://application:,,,/Resources/ImageNotFound.jpg", UriKind.RelativeOrAbsolute), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnDemand);
}
finally
{
bitmapSource = decoder.Frames[0];
bitmapSource.Freeze();
}
return bitmapSource;
}
The images are downloaded from the server and displayed in the image control. However, there is severe performance hit and application just hangs when more than 10 images are loaded. For low-resolution images, i can load even 20-30 images without hanging or slowing down the application.
Since i read the default algorithm for image control is Fant, I tried chaning the rendering properties to HighQuality. Application still hangs, but any lower than this kills the whole idea of having a high-resolution image for display.
RenderOptions.SetBitmapScalingMode(mic.ItemImage, BitmapScalingMode.HighQuality);
Is there a better way of loading images. I was thinking of first saving images to hard-disk and then loading to image source. Would that be effective in improving performance because I was thinking when I am loading images directly from URL it would store it in memory which eventually runs out. By saving images first I might avoid this, but there might a chance .NET actually is doing the same thing of storing first in temp file and then loading.
I also tried changing the BitmapCacheOption to all the available options but it didn't improve anything.
The scatterView can not gother a lot of images.
for exemple try to populate it with 1000 rectangle with one single color.
It will slow the application .
the problem can not be resolved ,Microsoft have to recode the ScatterView.
However you can desactivate some effects on the scatterView.
I'm working on a silverlight project where users get to create their own Collages.
The problem
When loading a bunch of images by using the BitmapImage class, Silverlight hogs up huge unreasonable amounts of RAM. 150 pictures where single ones fill up at most 4,5mb takes up about 1,6GB of RAM--thus ending up throwing memory exceptions.
I'm loading them through streams, since the user selects their own photos.
What I'm looking for
A class, method or some process to eliminate the huge amount of RAM being sucked up. Speed is an issue, so I don't want to be converting between images formats or anything like that. A fast resizing solution might work.
I've tried using a WriteableBitmap to render the images into, but I find this method forces me to reinvent the wheel when it comes to drag/drop and other things I want users to be able to do with the images.
What I would try is to take load each stream and resize it to a thumbnail (say, 640x480) before loading the next one. Then let the user work with the smaller images. Once you're ready to generate the PDF, reload the JPEGs from the original streams one at a time, disposing of each bitmap before loading the next.
I'm guessing you're doing something liek this:
Bitmap bitmap = new Bitmap (filename of jpeg);
and then doing:
OnPaint (...)
{
Graphics g = ....;
g.DrawImage (bitmap, ...);
}
This will be resizing the huge JPEG image to the size shown on screen every time you draw it. I'm guessing your JPEG is about 2500x2000 pixels in size. When you load a JPEG into a Bitmap, the bitmap loading code uncompresses the data and stores it either as RGB data in a format that will be easy to render (i.e. in the same pixel format as the display) or as a thing known as a Device Independant Bitmap (aka DIBitmap). These bitmaps require more RAM to store than a compressed JPEG.
Your current implementation is already doing format conversion and resizing, but doing it in an innefficent way, i.e. resizing a huge image down to screen size every time it's rendered.
Ideally, you want to load the image and scale it down. .Net has a system to do this:
Bitmap bitmap = new Bitmap (filename of JPEG);
Bitmap thumbnail = bitmap.GetThumbnailImage (width, height, ....);
bitmap.Dispose (); // this releases all the unmanged resources and makes the bitmap unusable - you may have been missing this step
bitmap = null; // let the GC know the object is no longer needed
where width and height are the size of the required thumbnail. Now, this might produce images that don't look as good as you might want them to (but it will use any embedded thumbnail data if it's present so it'll be faster), in which case, do a bitmap->bitmap resize.
When you create the PDF file, you'll need to reload the JPEG data, but from a user's point of view, that's OK. I'm sure the user won't mind waiting a short while to export the data to a PDF so long as you have some feedback to let the user know it's being worked on. You can also do this in a background thread and let the user work on another collage.
What might be happening to you is a little known fact about the garbage collection that got me as well. If an object is big enough ( I don't remember where the line is exactly ) Garbage Collection will decide that even though nothing currently in scope is linked to the object (in both yours and mine the objects are the images) it keeps the image in memory because it has decided that in case you ever want that image again it is cheaper to keep it around rather than delete it and reload it later.
This isn't a complete solution, but if you're going to be converting between bitmaps and JPEG's (and vice versa), you'll need to look into the FJCore image library. It's reasonably simple to use, and allows you to do things like resize JPEG images or move them to a different quality. If you're using Silverlight for client-side image processing, this library probably won't be sufficient, but it's certainly necessary.
You should also look into how you're presenting the images to the user. If you're doing collages with Silverlight, presumably you won't be able to use virtualizing controls, since the users will be manipulating all 150 images at once. But as other folks have said, you should also make sure you're not presenting bitmaps based on full-sized JPEG files either. A 1MB compressed JPEG is probably going to expand to a 10MB Bitmap, which is likely where a lot of your trouble is coming from. Make sure that you're basing the images you present to the user on much smaller (lower quality and resized) JPEG files.
The solution that finally worked for me was using WriteableBitmapEX to do the following:
Of course I only use thumbnails if the image isn't already small enough to store in memory.
The gotch'a I had was the fact that WriteableBitmap doesn't have a parameterless constructor, but initializing it with 0,0 as size and then loading the source sets these automatically. That didn't come naturally to me.
Thanks for the help everybody!
private WriteableBitmap getThumbnailFromBitmapStream(Stream bitmapStream, PhotoFrame photoFrame)
{
WriteableBitmap inputBitmap = new WriteableBitmap(0,0);
inputBitmap.SetSource(bitmapStream);
Size thumbnailSize = getThumbnailSizeFromWriteableBitmap(inputBitmap, photoFrame.size);
WriteableBitmap thumbnail = new WriteableBitmap(0,0);
thumbnail = inputBitmap.Resize((int)thumbnailSize.Width, (int)thumbnailSize.Height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
return thumbnail;
}
One additional variant to reduce ram using:
Dont load images, which ar invisible at this moment, and load them while user scrolling the page. This method uses by web developers to improve page load speed. For you its the way not to store hole amount of images in ram.
And I think the better way not to make thumbnails on run, but store them near the fullsize pictures and get only links for them. When it needed, you alway can get the link to fullsize picture and load it.
I have a user-uploaded image pulled from the database that I am resizing smaller to display on a web page that I intend to print. I thought about saving a smaller version when the user uploads it, but since the design of this document hasn't been finalized yet, I was looking for something more dynamic. Also, this document only needs to be printed up once, while the image uploaded is displayed at various places in the app numerous times.
Using javascript to resize it while keeping its proportions, it was printing fine for a while. After adding a margin for styling, the printer started printing the image at its full size. I'm assuming it's the margin. It looks fine on screen but pushes everything off the page on paper.
This led me to look into resizing it on the server, in the C# code, but we use user images uploaded to the database, and I can't seem to find the right time or place in the page life cycle to access and change the width and height. I've tried the various methods on the web using Bitmaps, but they all want a file, when I am using a FileDownloader page as the image url.
Perhaps I'm looking in the wrong place entirely and need to go back to the client. Advice and help is appreciated.
As long as your FileDownloader page returns the proper resized image, it shouldn't matter that you're not point to an actual image.
I'd something like this in your FileDownloader page (pseudo code):
using (Image img = LoadImageFromDatabase())
{
using (Image resized = ResizeImage(img))
{
Response.Clear();
// Set proper headers
using (MemoryStream ms = new MemoryStream())
{
resized.Save(ms); // maybe the function isn't called Save, can't remember a 100%
ms.Seek(0); // Can't remember if this is necessary
Response.BinaryWrite(ms);
}
}
}
Like I mentioned it's highly pseudo code, but it should be straight forward with a Visual Studio open, I just haven't access to it right now, and it's been quite a while since I last used this (since I'm stored the resized images like most other in this question recommends - I do so too, however I realize this is not an option for you)
When you are going to have to resize an image to known constraints, and there's the possibility of having to do that multiple times, I'd always advocate doing the resize once (on upload) and storing the result. Of course, you don't say that you need to retain the original image size, but if you do, then you just have to store the image twice - once original size and once at the resized dimensions.
Once you've done that, you can worry about defining your print layout based on the known dimensions of the resized image, and not have to faff about resizing for each use.
I would suggest converting on upload and possibly saving both images in case you want to let the user click through to the full image. Using this model you only do the conversion once and can render either size image. The GetThumbnailImage() method on the Image class will do what you desire, something like this:
String imageFile = uploadedFileName;
Image baseImage = Image.FromFile(imageFile);
Image thumbImage = baseImage.GetThumbnailImage(300,300,..., ...);
SaveMyImage(baseImage);
SaveMyImage(thumbImage);
Be sure to check the documentation for the parameters to GetThumbnailImage() to verify scaling issues and callback handling.
Could you implement such a process:
User sends an image
Image is opened by a function/routine/script, while the user waits
Image is resized on the fly and saved in the correct location which returns a code for success
User receives a message depending of the return value of the script.
EDIT:
I agree with most of the replies you got here.
If the pictures are stored in a database you need to first make thumbmails for all pictures and put them in the same database, then you need to implement a process to create the thumbmails on the fly when adding new pictures.
Disclaimer: I'm suggesting the open-source library I designed for this purpose. I'm definitely biased, but 4 years and thousands of happy users justify my bias :)
You shouldn't be resizing images inside an .ASPX page, or serving them either. Images should be handled in separate requests by a HttpModule so responsiveness doesn't suffer.
If you have some kind of ID in SQL for each image, you can use the SqlReader VirtualPathProvider to make it accessible via a URL, like /sqlimages/id
Combine that with this free, open-source dynamic image resizing module, and you're set.
You'll simply reference images like this: http://localhost/sqlimages/id?width=300&height=200 from your HTML, and you may not even have to write a line of C#.
If you write your own solution, read these 28 pitfalls you should avoid, so you don't end up crashing the server.
Hope this helps!