6

我有一个数组:

$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但不适合这次。

4

7 回答 7

10

试试这个

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
}
于 2012-12-10T17:19:48.090 回答
1

做了一个快速的类。应该能够找到您指定的任何两个数字的最小分辨率。我已经用您指定的分辨率预加载了它,但$_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)
于 2012-12-10T18:20:07.640 回答
0

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

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

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

一个简单的例子,假设比率数组从最低到最高排序。如果这是一个问题,那么我们将创建一个按区域排序的搜索(x x 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;
}

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

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

于 2012-12-10T17:14:46.597 回答
0

您可以先提取数组,如:

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

然后,您可以将给定的 needle 与数组匹配in_arrayarray_search例如:

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

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

于 2012-12-10T17:20:37.527 回答
0

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

    <?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'.


    ?>
于 2012-12-10T17:26:14.297 回答
0

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

$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;
于 2012-12-10T17:28:07.800 回答
0

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

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);
于 2012-12-10T18:19:10.810 回答