Individuazione della risoluzione più grande più vicina con proporzioni più vicine in una serie di risoluzioni

Ho un arrays:

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

Voglio recuperare il più grande valore più vicino con il rapporto di aspetto più vicino (stesso orientamento).

Quindi, in caso di $needle = '768x1280'800x1280 .
E, in caso di $needle = '320x240'640x480 . Mentre il più vicino qui è 480x640 non dovrebbe essere abbinato, perché il suo rapporto d'aspetto è troppo diverso. Così via.

Scopo:

Ho un set di immagini con risoluzioni come specificato in $resolutions . Quelle immagini saranno utilizzate per gli sfondi per smartphone.

Con JavaScript, sto inviando una richiesta con screen.width e screen.height per determinare $needle .

    Sul lato server, sto andando a recuperare il valore più grande più vicino della risoluzione data, ridimensionandolo per adattarlo all'integer schermo preservando le proporzioni, e se qualcosa si sovrappone alle size, ritagliala per adattarla perfettamente allo schermo.

    Problema:

    Mentre tutto è abbastanza semplice con il ridimensionamento e il ritaglio, non riesco a pensare a un modo per scoprire il valore più grande più vicino, per caricare l'image di riferimento.

    suggerimenti:

    Nel caso in cui aiuti, $resolutions e $needle possono essere in un formato diverso, es .: arrays('width' => x, 'height' => y) .

    Cerca:

    Ho provato a sperimentare la distanza di levenshtein: http://codepad.viper-7.com/e8JGOw
    Apparentemente, ha funzionato solo per 768x1280 e risultava 800x1280 . Per 320x240 risultava in 480x640 ma questa volta non si adattava.

    Prova questo

     echo getClosestRes('500x960'); echo '<br /> try too large to match: '.getClosestRes('50000x960'); function getClosestRes($res){ $screens = arrays( 'landscape'=>arrays( '640x480', '1200x800' ), 'portrait'=>arrays( '480x640', '480x800', '640x960', '800x1280', '1536x2048' ) ); list($x,$y)=explode('x',$res); $use=($x>$y?'landscape':'portrait'); // if exact match exists return original if (arrays_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 } 

    Puoi prima estrarre gli arrays come:

     $resolutions = arrays( '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; 

    Quindi puoi abbinare l'ago dato all'arrays con in_arrays e arrays_search come:

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

    Quando hai la chiave, aumentala per il valore maggiore più vicino. Ti lascerò fare da solo.

    Ok, ce l'ho. Ho scritto una function che restituisce la risoluzione più bassa adatta e tiene conto anche delle risoluzioni non standard.

      <?php //some obscure resolution, for illustrative purposes $theirResolution = '530x700'; $resolutions = arrays( '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'. ?> 

    Sarebbe più semplice se tu avessi un solo numero da confrontare?

    È un rapporto, quindi fallo, ad esempio: 640/480 = 1,33 *

    Quindi alless hai qualcosa di bello e semplice da confrontare con le size che stai inviando e presumibilmente trovare una tolleranza?

    Un semplice esempio, che presuppone che l'arrays del rapporto sia ordinato dal più basso al più alto. Se questo fosse un problema, avremmo creato una ricerca ordinata dall'area (x per y).

     function getNearestRatio($myx, $myy) { $ratios = arrays( arrays('x'=>480, 'y'=>640), arrays('x'=>480, 'y'=>800), arrays('x'=>640, 'y'=>480), arrays('x'=>640, 'y'=>960), arrays('x'=>800, 'y'=>1280), arrays('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; } 

    Ho costruito una tolleranza, in modo che corrisponda ai rapporti di aspetto "vicini", come alluso alla tua domanda.

    Questa function dovrebbe superare entrambi i casi di test che hai fornito.

    Bene, questo si è rivelato più grande di quanto mi aspettassi, ma penso che questo soddisfi i criteri. Funziona rompendo le risoluzioni disponibili fino al loro rapporto. Quindi sorting in base al delta tra il rapporto objective e i rapporti disponibili in ordine crescente, quindi in base alla dimensione (pixel) in ordine decrescente. Restituisce la corrispondenza più alta, che dovrebbe essere la partita più vicina e più piccola.

     class ResolutionMatcher { private $resolutions; public function __construct(arrays $resolutions) { foreach ($resolutions as $resolution) { $this->resolutions[$resolution] = $this->examineResolution($resolution); } } public function findClosest($target) { $targetDetails = $this->examineResolution($target); $deltas = arrays(); foreach ($this->resolutions as $resolution => $details) { if ($details['long'] < $targetDetails['long'] || $details['short'] < $targetDetails['short']) continue; $deltas[$resolution] = arrays( '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 = arrays_keys($deltas); return arrays_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 arrays( 'resolutions' => $resolution, 'pixels' => $pixels, 'long' => $long, 'short' => $short, 'ratio' => $ratio, ); } } 

    Uso:

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

    Prima di tutto, memorizzerei il pagliaio usando prima la width, l'altezza il secondo:

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

    Quindi, calcola le differenze di quota tra l'ago e ciascun elemento, seguito dalle size dell'area:

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

    Quindi si filtra l'elenco in base a quali risoluzioni sono più grandi; la prima corrispondenza è quella più vicina alle proporzioni dell'ago:

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

    Fatto un corso veloce Dovrebbe trovare con competenza la risoluzione minima per i due numbers specificati. L'ho precaricato con le risoluzioni che hai specificato, ma l'arrays $_resolutions può essere impostato su qualsiasi standard che ti piace e può anche essere modificato al volo.

     class Resolution { /** * Standard resolutions * * Ordered by smallest to largest width, followed by height. * * @var arrays */ private $_resolutions = arrays( arrays('480', '640'), arrays('480', '800'), arrays('640', '480'), arrays('640', '960'), arrays('800', '1280'), arrays('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 arrays The matched resolution width/height as an arrays. 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 arrays The resolution width/height as an arrays */ public function getSize() { return arrays($this->_width, $this->_height); } /** * Set the resolution * * @param int $width * @param int $height * @return arrays The new resolution width/height as an arrays */ public function setSize($width, $height) { $this->_width = abs(intval($width)); $this->_height = abs(intval($height)); return $this->getSize(); } /** * Get the standard resolutions * * @return arrays */ public function getStandardResolutions() { return $this->_resolutions; } /** * Set the standard resolution values * * @param arrays An arrays of resolution width/heights as sub-arrayss * @return arrays */ public function setStandardResolutions(arrays $resolutions) { $this->_resolutions = $resolutions; return $this->_resolutions; } } 

    Esempio di utilizzo

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