Solved

Image.GetThumbnailImage throws System.OutOfMemoryException in some cases.

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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

706 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

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now