Results 1 to 5 of 5

Thread: Extract thumbnail image from a file without reading the whole file

  1. #1

    Thread Starter
    l33t! MrPolite's Avatar
    Join Date
    Sep 2001
    Posts
    4,428

    Extract thumbnail image from a file without reading the whole file

    You could use Image.GetThumbnailImage() to get a thumbnail of an already loaded image, but if you have a lot of images and you do that, it'd be fairly slow because you would have to load all the images.
    Alternatively most images taken with cameras have a thumbnail of the image stored in the image metadata, and that can be accessed without loading the image as a whole.
    If you have .NET 1.1 installed then you can do this:

    .NET 1.1 version only
    Code:
    // Please do not remove :)
    // Written by Kourosh Derakshan
    // 
    using System.IO;
    using System.Drawing.Imaging;
    
    private const int THUMBNAIL_DATA = 0x501B;
    
    
    /// <summary>
    /// Gets the thumbnail from the image metadata. Returns null of no thumbnail
    /// is stored in the image metadata
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    Image GetThumbnail (string path)
    {
    	FileStream fs = File.OpenRead (path);
    	// Last parameter tells GDI+ not the load the actual image data
    	Image img = Image.FromStream (fs, false, false);
    	
    	
    	// GDI+ throws an error if we try to read a property when the image
    	// doesn't have that property. Check to make sure the thumbnail property
    	// item exists.
    	bool propertyFound = false;
    	for (int i=0; i<img.PropertyIdList.Length; i++)
    		if (img.PropertyIdList[i] == THUMBNAIL_DATA)
    		{
    			propertyFound = true;
    			break;
    		}
    	
    	if (!propertyFound)
    		return null;
    
    	PropertyItem p = img.GetPropertyItem (THUMBNAIL_DATA);
    	fs.Close();
    	img.Dispose();
    
    	
    	// The image data is in the form of a byte array. Write all 
    	// the bytes to a stream and create a new image from that stream
    	byte[] imageBytes = p.Value;
    	MemoryStream stream = new MemoryStream (imageBytes.Length);
    	stream.Write (imageBytes, 0, imageBytes.Length);
    
    	return Image.FromStream(stream);
    }



    If you want support for .NET 1.0 also, you can try this code below (or download the source file, right click and save target ExifThumbReader.cs):
    Code:
    // Please do not remove :)
    // Written by Kourosh Derakshan
    // 
    using System;
    using System.IO;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;
    
    /// <summary>
    /// Allows reading of embedded thumbnail image from the EXIF information in an image.
    /// </summary>
    public abstract class ExifThumbReader
    {
    	// GDI plus functions
    	[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
    	internal static extern int GdipGetPropertyItem(IntPtr image, int propid, int size, IntPtr buffer);
    
    	[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
    	internal static extern int GdipGetPropertyItemSize(IntPtr image, int propid, out int size);
    
    	[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
    	internal static extern int GdipLoadImageFromFile(string filename, out IntPtr image);
    
    	[DllImport("gdiplus.dll", EntryPoint="GdipDisposeImage", CharSet=CharSet.Unicode, ExactSpelling=true)]
    	private static extern int GdipDisposeImage(IntPtr image);
    
    
    
    
    
    	// EXIT tag value for thumbnail data. Value specified by EXIF standard
    	private const int THUMBNAIL_DATA = 0x501B;
    
    	/// <summary>
    	/// Reads the thumbnail in the given image. If no thumbnail is found, returns null
    	/// </summary>
    	public static Image ReadThumb (string imagePath)
    	{		
    		const int GDI_ERR_PROP_NOT_FOUND = 19;	// Property not found error
    		const int GDI_ERR_OUT_OF_MEMORY  = 3;
    
    		IntPtr hImage = IntPtr.Zero;
    		IntPtr buffer = IntPtr.Zero;	// Holds the thumbnail data
    		int ret;
    		ret = GdipLoadImageFromFile (imagePath, out hImage);
    
    		try
    		{
    			if (ret!=0)
    				throw createException (ret);
    
    			int propSize;
    			ret = GdipGetPropertyItemSize (hImage, THUMBNAIL_DATA, out propSize);
    			// Image has no thumbnail data in it. Return null
    			if (ret==GDI_ERR_PROP_NOT_FOUND)
    				return null;
    			if (ret!=0)
    				throw createException (ret);
    			
    
    			// Allocate a buffer in memory
    			buffer = Marshal.AllocHGlobal(propSize);
    			if (buffer == IntPtr.Zero)
    				throw createException (GDI_ERR_OUT_OF_MEMORY);
    
    			ret = GdipGetPropertyItem (hImage, THUMBNAIL_DATA, propSize, buffer);						
    			if (ret!=0)
    				throw createException (ret);
    		
    			// buffer has the thumbnail data. Now we have to convert it to
    			// an Image
    			return convertFromMemory (buffer);
    		}
    
    		finally
    		{
    			// Free the buffer
    			if (buffer != IntPtr.Zero)
    				Marshal.FreeHGlobal (buffer);
    
    			GdipDisposeImage (hImage);
    		}
    	}
    
    	/// <summary>
    	/// Generates an exception depending on the GDI+ error codes (I removed some error
    	/// codes)
    	/// </summary>
    	private static Exception createException (int gdipErrorCode)
    	{
    		switch (gdipErrorCode)
    		{
    			case 1:
    				return new ExternalException("Gdiplus Generic Error", -2147467259);
    			case 2:
    				return new ArgumentException("Gdiplus Invalid Parameter");
    			case 3:
    				return new OutOfMemoryException("Gdiplus Out Of Memory");
    			case 4:
    				return new InvalidOperationException("Gdiplus Object Busy");
    			case 5:
    				return new OutOfMemoryException("Gdiplus Insufficient Buffer");			
    			case 7:
    				return new ExternalException("Gdiplus Generic Error", -2147467259);
    			case 8:
    				return new InvalidOperationException("Gdiplus Wrong State");
    			case 9:
    				return new ExternalException("Gdiplus Aborted", -2147467260);
    			case 10:
    				return new FileNotFoundException("Gdiplus File Not Found");
    			case 11:
    				return new OverflowException("Gdiplus Over flow");
    			case 12:
    				return new ExternalException("Gdiplus Access Denied", -2147024891);
    			case 13:
    				return new ArgumentException("Gdiplus Unknown Image Format");
    			case 18:
    				return new ExternalException("Gdiplus Not Initialized", -2147467259);
    			case 20:
    				return new ArgumentException("Gdiplus Property Not Supported Error");
    		}
    
    		return new ExternalException("Gdiplus Unknown Error", -2147418113);
    	}
    
    
    
    	/// <summary>
    	/// Converts the IntPtr buffer to a property item and then converts its 
    	/// value to a Drawing.Image item
    	/// </summary>
    	private static Image convertFromMemory (IntPtr thumbData)
    	{
    		propertyItemInternal prop =	
    			(propertyItemInternal)Marshal.PtrToStructure 
    			(thumbData, typeof (propertyItemInternal));
    
    		// The image data is in the form of a byte array. Write all 
    		// the bytes to a stream and create a new image from that stream
    		byte[] imageBytes = prop.Value;
    		MemoryStream stream = new MemoryStream (imageBytes.Length);
    		stream.Write (imageBytes, 0, imageBytes.Length);
    		
    		return Image.FromStream(stream);
    	}
    
    	/// <summary>
    	/// Used in Marshal.PtrToStructure().
    	/// We need this dummy class because Imaging.PropertyItem is not a "blittable"
    	/// class and Marshal.PtrToStructure only accepted blittable classes.
    	/// (It's not blitable because it uses a byte[] array and that's not a blittable
    	/// type. See MSDN for a definition of Blittable.)
    	/// </summary>
    	[StructLayout(LayoutKind.Sequential)]
    		private	class propertyItemInternal
    	{
    		public int id=0;
    		public int len=0;
    		public short type=0;
    		public IntPtr value=IntPtr.Zero;
    
    		public byte[] Value
    		{
    			get
    			{
    				byte[] bytes = new byte[(uint)len];
    				Marshal.Copy(value, bytes, 0, len);
    				return bytes;
    			}
    		}
    	}
    }
    Last edited by MrPolite; Jun 5th, 2005 at 04:36 PM.
    rate my posts if they help ya!
    Extract thumbnail without reading the whole image file: (C# - VB)
    Apply texture to bitmaps: (C# - VB)
    Extended console library: (VB)
    Save JPEG with a certain quality (image compression): (C# - VB )
    VB.NET to C# conversion tips!!

  2. #2

    Thread Starter
    l33t! MrPolite's Avatar
    Join Date
    Sep 2001
    Posts
    4,428

    Re: Extract thumbnail image from a file without reading the whole file

    I uploaded the second solution as a csharp file
    Attached Files Attached Files
    Last edited by MrPolite; Jun 5th, 2005 at 04:36 PM.
    rate my posts if they help ya!
    Extract thumbnail without reading the whole image file: (C# - VB)
    Apply texture to bitmaps: (C# - VB)
    Extended console library: (VB)
    Save JPEG with a certain quality (image compression): (C# - VB )
    VB.NET to C# conversion tips!!

  3. #3
    New Member
    Join Date
    Aug 2005
    Posts
    1

    Re: Extract thumbnail image from a file without reading the whole file

    Sorry OOT...

    i have a question...

    i build an application using VB.NET. in that application there's a method that load an image file from network (fully shared folder). Today i found out that i got an Out of Memory error message when trying to load the image to some image object in VB.NET. The image file (in TIF format) was generated by scanning some document and saved it in TIF format, i dont know what software they were using. If i tried to preview the image file with windows thumbnail it only shows ACDSee icon. But when i cropped that image file using image editor from ACDSee, without resizing the image resolution, the error message didnt occured.

    Does anyone know what is the cause of that? and how to resolve it?
    Is there any connection with windows hotfix updates ???
    i use windows XP and VS.NET 2003 framework v1.1

    TIA

  4. #4

    Thread Starter
    l33t! MrPolite's Avatar
    Join Date
    Sep 2001
    Posts
    4,428

    Re: Extract thumbnail image from a file without reading the whole file

    hmm this doesn't directly relate to this post you know
    but I'd say possibly the software you used didn't write somethign correctly to the image? (Or possibly used a TIFF convention that isn't used normally so windows and .NET wouldnt recognize it but ACDSee would?)
    rate my posts if they help ya!
    Extract thumbnail without reading the whole image file: (C# - VB)
    Apply texture to bitmaps: (C# - VB)
    Extended console library: (VB)
    Save JPEG with a certain quality (image compression): (C# - VB )
    VB.NET to C# conversion tips!!

  5. #5
    New Member
    Join Date
    Mar 2006
    Posts
    1

    Re: Extract thumbnail image from a file without reading the whole file

    i got ur function but how to display the thumbnail out ?
    thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width