Solved

Image.GetThumbnailImage throws System.OutOfMemoryException in some cases.

Posted on 2008-06-20
3
3,952 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
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

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

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.

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

Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
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…
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an antispam), the admini…

730 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