Algorithm to resize image and maintain aspect ratio to fit iPhone
Asked Answered
E

5

12

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);
        }
    }
}
Engraving answered 23/10, 2011 at 0:53 Comment(1)
The only problem there is you act immediately after you check w>960, and assume that that new_h=h*(new_w/w) <=640, which may or may not be true. You must check bounds on both first.Blooded
S
24

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.

Sextans answered 23/10, 2011 at 4:15 Comment(3)
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.Pernick
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).Gentlemanatarms
@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.Sextans
H
43

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.

Hydrotherapeutics answered 25/4, 2012 at 14:37 Comment(1)
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;Nathalie
S
24

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.

Sextans answered 23/10, 2011 at 4:15 Comment(3)
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.Pernick
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).Gentlemanatarms
@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.Sextans
S
9

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);
Softwood answered 10/10, 2013 at 16:44 Comment(1)
This is simpler than the other answers. Use Math.min() to "fit" and use Math.max() to "fill".Meistersinger
A
3

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
Antakiya answered 16/6, 2014 at 18:50 Comment(0)
B
0

Similar to DevProd's answer, but I struggled to follow it myself because of the naming convention. Hopefully this is a little clearer:

How do I best-fit some media (a photo) inside a container?

//Define media size and container size
int mediaWidth = 600;
int mediaHeight = 600;
int containerWidth = 1024;
int containerHeight= 768;

//Calculate best fit (whichever dimension has a smaller 'fit')
float wFits   = containerWidth  / mediaWidth;
float hFits   = containerHeight / mediaHeight;
float minFits = wFits > hFits ? hFits : wFits;

//The new size of the media, best-fit'ing inside the container
int width  = (int) (mediaWidth*minFits);
int height = (int) (mediaHeight*minFits);
Bothwell answered 7/3, 2015 at 14:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.