【问题标题】:Finding closest larger resolution with nearest aspect ratio in an array of resolutions在分辨率数组中找到具有最接近纵横比的最接近的较大分辨率
【发布时间】:2012-11-28 03:10:33
【问题描述】:

我有一个数组:

$resolutions = array(
    '480x640',
    '480x800',
    '640x480',
    '640x960',
    '800x1280',
    '2048x1536'
);

我想用最接近的纵横比(相同的方向)检索最接近的较大值。

所以,如果是$needle = '768x1280' - 800x1280
而且,如果是$needle = '320x240' - 640x480。虽然这里最接近的是480x640,但它不应该匹配,因为它的纵横比差异太大。 以此类推。

目的:

我有一组图像,其分辨率在$resolutions 中指定。这些图片将用于智能手机壁纸。

使用 JavaScript,我通过screen.widthscreen.height 发送请求以确定$needle

在服务器端,我将获取给定分辨率的最接近的较大值,将其缩小以适应整个屏幕,同时保持纵横比,如果某些内容与尺寸重叠,则将其裁剪以完全适合屏幕。

问题:

虽然缩放和裁剪一切都非常简单,但我想不出一种方法来找出最接近的较大值,以加载参考图像。

提示:

如果有帮助,$resolutions$needle 可以采用不同的格式,即:array('width' => x, 'height' => y)

尝试:

我尝试尝试使用 levenshtein 距离:http://codepad.viper-7.com/e8JGOw
显然,它仅适用于 768x1280 并导致 800x1280。对于320x240,它导致480x640,但这不适合这次。

【问题讨论】:

  • 在您的第二个示例中 $needle = '320x240' - 640x480. 不应该选择 480x640 ?,因为 480 比 640 更接近 .. 或者您是否首先在 AxB 的 B 部分找到更接近 .. 请澄清什么你想要..
  • 第一步是将这些字符串拆分为单独的 x 和 y 分辨率。否则你会做很多重复的字符串操作。
  • 方向不重要吗?
  • @Nelson,误导,是的 - 我已编辑添加 with nearest aspect ratio 部分。 @popnoodles,已编辑。
  • @Ricardo,截至发稿时 - 没有。目前正在尝试“Levenshtein distance”,然后再继续前进。

标签: php arrays image resolution image-resizing


【解决方案1】:

如果你有一个数字来比较会更容易吗?

这是一个比率,所以就这样吧,例如:640 / 480 = 1.33*

那么你至少有一些漂亮而简单的东西可以与你发送的尺寸进行比较,并且大概会得出一个公差?

一个简单的例子,假设比率数组从最低到最高排序。如果这是一个问题,那么我们将创建一个按区域排序的搜索(x by y)。

function getNearestRatio($myx, $myy)
{

    $ratios = array(
        array('x'=>480, 'y'=>640),
        array('x'=>480, 'y'=>800),
        array('x'=>640, 'y'=>480),
        array('x'=>640, 'y'=>960),
        array('x'=>800, 'y'=>1280),
        array('x'=>2048, 'y'=>1536)
    );
    $tolerance = 0.1;
    foreach ($ratios as $ratio) {
         $aspect = $ratio['x'] / $ratio['y'];
        $myaspect = $myx / $myy;

        if ( ! ($aspect - $tolerance < $myaspect && $myaspect < $aspect + $tolerance )) {
            continue;
        }

        if ($ratio['x'] < $myx || $ratio['y'] < $myy) {
            continue;
        }
        break;
    }

    return $ratio;
}

我已经建立了一个容差,以便它匹配“附近”的纵横比,正如您在问题中提到的那样。

这个函数应该通过你给出的两个测试用例。

【讨论】:

  • 会找到最接近的比率而不是最接近的更大(或相等)尺寸
  • 找到最接近的值肯定是微不足道的吗?我假设困难在于找到最接近的纵横比。
  • 那么继续(不看给出的答案)
  • 再次,这将找到最接近的比率而不是最接近的更大(或相等)大小
  • 怎么回事?您是否使用输入和预期输出对此进行了测试?
【解决方案2】:

试试这个

