Algorithm to resize image and maintain aspect ratio to fit iPhone

19,421

Solution 1

I think the following should give you the idea. It's not in any particular language, but rather a C-like pseudo code.

shortSideMax = 640;
longSideMax = 960;
function Resize(image)
{
    if (image.width >= image.height)
    {
        if (image.width <= longSideMax && image.height <= shortSideMax)
            return image;  // no resizing required
        wRatio = longSideMax / image.width;
        hRatio = shortSideMax / image.height;
    }
    else
    {
        if (image.height <= longSideMax && image.width <= shortSideMax)
            return image; // no resizing required
        wRatio = shortSideMax / image.width;
        hRatio = longSideMax / image.height;
    }

    // hRatio and wRatio now have the scaling factors for height and width.
    // You want the smallest of the two to ensure that the resulting image
    // fits in the desired frame and maintains the aspect ratio.
    resizeRatio = Min(wRatio, hRatio);

    newHeight = image.Height * resizeRatio;
    newWidth = image.Width * resizeRatio;

    // Now call function to resize original image to [newWidth, newHeight]
    // and return the result.
}

The efficiency of this code, or what you have, won't be an issue. The time it takes to actually resize the image will dwarf the time it takes to do a couple of comparisons, two divides, and two multiplies.

Is this a "more mathematical" way to do it? I suppose, in that it collapses your four cases into two. But the approach is essentially the same.

Solution 2

Maybe a slightly shorter routine would be:

// Calculate resize ratios for resizing 
float ratioW = targetWidth / oldWidth; 
float ratioH = targetHeight / oldHeight;

// smaller ratio will ensure that the image fits in the view
float ratio = ratioW < ratioH?ratioW:ratioH;

newWidth = oldWidth*ratio;
newHeight = oldHeight*ratio;

Obviously if the ratio is > 1, then it's enlarging, if < 1 then it's shrinking.

Solution 3

Below, the simplest way I know to keep proportions. Hope it helps.

Javascript

function resize(width, height, maxWidth, maxHeight) {
    var ratio = Math.min(maxWidth / width, maxHeight / height);
    var newWidth = ratio * width;
    var newHeight = ratio * height;

    console.log(newWidth + ' ' + newHeight); // Test

    // Process resizing...
}

resize(1280, 1024, 600, 300);

PHP

function resize($width, $height, $maxWidth, $maxHeight) {
    $ratio = min(array($maxWidth / $width, $maxHeight / $height));
    $newWidth = $ratio * $width;
    $newHeight = $ratio * $height;

    echo $newWidth . ' ' . $newHeight; // Test

    // Process resizing...
}

resize(1600, 1280, 150, 150);

Solution 4

Any one coming to this page from Google looking for ASPECT FILL not ASPECT FIT, it is simply a matter of switching the ratio selection code i.e:

Jim's answer:

resizeRatio = Min(wRatio, hRatio); //Aspect Fit

becomes

resizeRatio = Max(wRatio, hRatio); //Aspect Fill

DevProd's answer:

float ratio = ratioW < ratioH?ratioW:ratioH; //Aspect Fit

becomes

float ratio = ratioW > ratioH?ratioW:ratioH; //Aspect Fill
Share:
19,421
Alex Coplan
Author by

Alex Coplan

Hello! I'm a Computer Science student. I like all things computery, but am especially interested in Ruby (+Rails), Javascript, AI and NLP. I enjoy hacking, making anything web-related and have written a couple of iPhone apps. Other things I like: Music, especially the organ! Languages: I can speak quite a bit of German and I'm a beginner in Spanish. I have a slight obsession with Duolingo Also, before you downvote my post:

Updated on June 03, 2022

Comments

  • Alex Coplan
    Alex Coplan about 2 years

    I'm creating a web service for an iPhone app to interact with.

    When my client uploads images server-side, I want my php script to resize the image, whilst maintaining the aspect ratio, so that it will fit onto the iPhone screen. (i.e. the longest side is <= 960 and the shortest <= 640

    I've created a mock-up in JS, simply because I find it easier to do quickly.

    I'm pretty sure, though I may be wrong, that this isn't the most efficient way of doing it. Could someone correct me with either better logic (especially the bit at the start), or a more mathematical way of approaching this?

    var w = 960, h = 960, new_w, new_h;
    if (w >= h && w > 960 || h >= w && h > 960 || w >= h && h > 640 || h >= w && w > 640) {
        if (w > h) {
            if (w>960) {
                new_w = 960;
                new_h = h*(new_w/w);
            }
            if (h>640) {
                new_h = 640;
                new_w = w*(new_h/h);
            }
        }
        else {
            if (h>960) {
                new_h = 960;
                new_w = w*(new_h/h);
            }
            if (w>640) {
                new_w = 640;
                new_h = h*(new_w/w);
            }
        }
    }
    
  • Kasapo
    Kasapo almost 12 years
    I had pretty much the exact same algorithm, but it was nice to see someone else's solution to this problem-- and kudos on the note that actually performing resizing action on the image will make the algorithm's efficiency a moot point.
  • Manish Jain
    Manish Jain about 11 years
    this algo not working for me when the image's height is 800 and width is 600 while shortsidemax is 275(vertical) and longsidemax is 480(horizontal).
  • Jim Mischel
    Jim Mischel about 11 years
    @ManishJain: You should post a question, then, showing what you've tried and the results you're getting. Be sure to link this answer and explain that it's not working.
  • Nikola Lajic
    Nikola Lajic almost 10 years
    I just want to note that changing between aspect fit and aspect fill is really simple. For aspect fill just filp the sign: float ratio = ratioW > ratioH ? ratioW : ratioH;
  • akinuri
    akinuri about 4 years
    This is simpler than the other answers. Use Math.min() to "fit" and use Math.max() to "fill".