views:

1066

answers:

3

How to resize an image an image in C# to a certain hard-disk size, like 2MiB? Is there a better way than trial and error (even if it's approximate, of course).

Any particular keywords to search for when trying to find the solution on the web?

+1  A: 

You can calculate an approximate information level for the image by taking the original image size divided by the number of pixels:

info = fileSize / (width * height);

I have an image that is 369636 bytes and 1200x800 pixels, so it uses ~0.385 bytes per pixel.

I have a smaller version that is 101111 bytes and 600x400 pixels, so it uses ~0.4213 bytes per pixel.

When you shrink an image you will see that it generally will contain slightly more information per pixel, in this case about 9% more. Depending on your type of images and how much you shrink them, you should be able to calculate an average for how much the information/pixel ration increases (c), so that you can calculate an approximate file size:

newFileSize = (fileSize / (width * height)) * (newWidth * newHeight) * c

From this you can extract a formula for how large you have to make an image to reach a specific file size:

newWidth * newHeight = (newFileSize / fileSize) * (width * height) / c

This will get you pretty close to the desired file size. If you want to get closer you can resize the image to the calculated size, compress it and calculate a new bytes per pixel value from the file size that you got.

Guffa
+1  A: 

If it's a 24bit BMP i think you would need to do something like this:

//initial size =  WxH
long bitsperpixel = 24; //for 24 bit BMP
double ratio;
long size = 2 * 1 << 20;//2MB = 2 * 2^20
size -= 0x35;//subtract the BMP header size from it
long newH, newW, left, right, middle,BMProwsize;
left = 1;
right = size;//binary search for new width and height
while (left < right)
{
    middle = (left + right + 1) / 2;
    newW = middle;
    ratio = Convert.ToDouble(newW) / Convert.ToDouble(W);
    newH = Convert.ToInt64(ratio * Convert.ToDouble(H));
    BMProwsize = 4 * ((newW * bitsperpixel + 31) / 32);
    //row size must be multiple of 4
    if (BMProwsize * newH <= size)
        left = middle;
    else
        right = middle-1;                
}

newW = left;
ratio = Convert.ToDouble(newW) / Convert.ToDouble(W);
newH = Convert.ToInt64(ratio * Convert.ToDouble(H));
//resize image to newW x newH and it should fit in <= 2 MB

If it is a different BMP type like 8 bit BMP also in the header section there will be more data specifying the actual color of each value from 0 to 255 so you will need to subtract more from the total file size before the binary search.

Razvi
A: 

It depends on what you are willing to change

  1. Make the size of the image smaller
  2. Change the format of the image
  3. If the format supports a lossy compression, decrease the quality
  4. If you are storing meta-data that you don't need, remove it
  5. Reduce the number of colors (and bits per pixel)
  6. Change to a paletted format
  7. Change to a paletted format and reduce the colors

The company I work for offers a free e-course in imaging. Chapter 5 has a lot of these tips

http://www.atalasoft.com/imaging-course/chapter-5/

It's hard to guess what the final disk size will be, but if you know a starting point you can get a pretty good estimate. Reducing the size will probably be proportional, reducing the bits per pixel will also likely be proportional.

If you change the format, compression or quality, it's really just a guess -- depends highly on the image content. You could probably get a good range by trying it on a corpus of images that matches what you think you'll be seeing.

Lou Franco