echo getClosestRes('500x960');
echo '<br /> try too large to match: '.getClosestRes('50000x960');

function getClosestRes($res){
    $screens = array(
        'landscape'=>array(
            '640x480',
            '1200x800'
        ),
        'portrait'=>array(
            '480x640',
            '480x800',
            '640x960',
            '800x1280',
            '1536x2048'
        )
    );

    list($x,$y)=explode('x',$res);
    $use=($x>$y?'landscape':'portrait');

    // if exact match exists return original
    if (array_search($res, $screens[$use])) return $res; 

    foreach ($screens[$use] as $screen){
        $s=explode('x',$screen);
        if ($s[0]>=$x && $s[1]>=$y) return $screen;
    }
    // just return largest if it gets this far.
    return $screen; // last one set to $screen is largest
}

【讨论】:

  • 根据我的测试,开箱即用 - 这是迄今为止效果最好的。 +1
  • 耐心点,不会忘记的。
【解决方案3】:

您可以先提取如下数组:

$resolutions = array(
    '480x640',
    '480x800',
    '640x480',
    '640x960',
    '800x1280',
    '2048x1536'
);

foreach ($resolutions as $resolution):
    $width[]=(int)$resolution;
    $height[]=(int)substr(strrchr($resolution, 'x'), 1);
    echo $width,' x ',$height,'<br>';
endforeach;

然后你可以用in_arrayarray_search这样的数组匹配给定的needle:

$key = array_search('480', $items);
echo $key;  

当您拥有密钥时,只需将其递增为最接近的较大值。我会让你自己做。

