# php – Show correct aspect ratio-ThrowExceptions

Exception or error:

I am trying to get the aspect ratio of a photo but the following code shows wrong aspect ratio on a photo with 3776 in width and 2520 in height (472:315) but it shows correct aspect ratio on a photo with 3968 in width and 2232 in height though (16:9).

``````function gcd(\$a, \$b) {
if(\$a == 0 || \$b == 0) {
return abs(max(abs(\$a), abs(\$b)));
}

\$r = \$a % \$b;
return (\$r != 0) ? gcd(\$b, \$r) : abs(\$b);
}

\$gcd = gcd(\$widtho, \$heighto);
echo (\$widtho / \$gcd).':'.(\$heighto / \$gcd);
``````

How can I solve my problem?

How to solve:

Actually, 3780×2520 is an aspect ratio of 3:2; because you’re using 3776 for the width, 472:315 is the correct ratio. If you do the division, it comes out to 1.498, which is pretty close-enough to 1.5 to consider rounding to 3:2.

If you want only “standard” ratios (like “3:2” or “16:9”), you could pass the detected ratio to another function that rounds them to find the nearest/best-match instead.

This is a thrown-together function that can do the rounding for you (only tested against the dimensions in your example, so I can’t guarantee 100% success yet):

``````function findBestMatch(\$ratio) {
\$commonRatios = array(
array(1, '1:1'), array((4 / 3), '4:3'), array((3 / 2), '3:2'),
array((5 / 3), '5:3'), array((16 / 9), '16:9'), array(3, '3')
);

list(\$numerator, \$denominator) = explode(':', \$ratio);
\$value = \$numerator / \$denominator;

\$end = (count(\$commonRatios) - 1);
for (\$i = 0; \$i < \$end; \$i++) {
if (\$value == \$commonRatios[\$i][0]) {
// we have an equal-ratio; no need to check anything else!
return \$commonRatios[\$i][1];
} else if (\$value < \$commonRatios[\$i][0]) {
// this can only happen if the ratio is `< 1`
return \$commonRatios[\$i][1];
} else if ((\$value > \$commonRatios[\$i][0]) && (\$value < \$commonRatios[\$i + 1][0])) {
// the ratio is in-between the current common-ratio and the next in the list
// find whichever one it's closer-to and return that one.
return ((\$value - \$commonRatios[\$i][0]) < (\$commonRatios[\$i + 1][0] - \$value)) ? \$commonRatios[\$i][1] : \$commonRatios[\$i + 1][1];
}
}

// we didn't find a match; that means we have a ratio higher than our biggest common one
// return the original value
return \$ratio;
}
``````

To use this function, you pass in the ratio-string (not the numeric value) to it and it will attempt to “find a best match” in the common list of ratios.

Example usage:

``````\$widtho = 3968;
\$heighto = 2232;
\$gcd = gcd(\$widtho, \$heighto);
\$ratio = (\$widtho / \$gcd).':'.(\$heighto / \$gcd);
echo 'found: ' . \$ratio . "\n";
echo 'match: ' . findBestMatch(\$ratio) . "\n";

\$widtho = 3776;
\$heighto = 2520;
\$gcd = gcd(\$widtho, \$heighto);
\$ratio = (\$widtho / \$gcd).':'.(\$heighto / \$gcd);
echo 'found: ' . \$ratio . "\n";
echo 'match: ' . findBestMatch(\$ratio) . "\n";

\$widtho = 3780;
\$heighto = 2520;
\$gcd = gcd(\$widtho, \$heighto);
\$ratio = (\$widtho / \$gcd).':'.(\$heighto / \$gcd);
echo 'found: ' . \$ratio . "\n";
echo 'match: ' . findBestMatch(\$ratio) . "\n";
``````

The above test will output the following:

``````found: 16:9
match: 16:9

found: 472:315
match: 3:2

found: 3:2
match: 3:2
``````

* I took the list of “standard” aspect ratios from wikipedia, if you want a reference.

I found this gist which works nicely in Javascript. So I ported it to PHP.

``````  /**
* Returns the nearest aspect ratio of a width and height within a limited range of possible aspect ratios.
* In other words, while 649x360 technically has an aspect ratio of 649:360,
* it’s often useful to know that the nearest normal aspect ratio is actually 9:5 (648x360).
* @param \$width
* @param \$height
* @param int \$side The nearest ratio to side with. A number higher than zero
* tells the function to always return the nearest ratio that is equal or higher
* than the actual ratio, whereas a smaller number returns the nearest ratio higher
* that is equal or smaller than the actual ratio. Defaults to 0.
* @param int \$maxW The maximum width in the nearest normal aspect ratio. Defaults to 16.
* @param int \$maxH The maximum height in the nearest normal aspect ratio. Defaults to 16.
* @return string|null
*/

function closestAspectRatio(\$width, \$height, \$side = 0, \$maxW = 16, \$maxH = 16) {

\$ratiosT = [];
\$ratios = [];
\$ratio = (\$width * 100) / (\$height * 100);
\$maxW++;
\$maxH++;

for (\$w = 1; \$w < \$maxW; \$w++) {
for (\$h = 1; \$h < \$maxH; \$h++) {
\$ratioX = strval((\$w * 100) / (\$h * 100));
if (!array_key_exists(\$ratioX, \$ratiosT)) {
\$ratiosT[\$ratioX] = TRUE;
\$ratios[\$w . ':' . \$h] = \$ratioX;
}
}
}

\$match = NULL;

foreach (\$ratios as \$key => \$value) {

if (!\$match || (!\$side && abs(\$ratio - \$value) < abs(\$ratio - \$ratios[\$match])
) || (
\$side < 0 && \$value <= \$ratio && abs(\$ratio - \$value) < abs(\$ratio - \$ratios[\$match])
) || (
\$side > 0 && \$value >= \$ratio && abs(\$ratio - \$value) < abs(\$ratio - \$ratios[\$match])
)) {
\$match = \$key;
}
}

return \$match;
}
``````

Use it this way (with all parameters):

``````print closestAspectRatio(3776, 2520, 0, 16, 16); // prints 3:2
print closestAspectRatio(2520, 3776, 0, 16, 16); // prints 2:3
``````

Here is the JS demo for reference:

``````console.log('3776 x 2520 = ' + nearestNormalAspectRatio(3776, 2520, 0));
console.log('2520 x 3776 = ' + nearestNormalAspectRatio(2520, 3776, 0));

function nearestNormalAspectRatio(width, height, side) {
var
ratio = (width * 100) / (height * 100),
maxW = 3 in arguments ? arguments[2] : 16,
maxH = 4 in arguments ? arguments[3] : 16,
ratiosW = new Array(maxW).join(',').split(','),
ratiosH = new Array(maxH).join(',').split(','),
ratiosT = {},
ratios = {},
match,
key;

ratiosW.forEach(function(empty, ratioW) {
++ratioW;

ratiosH.forEach(function(empty, ratioH) {
++ratioH;

ratioX = (ratioW * 100) / (ratioH * 100);

if (!ratiosT[ratioX]) {
ratiosT[ratioX] = true;

ratios[ratioW + ':' + ratioH] = ratioX;
}
});
});

for (key in ratios) {
if (!match || (!side && Math.abs(ratio - ratios[key]) < Math.abs(ratio - ratios[match])) || (
side < 0 && ratios[key] <= ratio && Math.abs(ratio - ratios[key]) < Math.abs(ratio - ratios[match])
) || (
side > 0 && ratios[key] >= ratio && Math.abs(ratio - ratios[key]) < Math.abs(ratio - ratios[match])
)) {
match = key;
}
}

return match;
}``````