?
Solved

Image.GetThumbnailImage throws System.OutOfMemoryException in some cases.

Posted on 2008-06-20
3
Medium Priority
?
4,095 Views
Last Modified: 2013-11-26
Hi,

Here's some background info: I've got an app which interfaces with a scanner, saves the scanned pages as images in a user-specified format, does some processing on those images and "uploads" them to a remote database via a web-service.

If the processing of any of the images fails, the user is shown a dialog containing thumbnails of all the images, with the erroneous ones highlighted. The user can then choose to "skip" those images, so that they are not uploaded.

The problem occurs sometimes when the thumbnails are generated (inside a UserControl - code included below). If the files are TIFF files, the Image.GetThumbnailImage call (inside the Thumbnail helper class - code also included) throws an OutOfMemoryException, even when there are only a handful (2-5) images. If I change the format of the scanned files to PNG or JPEG, it generates the thumbnails properly.

I wrote a little test app which only uses the Thumbnail class to generate thumbnails. When I run the test app and the main app against the same set of images, the test app generated the thumbnails successfully (on my dev machine) but the main app fell over (on the production machine). Both machines have 2GB of memory but from what I've experienced and found on the web about GDI+, it seems it likes to throw the OutOfMemoryException for a whole range of reasons, not necessarily related to lack of memory.

Any ideas as to the cause and solution would be much appreciated.
//USER CONTROL CODE
    public delegate void NoArgDelegate();
    public delegate void ImageArgDelegate(Image image);
 
    public partial class ScannedImageTile : UserControl
    {
        private ScannedFile _scannedFile;
        private Image _image;
        private static Size _thumbSize = new Size(72, 102);
        
        public ScannedImageTile()
        {
            InitializeComponent();
        }
 
        public ScannedFile ScannedFile
        {
            get { return _scannedFile; }
            set { 
                _scannedFile = value;
                if (_scannedFile == null)
                {
                    if (_image != null) _image.Dispose();
                    _image = null;
                    _pictureBox = null;
                }
                else
                {
                    _errorProvider.SetError(_pictureBox, _scannedFile.ErrorMessages);
                }
            }
        }
 
        private void GetThumbnail()
        {
            using (MemoryStream stream = new MemoryStream(_scannedFile.GetImageData()))
            {
                _image = Image.FromStream(stream);
            }
            Image thumb = Thumbnail.GetThumbnailImage(ref _thumbSize, _image);
            UpdatePictureSafely(thumb);
        }
 
        private void UpdatePictureSafely(Image thumb)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new ImageArgDelegate(UpdatePictureSafely), thumb);
            }
            else
            {
                _pictureBox.Image = thumb;
            }
        }
 
        private void ScannedImageTile_Paint(object sender, PaintEventArgs e)
        {
            if (_scannedFile != null && _pictureBox.Image == null)
                BeginInvoke(new NoArgDelegate(GetThumbnail));  
        }
    }
 
//THUMBNAIL HELPER CLASS CODE
    public class Thumbnail:ObjectModelBase
    {
        ...
        public static System.Drawing.Image GetThumbnailImage(ref Size thumbSize, System.Drawing.Image image)
        {
            System.Drawing.Image.GetThumbnailImageAbort myCallback =
                                  new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
 
            System.Drawing.Image thumb = image.GetThumbnailImage(thumbSize.Width, thumbSize.Height, myCallback, IntPtr.Zero);
            return thumb;
        }
 
        private static bool ThumbnailCallback()
        {
            return false;
        }
    }

Open in new window

0
Comment
Question by:Velio
3 Comments
 
LVL 15

Assisted Solution

by:ozymandias
ozymandias earned 2000 total points
ID: 21837230
This can occur if you try to get a thumnauil from an image with an indexed pixel fornat.
I think it's probably a bug.
I have heard that you can often get round it my catching the out of memory exception and tryiung the operation again inside the catch block but \I am not convinced. Might be worth a try though.
0
 
LVL 13

Accepted Solution

by:
Velio earned 0 total points
ID: 21844291
Thanks for the reply ozymandias,

I also ran into the catching and retrying suggestion but it didn't work in my case.

I've gotten around the problem by creating the thumbnail myself, using a Graphics's object and its DrawImage method.

For future reference of anyone who might come across here, looking for a solution, I've replaced line 40:  Image thumb = Thumbnail.GetThumbnailImage(ref _thumbSize, _image);
with the following:

Image thumb = new Bitmap(_thumbSize.Width, _thumbSize.Height);
using (Graphics graphics = Graphics.FromImage(thumb))
{
    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
    graphics.SmoothingMode = SmoothingMode.HighQuality;
    graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
    graphics.CompositingQuality = CompositingQuality.HighQuality;
    graphics.DrawImage(_image, 0, 0, _thumbSize.Width, _thumbSize.Height);
}

Open in new window

0
 

Expert Comment

by:sabritahiri
ID: 22420425
Hi,
It seems like this is a closed question but the solution given is avoiding the use of System.Drawing.GetThumbnailImage, which I don't think should be the solution. I searched in other forums and the problem seemed not solvable. My finding is that when handling the Width and Height we need to take more care of conversion and division. I was trying to get a scalable thumbnail, so I created a small function which takes width and height, does proper double conversion and calculations and returns height in proper format (i had width set to 100px).

Here is what I think is a better solution:


Int32 ImgW = dbImage.Width;
            Int32 ImgH = dbImage.Height;
            Int32 imgHeight = ScaleImage(ImgW, ImgH);
            /// Create a new image - a thumbnail by changing the height and width
            System.Drawing.Image thumbnailImage = dbImage.GetThumbnailImage(100, imgHeight, null, new System.IntPtr());
 
and then the function ScaleImage():
 
protected Int32 ScaleImage(Int32 imgWidth, Int32 imgHeight)
    {
 
        double imgW = (double)imgWidth;
        double imgH = (double)imgHeight;
 
        imgH = imgH * (100 / imgW);
        imgHeight = Convert.ToInt32(imgH);
 
        return imgHeight;
 
    }

Open in new window

0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Entity Framework is a powerful tool to help you interact with the DataBase but still doesn't help much when we have a Stored Procedure that returns more than one resultset. The solution takes some of out-of-the-box thinking; read on!
This article describes and provides a custom-made tool I wrote to give businesses a means of identifying commercial music content, without having to expend too much effort. Business recordings are easily identified from possibly illegal music files …
The video will let you know the exact process to import OST/PST files to the cloud based Office 365 mailboxes. Using Kernel Import PST to Office 365 tool, one can quickly import numerous OST/PST files to Office 365. Besides this, the tool also comes…
Watch the software video of Kernel Import PST to Office 365 tools which can easily import PST and OST files to Office 365 for bulk mailboxes. The process of migration is simple and user can map source and destination mailboxes and easily import data…

589 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question