【讨论】:

    【解决方案4】:

    好的,我知道了。我编写了一个函数,它返回最低的合适分辨率,并考虑非标准分辨率。

        <?php
        //some obscure resolution, for illustrative purposes
        $theirResolution = '530x700'; 
        $resolutions = array(
            '480x640',
            '480x800',
            '640x480',
            '640x960',
            '800x1280',
            '2048x1536'
        );
    
        function findSmallestResolution($theirResolution,$resolutions){
            $temp = explode('x',$theirResolution);
            //Isolate their display's X dimension
            $theirResolutionX = intval($temp[1]);
            foreach($resolutions as $key => $value){
                $temp = explode('x',$value);
                //if the current resolution is bigger than or equal to theirs in the X dimension, then it's a possibility.
                if($theirResolutionX <= intval($temp[1])){
                    $possibleResolutionsX[] = $value;
                }
            }
            //Now we'll filter our $possibleResolutions in the Y dimension.
            $temp = explode('x',$theirResolution);
            //Isolate their display's Y dimension
            $theirResolutionY = intval($temp[0]);
            foreach($possibleResolutionsX as $key => $value){
                $temp = explode('x',$value);
                //if the current resolution is bigger than or equal to theirs in the X dimension, then it's a possibility.
                if($theirResolutionY <= intval($temp[0])){
                    $possibleResolutionsXY[] = $value;
                }
            }
            //at this point, $possibleResolutionsXY has all of our entries that are big enough. Now to find the smallest among them.
            foreach($possibleResolutionsXY as $key => $value){
                $temp = explode('x', $value);
                //since we didn't specify how standard our app's possible resolutions are, I'll have to measure the smallest in terms of total dots and not simply X and Y.
                $dotCount[] = intval($temp[0]) * intval($temp[1]);
            }
            //find our resolution with the least dots from the ones that still fit the user's.
            foreach($dotCount as $key => $value){
                if($value == min($dotCount)){
                    $minkey = $key;
                }
            }
            //use the key from dotCount to find its corresponding resolution from possibleResolutionsXY.
            return $possibleResolutionsXY[$minkey];
        }
    
    
        findSmallestResolution($theirResolution,$resolutions);
        // returns '640x960'.
    
    
        ?>
    

    【讨论】:

    • 240x320 上失败。它应该返回480x640,它返回了640x480
    • 根据提问者定义的条件,这不是失败。它是可以封装用户分辨率的一组最小分辨率之一。尽管如此,您是对的,因为这个解决方案确实违反直觉。
    【解决方案5】:

    首先,我会先使用宽度,然后使用高度来存储干草堆:

    $resolutions = array(
        array('w' => 640, 'h' => 480),
        array('w' => 800, 'h' => 480),
        array('w' => 960, 'h' => 640),
        array('w' => 1280, 'h' => 800),
        array('w' => 2048, 'h' => 1536),
    );
    

    然后,计算针与每件物品的尺寸差异,再计算面积大小:

    array_walk($resolutions, function(&$item) use ($needle) {
        $item['aspect'] = abs($item['w'] - $needle['w']) / abs($item['h'] - $needle['h']);
        $item['area'] = $item['w'] * item['h'];
    });
    
    usort($resolutions, function($a, $b) {
      if ($a['aspect'] != $b['aspect']) {
        return ($a['aspect'] < $b['aspect']) ? -1 : 1;
      }
     return 0;
    });
    

    然后根据更大的分辨率过滤列表;第一个匹配是最接近针长宽比的匹配:

    $needle_area = $needle['w'] * $needle['h'];
    foreach ($resolutions as $item) {
        if ($needle_area < $item['area']) {
            return $item;
        }
    }
    return null;
    

    【讨论】:

      【解决方案6】:

      嗯,结果比我预期的要大,但我认为这符合标准。 它通过将可用分辨率分解为它们的比率来工作。然后按目标比率和可用比率之间的增量升序排序,然后按大小(像素)降序排序。返回顶部匹配 - 这应该是最接近、最小的匹配。

      class ResolutionMatcher
      {
          private $resolutions;
      
          public function __construct(array $resolutions)
          {
              foreach ($resolutions as $resolution) {
                  $this->resolutions[$resolution] = $this->examineResolution($resolution);
              }
          }
      
          public function findClosest($target)
          {
              $targetDetails = $this->examineResolution($target);
              $deltas = array();
              foreach ($this->resolutions as $resolution => $details) {
                  if ($details['long'] < $targetDetails['long'] || $details['short'] < $targetDetails['short']) continue;
                  $deltas[$resolution] = array(
                      'resolution' => $resolution,
                      'delta' => abs($details['ratio'] - $targetDetails['ratio']),
                  );
              }
              $resolutions = $this->resolutions;
              uasort($deltas, function ($a, $b) use ($resolutions) {
                  $deltaA = $a['delta'];
                  $deltaB = $b['delta'];
                  if ($deltaA === $deltaB) {
                      $pixelsA = $resolutions[$a['resolution']]['pixels'];
                      $pixelsB = $resolutions[$b['resolution']]['pixels'];
                      if ($pixelsA === $pixelsB) {
                          return 0;
                      }
                      return $pixelsA > $pixelsB ? 1 : -1;
                  }
                  return $deltaA > $deltaB ? 1 : -1;
              });
              $resolutions = array_keys($deltas);
              return array_pop($resolutions);
          }
      
          private function examineResolution($resolution)
          {
              list($width, $height) = explode('x', $resolution);
              $long = ($width > $height) ? $width : $height;
              $short = ($width < $height) ? $width : $height;
              $ratio = $long / $short;
              $pixels = $long * $short;
              return array(
                  'resolutions' => $resolution,
                  'pixels' => $pixels,
                  'long' => $long,
                  'short' => $short,
                  'ratio' => $ratio,
              );
          }
      }
      

      用法:

      $resolutions = array(
          '480x640',
          '480x800',
          '640x480',
          '640x960',
          '800x1280',
          '2048x1536'
      );
      
      $target = $_GET['target'];
      
      $matcher = new ResolutionMatcher($resolutions);
      $closest = $matcher->findClosest($target);
      

      【讨论】:

        【解决方案7】:

        快速上课。应该能够找到您指定的任何两个数字的最小分辨率。我已经用您指定的分辨率预加载了它,但 $_resolutions 数组可以设置为您喜欢的任何标准,也可以即时更改。

        class Resolution {
        
            /**
             * Standard resolutions
             *
             * Ordered by smallest to largest width, followed by height.
             *
             * @var array
             */
            private $_resolutions = array(
                array('480', '640'),
                array('480', '800'),
                array('640', '480'),
                array('640', '960'),
                array('800', '1280'),
                array('2048', '1536')
            );
        
            /**
             * Width
             *
             * @var int
             */
            private $_width;
        
            /**
             * Height
             *
             * @var int
             */
            private $_height;
        
            /**
             * Constructor
             *
             * @param  int $width
             * @param  int $height
             * @return void
             */
            public function __construct($width, $height) {
                $this->setSize($width, $height);
            }
        
            /**
             * Find the minimum matched standard resolution
             *
             * @param  bool $revertToLargest (OPTIONAL) If no large enough resolution is found, use the largest available.
             * @param  bool $matchAspectRatio (OPTIONAL) Attempt to get the closest resolution with the same aspect ratio. If no resolutions have the same aspect ratio, it will simply use the minimum available size.
             * @return array The matched resolution width/height as an array.  If no large enough resolution is found, FALSE is returned, unless $revertToLargest is set.
             */
            public function getMinimumMatch($revertToLargest = false, $matchAspectRatio = true) {
                if ($matchAspectRatio) {
                    $aspect = $this->_width/$this->_height;
                    foreach ($this->_resolutions as $res) {
                        if ($res[0]/$res[1] == $aspect) {
                            if ($this->_width > $res[0] || $this->_height >     $res[1]) {
                                return ($revertToLargest ? $res : false);
                            }
                            return $res;
                        }
                    }
                }
                foreach ($this->_resolutions as $i => $res) {
                    if ($this->_width <= $res[0]) {
                        $total = count($this->_resolutions);
                        for ($j = $i; $j < $total; $j++) {
                            if ($this->_height <= $this->_resolutions[$j][1]) {
                                return $this->_resolutions[$j];
                            }
                        }
                    }
                }
                return ($revertToLargest ? end($this->_resolutions) : false);
            }
        
            /**
             * Get the resolution
             *
             * @return array The resolution width/height as an array
             */
            public function getSize() {
                return array($this->_width, $this->_height);
            }
        
            /**
             * Set the resolution
             *
             * @param  int $width
             * @param  int $height
             * @return array The new resolution width/height as an array
             */
            public function setSize($width, $height) {
                $this->_width = abs(intval($width));
                $this->_height = abs(intval($height));
                return $this->getSize();
            }
        
            /**
             * Get the standard resolutions
             *
             * @return array
             */
            public function getStandardResolutions() {
                return $this->_resolutions;
            }
        
            /**
             * Set the standard resolution values
             *
             * @param  array An array of resolution width/heights as sub-arrays
             * @return array
             */
            public function setStandardResolutions(array $resolutions) {
                $this->_resolutions = $resolutions;
                return $this->_resolutions;
            }
        
        }
        

        示例用法

        $screen = new Resolution(320, 240);
        $screen->getMinimumMatch();
        // Returns 640 x 480 (aspect ratio matched)
        
        $screen = new Resolution(1280, 960);
        $screen->getMinimumMatch();
        // Returns 640 x 480 (aspect ratio matched)
        
        $screen = new Resolution(400, 960);
        $screen->getMinimumMatch();
        // Returns 640 x 960 (aspect ratio not matched, so uses closest fit)
        
        $screen = new Resolution(5000, 5000);
        $screen->getMinimumMatch();
        // Returns FALSE (aspect ratio not matched and resolution too large)
        
        $screen = new Resolution(5000, 5000);
        $screen->getMinimumMatch(true);
        // Returns 2048 x 1536 (aspect ratio not matched and resolution too large, so uses largest available)
        

        【讨论】:

        • 代码很干净,很努力,只是320x240返回480x640。虽然它是最接近的,但纵横比不合适,因此在这种情况下最接近的应该是 640x480(精确双倍)。不过,为努力和清洁+1。
        • @psycketom 啊,我没有意识到你也想保持纵横比。试试更新的代码。
        猜你喜欢
        • 2013-01-19
        • 1970-01-01
        • 1970-01-01
        • 2012-10-06
        • 2011-10-27
        • 2021-11-24
        • 1970-01-01
        • 2013-04-02
        相关资源
        最近更新 更多