views:

238

answers:

7

Hello,

I'm trying to write a method to reduce the size of any image 50% each time is called but I've found a problem. Sometimes, I end up with a bigger filesize while the image is really just half of what it was. I'm taking care of DPI and PixelFormat. What else am I missing?

Thank you for your time.

public Bitmap ResizeBitmap(Bitmap origBitmap, int nWidth, int nHeight)
{
    Bitmap newBitmap = new Bitmap(nWidth, nHeight, origBitmap.PixelFormat);

    newBitmap.SetResolution(
       origBitmap.HorizontalResolution, 
       origBitmap.VerticalResolution);

    using (Graphics g = Graphics.FromImage((Image)newBitmap))
    {
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;

        g.DrawImage(origBitmap, 0, 0, nWidth, nHeight);
    }

    return newBitmap;
}

http://imgur.com/lY9BN.png

http://imgur.com/KSka0.png

Edit: Here's the missing code:

int width = (int)(bitmap.Width * 0.5f);
int height = (int)(bitmap.Height * 0.5f);
Bitmap resizedBitmap = ResizeBitmap(bitmap, width, height);
resizedBitmap.Save(newFilename);

Edit 2: Based on your comments, this is the solution I've found:

private void saveAsJPEG(string savingPath, Bitmap bitmap, long quality)
{
    EncoderParameter parameter = new EncoderParameter(Encoder.Compression, quality);
    ImageCodecInfo encoder = getEncoder(ImageFormat.Jpeg);
    if (encoder != null)
    {
        EncoderParameters encoderParams = new EncoderParameters(1);
        encoderParams.Param[0] = parameter;
        bitmap.Save(savingPath, encoder, encoderParams);
    }
}

private ImageCodecInfo getEncoder(ImageFormat format)
{

    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo codec in codecs)
        if (codec.FormatID == format.Guid)
            return codec;

    return null;
}
+6  A: 

You are most probably saving the jpeg image with a low compression ratio (high quality).

Marek
My guess as well.
mafutrct
Well, I'm not choosing any compression ratio. Is there a way I can use the same compression ratio as in the original image? Thanks.
Matías
@Matías - from what I remember the compression ratio isn't stored in the header so that information is lost once you've saved the file.
ChrisF
@Matiás: the default compression value used when none is specified is high. I am using an approach to iterate the compression level values until the output is of suitable size (when user selects to limit output size to a certain value when exporting jpeg from the application).
Marek
+1  A: 

I would say that you are saving it as a high quality jpeg (less compression == more space)

Bryan Denny
+2  A: 

The problem isn't situated in the code you posted, but where you save the JPEG. What compression ratio are you using?

Gerrie Schenck
+1  A: 

Seeming as your bitmaps are actually saved as jpegs, and knowing how c# likes to do things for you, it is probably saving with a default compression level that is fairly low. The original jpeg must have a decent compression level on it. Even if it was the same compression level, the way the jpeg format works may mean that you have found a fluke image that dose bloat at a lower resolution.

A bit map though is an exact size, if it is ten pixels wide and ten high, it is 100 pixels, in say 16 bit (2 byte) colour that's 200 bytes, plus a bit of header.

lower the resolution to 5 by 5, you have 50 bytes plus a bit of header, should be the same amount of header though.

thecoshman
+3  A: 

Recompressing a JPEG image is something you ought to avoid. But the real problem here is the InterpolationMode, a high quality one produces a lot of subtly shaded pixels, making it harder to compress the image. Using InterpolationMode.NearestNeighbor should avoid that, but at the cost of getting a lower quality image.

Hans Passant
I've tried that, and the filesize is still bigger than the original image.
Matías
@Matías: what happens when you start out with a PNG so that the noise due to the 1st compression is avoided?
Hans Passant
@nobugz: What do you mean? My source images are always JPEG, sorry I didn't mention that before.
Matías
I think this advice is backwards. JPEG compresses better on smoothly changing input rather than jaggy input.
Mark Ransom
@Mark: HQ interpolation is *not* smooth. The pixels dither frequently, off by +/- 4 or so.
Hans Passant
+1  A: 

Try this code to save the JPEG you have. It will allow you setting the compression quality. Experiment and see if it helps your size problem:

private void saveJpeg(string path, Bitmap img, long quality)
{
    EncoderParameter parameter = new EncoderParameter(Encoder.Quality, quality);
    ImageCodecInfo encoder = this.getEncoderInfo("image/jpeg");
    if (encoder != null)
    {
        EncoderParameters encoderParams = new EncoderParameters(1);
        encoderParams.Param[0] = parameter;
        img.Save(path, encoder, encoderParams);
    }
}

private ImageCodecInfo getEncoderInfo(string mimeType)
{
    ImageCodecInfo[] imageEncoders = ImageCodecInfo.GetImageEncoders();
    for (int i = 0; i < imageEncoders.Length; i++)
    {
        if (imageEncoders[i].MimeType == mimeType)
        {
            return imageEncoders[i];
        }
    }
    return null;
}
Jefim
what's getEncoderInfo?
Matías
Sorry, I completely forgot about that method. Now it should be complete (even though I would guess it is too late now :))
Jefim
Thank you, works great!
Matías
+1  A: 

It seems like the original is the output from a digital camera. I think the camera producers use proprietary super optimized compression algoritms, while a common library may have a less effective implementation. (And of course you're probably not using the same compression level as the camera.)

In addition the camera uses the raw data (possibly doing some "magic" preprocessing on it first) from its sensors as input. While when you re-compress it, the input has compression and rescaling artifacts in it which may cause the compression to be less effective.

Actually i think if you compress an image several times with the same jpg algoritm at the same level, it could grow in size because the artifacts from the previous compressions adds "details" that is harder to compress (and it becomes uglier too).

(The camera compression algoritm may also be optimized for the fact that the input is always a photo, while a general purpose algoritm may do some less effective stuff to work reasonably well both for photos and line art mixed images containing both photo and generated graphical elements.)

Stein G. Strindhaug
JPEG never works well on line art, unless you crank the quality levels to the max. You're correct that camera makers can optimize their JPEG algorithms though. When Sigma came out with their Foveon sensor, they discovered that the algorithm they were using was losing some of the detail that their new sensor was providing vs. the competition, and they had to adapt it.
Mark Ransom
Of course it's not good for line art (use png etc. for that!). I was actually thinking of mixed images with lots of different types of graphics. (I've updated my answer)
Stein G. Strindhaug