1

I tried to find out, but I couldn't.

A image, for example, 241x76 has a total of 18,316 pixels (241 * 76). The resize rule is, the amount of pixels cannot pass 10,000. Then, how can I get the new size keeping the aspect ratio and getting less than 10,000 pixels?

Deestan
  • 16,738
  • 4
  • 32
  • 48
Ratata Tata
  • 2,781
  • 1
  • 34
  • 49

5 Answers5

6

Pseudocode:

pixels = width * height
if (pixels > 10000) then
  ratio = width / height
  scale = sqrt(pixels / 10000)
  height2 = floor(height / scale)
  width2 = floor(ratio * height / scale)
  ASSERT width2 * height2 <= 10000
end if

Remember to use floating-point math for all calculations involving ratio and scale when implementing.


Python

import math

def capDimensions(width, height, maxPixels=10000):
  pixels = width * height
  if (pixels <= maxPixels):
    return (width, height)

  ratio = float(width) / height
  scale = math.sqrt(float(pixels) / maxPixels)
  height2 = int(float(height) / scale)
  width2 = int(ratio * height / scale)
  return (width2, height2)
Deestan
  • 16,738
  • 4
  • 32
  • 48
  • Awesome! Did you know the math name of { w*h = 10000; w/h = 3.17; } (in other words, what was done in the answer)? – Ratata Tata Apr 11 '12 at 14:58
  • I just put down all the variables and their relationships on paper and fiddled with them. Don't know if there are any fancy names for any of it. :) – Deestan Apr 11 '12 at 15:31
1

An alternative function in C# which takes and returns an Image object:

    using System.Drawing.Drawing2D;

    public Image resizeMaxPixels(int maxPixelCount, Image originalImg)
    { 
        Double pixelCount = originalImg.Width * originalImg.Height;
        if (pixelCount < maxPixelCount) //no downsize needed
        {
            return originalImg;
        }
        else
        {
            //EDIT: not actually needed - scaleRatio takes care of this
            //Double aspectRatio = originalImg.Width / originalImg.Height;

            //scale varies as the square root of the ratio (width x height):
            Double scaleRatio = Math.Sqrt(maxPixelCount / pixelCount);

            Int32 newWidth = (Int32)(originalImg.Width * scaleRatio);
            Int32 newHeight = (Int32)(originalImg.Height * scaleRatio);
            Bitmap newImg = new Bitmap(newWidth, newHeight);
            //this keeps the quality as good as possible when resizing
            using (Graphics gr = Graphics.FromImage(newImg))
            {
                gr.SmoothingMode = SmoothingMode.AntiAlias;
                gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
                gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
                gr.DrawImage(originalImg, new Rectangle(0, 0, newWidth, newHeight));
            }
            return newImg;
        }
    }

with graphics code from the answer to Resizing an Image without losing any quality

EDIT: Calculating the aspect ratio is actually irrelevant here as we're already scaling the width and height by the (square root) of the total pixel ratio. You could use it to calculate the newWidth based on the newHeight (or vice versa) but this isn't necessary.

Community
  • 1
  • 1
Widor
  • 13,003
  • 7
  • 42
  • 64
1

Deestan's code works for square images, but in situations where the aspect ratio is different than 1, a square root won't do. You need to take scale to the power of aspect ratio divided by 2.

Observe (Python):

def capDimensions(width, height, maxPixels):
    pixels = width * height
    if (pixels <= maxPixels):
        return (width, height)
    ratio = float(width) / height
    scale = (float(pixels) / maxPixels)**(width/(height*2))
    height2 = round(float(height) / scale) 
    width2 = round(ratio * height2)
    return (width2, height2)

Let's compare the results.

initial dimensions: 450x600 initial pixels: 270000

I'm trying to resize to get as close as possible to 119850 pixels.

with Deestan's algorithm: capDimensions: 300x400 resized pixels: 67500

with the modified algorithm: capDimensions. 332x442 resized pixels: 82668

parallelogram
  • 139
  • 3
  • 10
0
width2 = int(ratio * height / scale)

would better be

width2 = int(ratio * height2)  

because this would potentially preserve the aspect ratio better (as height2 has been truncated).

Without introducing another variable like 'sc', one can write
new_height = floor(sqrt(m / r))

and

new_width = floor(sqrt(m * r))
given m=max_pixels (here: 10.000), r=ratio=w/h (here: 241/76 = 3.171)

Both results are independent of each other! From each new_value, you can calculate the other dimension, with (given: new_height) new_width = floor(new_height * r)
(given: new_width) new_height = floor(new_width / r)

Because of clipping the values (floor-function), both pairs of dimensions may differ in how close their ratio is to the original ratio; you'd choose the better pair.

user1016274
  • 4,071
  • 1
  • 23
  • 19
  • newbie to SO here: why the downvote? I didn't have the option to edit Deestan's answer with the python example. – user1016274 Apr 12 '12 at 15:31
  • this is a little difficult to interpret, but fundamentally more correct than the other answers - if you want to get as close as possible to the limit, you have to account for the rounding, which can leave the aspect ratio further away from what it could potentially be. – CaptainCodeman Feb 03 '23 at 20:36
0

Scaling images down to max number of pixels, while maintaining aspect ratio

This is what I came up with this afternoon, while trying to solve the math problem on my own, for fun. My code seems to work fine, I tested with a few different shapes and sizes of images. Make sure to use floating point variables or the math will break.

Pseudocode

orig_width=1920
orig_height=1080
orig_pixels=(orig_width * orig_height)
max_pixels=180000

if (orig_pixels <= max_pixels) {
   # use original image
}
else {
   # scale image down
   ratio=sqrt(orig_pixels / max_pixels)
   new_width=floor(orig_width / ratio)
   new_height=floor(orig_height / ratio)
}

Example results

1920x1080 (1.77778 ratio) becomes 565x318  (1.77673 ratio, 179,670 pixels)
1000x1000 (1.00000 ratio) becomes 424x424  (1.00000 ratio, 179,776 pixels)
 200x1200 (0.16667 ratio) becomes 173x1039 (0.16651 ratio, 179,747 pixels)
Raven
  • 26
  • 2