#1
  1. Frustrated Wizard
    ASP Hero (2000 - 2499 posts)

    Join Date
    Apr 2005
    Posts
    2,419
    Rep Power
    24

    Fuzzy thumbnails, bad image quality


    Hi All,

    On my webpage i have a .NET script using the GetThumbnailImage function to create jpeg thumbnails, but a known problem with this is the creation of fuzzy images having bad image quality. Here is the piece of code (it receives the image file path, desired width and height from the querystring):

    Code:
    <%@Import Namespace="System.IO" %>
    <%@Import Namespace="System.Drawing.Imaging" %>
    <%@Import Namespace="System.Drawing.Drawing2D" %>
    
    <script language="vb" runat="server">
    
       Function ThumbnailCallback() as Boolean
        Return False
      End Function
    
      Sub Page_Load(sender as Object, e as EventArgs)
    
        'Get the image path and maximum width and height from the querystring
        Dim IMAGE_DIRECTORY as String = Request.QueryString("img")
        Dim maxWidth as Integer = Request.QueryString("w")
        Dim maxHeight as Integer = Request.QueryString("h")
        
        'Get information about the original (large) image
        Dim currentImage as System.Drawing.Image
        currentImage = System.Drawing.Image.FromFile(Server.MapPath(IMAGE_DIRECTORY))
    
        Dim imgHeight, imgWidth as Integer      
        imgHeight = currentImage.Height
        imgWidth = currentImage.Width
    
        'Rescale the size of the original width and height
        If imgWidth > maxWidth OR imgHeight > maxHeight then
            'Determine what dimension is off by more
            Dim deltaWidth as Integer = imgWidth - maxWidth
            Dim deltaHeight as Integer = imgHeight - maxHeight
            Dim scaleFactor as Double
            
            If deltaHeight > deltaWidth then
              'Scale by the height
              scaleFactor = maxHeight / imgHeight
            Else
              'Scale by the Width
              scaleFactor = maxWidth / imgWidth
            End If
            
            imgWidth *= scaleFactor
            imgHeight *= scaleFactor          
        End If
    
        'Set the content type to image/jpeg so the <img> tag can handle it
        Response.ContentType = "image/jpeg"    
    
        'In case the height or width of the original image do not equal the rescaled values
        If imgHeight <> currentImage.Height Or imgWidth <> currentImage.Width then
    
    	'Create useless callback
          	Dim dummyCallBack as System.Drawing.Image.GetThumbNailImageAbort
          	dummyCallBack = New System.Drawing.Image.GetThumbnailImageAbort(AddressOf ThumbnailCallback)
    
    	'Create the thumbnail image (dummycallback uses a fake zeropointer)
          	Dim thumbNailImg as System.Drawing.Image
          	thumbNailImg = currentImage.GetThumbnailImage(imgWidth, imgHeight, dummyCallBack, IntPtr.Zero)
    
    	'Stream the thumbnail response towards the <img> tag
          	thumbNailImg.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg)  
    
          	'Clean up / Dispose...
          	ThumbnailImg.Dispose()
    
        Else
    
          	currentImage.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg)  
    
        End If
    
        'Clean up by dispose
        currentImage.Dispose()
    
      End Sub
    </script>
    To improve image quality i read something about the parameters SmoothingMode, CompositingQuality and InterpolationMode. Below is a piece of sample code using these parameters:

    Code:
    Bitmap bmp = new Bitmap(imgWidth, imgHeight);  
    
    System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(bmp); 
    gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality  ; 
    gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; 
    gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; 
    
    System.Drawing.Rectangle rectDestination = new System.Drawing.Rectangle(0, 0, imgWidth, imgHeight);
    gr.DrawImage(currentImage, rectDestination, 0, 0, currentImage.Width, currentImage.Height, GraphicsUnit.Pixel);  
    
    bmp.Save(path); 
      
    bmp.Dispose();
    currentImage.Dispose();
    I tried different ways to integrate this approach with my original code but it's just not working. I just want to get rid of the fuzzy thumbnails and create them with good image quality.

    Does anyone have experience with this? How to combine the 2 pieces of code above to get a script which creates optimal thumbs?
    Last edited by leuvenaar; May 12th, 2012 at 02:35 PM.
  2. #2
  3. Couch Potato Wizard
    ASP Mastermind (5000+ posts)

    Join Date
    Jan 2005
    Location
    India
    Posts
    13,651
    Rep Power
    2689
    I use this code to resize images
    Namespaces used
    Code:
    Imports System.Drawing
    Imports System.Drawing.Imaging
    Code:
        Public Shared Function ResizeImages(ByVal Width As Integer, ByVal Height As Integer, ByVal InputImage As Stream) As Bitmap
            Dim sWidth As Integer = Width
            Dim sHeight As Integer = Height
    
            Dim bmpOut As Bitmap = Nothing
            Dim loBMP As New Bitmap(InputImage)
            Dim loFormat As ImageFormat = loBMP.RawFormat
    
            Dim lnRatio, lnTemp As Decimal
            Dim lnNewWidth As Integer = 0
            Dim lnNewHeight As Integer = 0
    
    
            If loBMP.Width > loBMP.Height Then
                lnRatio = sWidth / loBMP.Width
                lnNewWidth = sWidth
                lnTemp = loBMP.Height * lnRatio
                lnNewHeight = CInt(lnTemp)
    
            Else
                lnRatio = sHeight / loBMP.Height
                lnNewHeight = sHeight
                lnTemp = loBMP.Width * lnRatio
                lnNewWidth = CInt(lnTemp)
            End If
    
            bmpOut = New Bitmap(lnNewWidth, lnNewHeight)
            Dim g As Graphics = Graphics.FromImage(bmpOut)
            g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
            g.FillRectangle(Brushes.White, 0, 0, lnNewWidth, lnNewHeight)
            g.DrawImage(loBMP, 0, 0, lnNewWidth, lnNewHeight)
            loBMP.Dispose()
    
            Return bmpOut
        End Function
    This is how i call this function
    Code:
    'RESIZE IMAGE - FOR THUMBNAIL
    Dim bmpOut As System.Drawing.Bitmap = CGlobal.ResizeImages(310, 160, fuImage.PostedFile.InputStream())
    
    'UPLOAD THUMBNAIL (310x160)
    bmpOut.Save(ConfigurationManager.AppSettings("ImagePath") & strThumb, System.Drawing.Imaging.ImageFormat.Jpeg)

    Comments on this post

    • leuvenaar agrees : Thanks for responding, here is some points.
    Laziness is my religion and Sunday is my God

    Get the Mantra!
  4. #3
  5. Moderator From Beyond
    ASP Mastermind (5000+ posts)

    Join Date
    Sep 2004
    Location
    Israel
    Posts
    31,135
    Rep Power
    2926
    Another way to go about this is using my Image Resize Code and changing a few things.

    First, instead of saving the thumbnail to file on the server disk, send it directly to the response stream. To achieve this, change the existing "Save" method to be this instead:
    Code:
    public void Save(System.IO.Stream targetStream, System.Drawing.Imaging.ImageFormat fileFormat, int compressionLevel)
    {
        if (compressionLevel <= 0 || compressionLevel > 100)
        {
            try
            {
                m_Bitmap.Save(targetStream, fileFormat);
            }
            catch (Exception exp)
            {
                throw new Exception("Error saving file: " + exp.Message);
            }
        }
        else
        {
            ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);
    
            // Create an Encoder object based on the GUID for the Quality parameter category.
            System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
    
            // Create an EncoderParameters object.
            // An EncoderParameters object has an array of EncoderParameter
            // objects. In this case, there is only one EncoderParameter object in the array.
            EncoderParameters myEncoderParameters = new EncoderParameters(1);
            EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, (long)compressionLevel);
            myEncoderParameters.Param[0] = myEncoderParameter;
            try
            {
                m_Bitmap.Save(targetStream, jgpEncoder, myEncoderParameters);
            }
            catch (Exception exp)
            {
                throw new Exception("Error saving file with compression of " + compressionLevel + ": " + exp.ToString());
            }
        }
    }
    And then call it like this:
    Code:
    wrapper.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg, compressionLevel);
    This will send the image directly to the browser instead of saving to disk.

    Now to actually use it, assign the image source directly to the ".aspx" file with proper parameters:
    Code:
    <img src='ImageResizer.aspx?img=/images/properties/" & rs("id") & "/" & objFile.Name & "&width=100&height=70&virtual=1' border='0' />
    Remember to tell the code it's a virtual path, you can also have it auto calculate the width by giving -1 instead of fixed value.

    Just to make it clear, here are some basic "rules":

    Width and Height in my code are determined by the querystring values you give. The options are:
    • Give a fixed width (e.g. 200) and height of -1 - this will cause the algorithm to calculate the height while keeping the same ratio, e.g. if the original width was 800 and the original height was 1600 then the new height will be 400 to keep the 2:1 ratio.
    • Give a fixed height (e.g. 200) and width of -1 - this will cause the algorithm to calculate the width while keeping the same ratio, e.g. if the original width was 800 and the original height was 1600 then the new width will be 100 to keep the 2:1 ratio.
    • Give both fixed width and fixed height - this will ignore the ratio and force the resize, usually resulting in blurred image which can't be avoided.

    Comments on this post

    • leuvenaar agrees : Excellent help, this is what i needed. I can only give 18 points :-(

Similar Threads

  1. Getting Image dimensions of uploaded Image file
    By GEM1204 in forum .NET Development
    Replies: 6
    Last Post: October 25th, 2006, 06:23 PM
  2. ASP image Thumbnails
    By wakeboardking in forum ASP Development
    Replies: 5
    Last Post: February 28th, 2006, 04:31 AM
  3. vb: image resizing from stream to SQL Server
    By timandkids in forum .NET Development
    Replies: 0
    Last Post: November 10th, 2004, 12:54 PM
  4. Displaying alternate image when image source not found
    By Kate Perry in forum ASP Development
    Replies: 9
    Last Post: August 17th, 2004, 10:14 AM
  5. Problem displaying image from Access in table
    By FDisk in forum .NET Development
    Replies: 2
    Last Post: April 18th, 2004, 01:44 AM

IMN logo majestic logo threadwatch logo seochat tools logo