Pregunta

I want to save image in proportional size without losing its quality. This right if I do that?

var pathImage = ""; 
var pathFolder = "";  
System.Drawing.Image image;  
System.Drawing.Image thumb;  
image = System.Drawing.Image.FromFile(pathImage);  
var imageWidth = image.Width;  
var imageHeight = image.Height;  
width = (180 * imageWidth) / imageHeight;  
height = 180;  
thumb = image.GetThumbnailImage(width, height, null, System.IntPtr.Zero);  
ImageCodecInfo myCodecInfoEncoder = GetEncoder(ImageFormat.Jpeg);  
var myEncoderParameters = new EncoderParameters(1);  
var myEncoderParameter = new EncoderParameter(Encoder.Quality, 50L);  
myEncoderParameters.Param[0] = myEncoderParameter;  
thumb.Save(pathFolder, myCodecInfoEncoder, myEncoderParameters);  

Thanks.

¿Fue útil?

Solución

This is actually multiple questions rolled into one:

  • How do you get high quality scaling from GDI+?
  • How do you maintain transparency and original image information when saving?
  • What is the best way to maintain image quality when saving to disk?

In the rest of this I try to answer each of these.


HIGH QUALITY SCALING

To start answering the first of these, you probably don't want to use the GetThumbnail() routine. It is pretty much a black box and doesn't let you change very many settings. Instead you will want to do something like this hastily crafted routine:

//      aspectScale = when true, create an image that is up to the size (new w, new h)
//                    that maintains original image aspect. When false, just create a
//                    new image that is exactly the (new w, new h)
private static Bitmap ReduceImageSize(Bitmap original, int newWidth, int newHeight, bool aspectScale)
{
    if (original == null || (original.Width < newWidth && original.Height < newHeight)) return original;

    // calculate scale
    var scaleX = newWidth / (float)original.Width;
    var scaleY = newHeight / (float)original.Height;
    if (scaleY < scaleX) scaleX = scaleY;

    // calc new w/h
    var calcWidth = (aspectScale ? (int)Math.Floor(original.Width * scaleX) : newWidth);
    var calcHeight = (aspectScale ? (int)Math.Floor(original.Height * scaleX) : newHeight);
    var resultImg = new Bitmap(calcWidth, calcHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    using (var offsetMtx = new System.Drawing.Drawing2D.Matrix())
    {
        offsetMtx.Translate(calcWidth / 2.0f, calcHeight / 2.0f);
        offsetMtx.Scale(scaleX, scaleX);
        offsetMtx.Translate(-original.Width / 2.0f, -original.Height / 2.0f);

        using (var resultGraphics = System.Drawing.Graphics.FromImage(resultImg))
        {
            // scale
            resultGraphics.Transform = offsetMtx;

            // IMPORTANT: Compromise between quality and speed for these settings
            resultGraphics.SmoothingMode = SmoothingMode.HighQuality;
            resultGraphics.InterpolationMode = InterpolationMode.Bicubic;

            // For a white background add: resultGraphics.Clear(Color.White);

            // render the image
            resultGraphics.DrawImage(original, Point.Empty);
        }
    }

    return resultImg;
}

What you'll notice about this routine is you can change the SmoothingMode and you can also change the InterpolationMode. Those allow you to customize the quality of the scaling that you get out of this routine. To call my simple example you would just use:

var newImage = ReduceImageSize(originalImage, 100, 100, true);

This would scale your original image to something less than or equal to 100px wide and the same for the height (which is probably what you want).

HIGH QUALITY SAVE

For the other piece of this - how to save, if you need transparency you can't use a JPEG. As a simple place to start I would recommend using PNG format for saving. That looks like:

newImage.Save(fileName + ".png", ImageFormat.Png);

PNG includes some level of compression by default (much like JPEG) but it also maintains transparency which seems to be something that is important to you. If you need to compress further you can look at the JPEG save that you already have, reducing the pixel depth or indexed colors. If you search google you should come up with excellent ways of supporting each of these.

MAINTAIN ORIGINAL DATA

You didn't ask about it but something to realize is that images are more than just a bunch of pixels - they also have Metadata. Metadata includes things such as EXIF information or other pieces of data that describe how the image was taken. When .NET creates a new image and you save that you have effectively stripped this data out of the image (which may or may not be the goal here). If you want to keep this data one place to start is by using the PropertyItems list. You can iterate over this array on the old image, copying to the new image.

This actually doesn't expose all of the data that is available though. If you truly want to be complete in copying metadata from the original image to the output image you are going to probably need to read up on image specifications or use a tool such as this one from Phil Harvey: http://www.sno.phy.queensu.ca/~phil/exiftool/.

Hope that helps set you in the right direction! Best of luck!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top