-1

我正在尝试获取照片的纵横比,但以下代码在宽度为 3776、高度为 2520(472:315)的照片上显示错误的纵横比,但在宽度为 3968 和 2232 的照片上显示正确的纵横比虽然高度(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);

我该如何解决我的问题?

提前致谢。

4

2 回答 2

3

实际上,3780x2520 是 3:2 的纵横比;因为您使用 3776 作为宽度,所以 472:315 是正确的比例。如果您进行除法,则结果为 1.498,非常接近 1.5,可以考虑四舍五入为 3:2。

如果您只想要“标准”比率(如“3:2”或“16:9”),则可以将检测到的比率传递给另一个函数,将它们四舍五入以找到最接近/最佳匹配。

这是一个可以为您进行四舍五入的综合函数(仅针对您示例中的尺寸进行了测试,因此我不能保证 100% 成功):

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;
}

要使用此函数,您将比率字符串(不是数值)传递给它,它会尝试在常用比率列表中“找到最佳匹配”。

示例用法:

$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";

上述测试将输出以下内容:

found: 16:9
match: 16:9

found: 472:315
match: 3:2

found: 3:2
match: 3:2

*如果您需要参考,我从wikipedia中获取了“标准”纵横比列表。

于 2012-10-19T03:49:24.513 回答
1

我发现这个要点在 Javascript 中运行良好。所以我将它移植到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;
  }

以这种方式使用它(使用所有参数):

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

这是供参考的JS演示:

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;
}

于 2020-03-16T17:19:39.460 回答