Solved

Image.GetThumbnailImage throws System.OutOfMemoryException in some cases.

Posted on 2008-06-20
3
3,923 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 500 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

Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Hey Audio! 3 29
Programmatically storing deadlock graphs with SQL Server 6 41
VB.NET - Error - Object not set in Module 4 33
DataTables + iCheck + pagination Issue 2 23
This document covers how to connect to SQL Server and browse its contents.  It is meant for those new to Visual Studio and/or working with Microsoft SQL Server.  It is not a guide to building SQL Server database connections in your code.  This is mo…
Introduction Hi all and welcome to my first article on Experts Exchange. A while ago, someone asked me if i could do some tutorials on object oriented programming. I decided to do them on C#. Now you may ask me, why's that? Well, one of the re…
This video shows how to quickly and easily add an email signature for all users on Exchange 2016. The resulting signature is applied on a server level by Exchange Online. The email signature template has been downloaded from: www.mail-signatures…
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…

778 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