Domanda

Qual è il modo più efficiente per ridimensionare immagini di grandi dimensioni in PHP?

Attualmente sto utilizzando il GD funzione imagecopyresampled per acquisire immagini ad alta risoluzione e ridimensionarle in modo pulito fino a una dimensione per la visualizzazione sul Web (circa 700 pixel di larghezza per 700 pixel di altezza).

Funziona benissimo su foto di piccole dimensioni (meno di 2 MB) e l'intera operazione di ridimensionamento richiede meno di un secondo sul server.Tuttavia, il sito eventualmente servirà i fotografi che potrebbero caricare immagini di dimensioni fino a 10 MB (o immagini di dimensioni fino a 5000x4000 pixel).

L'esecuzione di questo tipo di operazione di ridimensionamento con immagini di grandi dimensioni tende ad aumentare l'utilizzo della memoria con un margine molto ampio (le immagini più grandi possono aumentare l'utilizzo della memoria per lo script oltre gli 80 MB).Esiste un modo per rendere più efficiente questa operazione di ridimensionamento?Dovrei utilizzare una libreria di immagini alternativa come ImageMagick?

In questo momento, il codice di ridimensionamento è simile a questo

function makeThumbnail($sourcefile, $endfile, $thumbwidth, $thumbheight, $quality) {
    // Takes the sourcefile (path/to/image.jpg) and makes a thumbnail from it
    // and places it at endfile (path/to/thumb.jpg).

    // Load image and get image size.
    $img = imagecreatefromjpeg($sourcefile);
    $width = imagesx( $img );
    $height = imagesy( $img );

    if ($width > $height) {
        $newwidth = $thumbwidth;
        $divisor = $width / $thumbwidth;
        $newheight = floor( $height / $divisor);
    } else {
        $newheight = $thumbheight;
        $divisor = $height / $thumbheight;
        $newwidth = floor( $width / $divisor );
    }

    // Create a new temporary image.
    $tmpimg = imagecreatetruecolor( $newwidth, $newheight );

    // Copy and resize old image into new image.
    imagecopyresampled( $tmpimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height );

    // Save thumbnail into a file.
    imagejpeg( $tmpimg, $endfile, $quality);

    // release the memory
    imagedestroy($tmpimg);
    imagedestroy($img);
È stato utile?

Soluzione

La gente dice che ImageMagick è molto più veloce.Nella migliore delle ipotesi basta confrontare entrambe le librerie e misurarlo.

  1. Preparare 1000 immagini tipiche.
  2. Scrivi due script: uno per GD, uno per ImageMagick.
  3. Eseguili entrambi alcune volte.
  4. Confronta i risultati (tempo di esecuzione totale, utilizzo della CPU e I/O, qualità dell'immagine dei risultati).

Qualcosa che è il migliore per tutti gli altri, non potrebbe essere il migliore per te.

Inoltre, secondo me, ImageMagick ha un'interfaccia API molto migliore.

Altri suggerimenti

Ecco uno snippet dei documenti php.net che ho utilizzato in un progetto e funziona bene:

<?
function fastimagecopyresampled (&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h, $quality = 3) {
    // Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled.
    // Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled".
    // Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting.
    // Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain.
    //
    // Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero.
    // Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
    // 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
    // 2 = Up to 95 times faster.  Images appear a little sharp, some prefer this over a quality of 3.
    // 3 = Up to 60 times faster.  Will give high quality smooth results very close to imagecopyresampled, just faster.
    // 4 = Up to 25 times faster.  Almost identical to imagecopyresampled for most images.
    // 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.

    if (empty($src_image) || empty($dst_image) || $quality <= 0) { return false; }
    if ($quality < 5 && (($dst_w * $quality) < $src_w || ($dst_h * $quality) < $src_h)) {
        $temp = imagecreatetruecolor ($dst_w * $quality + 1, $dst_h * $quality + 1);
        imagecopyresized ($temp, $src_image, 0, 0, $src_x, $src_y, $dst_w * $quality + 1, $dst_h * $quality + 1, $src_w, $src_h);
        imagecopyresampled ($dst_image, $temp, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $dst_w * $quality, $dst_h * $quality);
        imagedestroy ($temp);
    } else imagecopyresampled ($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
    return true;
}
?>

http://us.php.net/manual/en/function.imagecopyresampled.php#77679

phpThumb utilizza ImageMagick quando possibile per la velocità (ricorrendo a GD se necessario) e sembra memorizzare abbastanza bene nella cache per ridurre il carico sul server.È piuttosto leggero da provare (per ridimensionare un'immagine, basta chiamare phpThumb.php con una query GET che include il nome del file grafico e le dimensioni di output), quindi potresti provarlo per vedere se soddisfa le tue esigenze.

Per immagini più grandi utilizzare libjpeg per ridimensionare al caricamento dell'immagine in ImageMagick e quindi ridurre in modo significativo l'utilizzo della memoria e migliorare le prestazioni, non è possibile con GD.

$im = new Imagick();
try {
  $im->pingImage($file_name);
} catch (ImagickException $e) {
  throw new Exception(_('Invalid or corrupted image file, please try uploading another image.'));
}

$width  = $im->getImageWidth();
$height = $im->getImageHeight();
if ($width > $config['width_threshold'] || $height > $config['height_threshold'])
{
  try {
/* send thumbnail parameters to Imagick so that libjpeg can resize images
 * as they are loaded instead of consuming additional resources to pass back
 * to PHP.
 */
    $fitbyWidth = ($config['width_threshold'] / $width) > ($config['height_threshold'] / $height);
    $aspectRatio = $height / $width;
    if ($fitbyWidth) {
      $im->setSize($config['width_threshold'], abs($width * $aspectRatio));
    } else {
      $im->setSize(abs($height / $aspectRatio), $config['height_threshold']);
    }
    $im->readImage($file_name);

/* Imagick::thumbnailImage(fit = true) has a bug that it does fit both dimensions
 */
//  $im->thumbnailImage($config['width_threshold'], $config['height_threshold'], true);

// workaround:
    if ($fitbyWidth) {
      $im->thumbnailImage($config['width_threshold'], 0, false);
    } else {
      $im->thumbnailImage(0, $config['height_threshold'], false);
    }

    $im->setImageFileName($thumbnail_name);
    $im->writeImage();
  }
  catch (ImagickException $e)
  {
    header('HTTP/1.1 500 Internal Server Error');
    throw new Exception(_('An error occured reszing the image.'));
  }
}

/* cleanup Imagick
 */
$im->destroy();

Da te Quesion, sembra che tu sia un po 'nuovo a GD, condividerò alcune mie esperze, forse questo è un po' fuori tema, ma penso che sarà utile a qualcuno di nuovo a GD come te:

Passaggio 1: convalidare il file. Utilizzare la seguente funzione per verificare se il file $_FILES['image']['tmp_name'] il file è un file valido:

   function getContentsFromImage($image) {
      if (@is_file($image) == true) {
         return file_get_contents($image);
      } else {
         throw new \Exception('Invalid image');
      }
   }
   $contents = getContentsFromImage($_FILES['image']['tmp_name']);

Passaggio 2: ottieni il formato del file Prova la seguente funzione con estensione finfo per verificare il formato del file (contenuto).Diresti perché non lo usi e basta $_FILES["image"]["type"] controllare il formato del file?Perché SOLTANTO controlla l'estensione del file e non il contenuto del file, se qualcuno rinomina un file originariamente chiamato mondo.png A mondo.jpg, $_FILES["image"]["type"] restituirà jpeg non png, quindi $_FILES["image"]["type"] potrebbe restituire un risultato errato.

   function getFormatFromContents($contents) {
      $finfo = new \finfo();
      $mimetype = $finfo->buffer($contents, FILEINFO_MIME_TYPE);
      switch ($mimetype) {
         case 'image/jpeg':
            return 'jpeg';
            break;
         case 'image/png':
            return 'png';
            break;
         case 'image/gif':
            return 'gif';
            break;
         default:
            throw new \Exception('Unknown or unsupported image format');
      }
   }
   $format = getFormatFromContents($contents);

Passaggio 3: Ottieni la risorsa GD Ottieni la risorsa GD dai contenuti che abbiamo prima:

   function getGDResourceFromContents($contents) {
      $resource = @imagecreatefromstring($contents);
      if ($resource == false) {
         throw new \Exception('Cannot process image');
      }
      return $resource;
   }
   $resource = getGDResourceFromContents($contents);

Passaggio 4: ottieni la dimensione dell'immagine Ora puoi ottenere la dimensione dell'immagine con il seguente semplice codice:

  $width = imagesx($resource);
  $height = imagesy($resource);

Ora, Vediamo quindi quale variabile abbiamo ottenuto dall'immagine originale:

       $contents, $format, $resource, $width, $height
       OK, lets move on

Passaggio 5, calcola gli argomenti dell'immagine ridimensionata Questo passaggio è correlato alla tua domanda, lo scopo della seguente funzione è ottenere argomenti di ridimensionamento per la funzione GD imagecopyresampled(), il codice è un po' lungo, ma funziona benissimo, ha anche tre opzioni:allungare, restringere e riempire.

stirata:la dimensione dell'immagine di output è la stessa della nuova dimensione impostata.Non manterrà il rapporto altezza/larghezza.

Restringersi:la dimensione dell'immagine di output non supererà la nuova dimensione specificata e manterrà il rapporto altezza/larghezza dell'immagine.

riempire:la dimensione dell'immagine di output sarà la stessa della nuova dimensione fornita, lo farà ritaglia e ridimensiona immagine se necessario e mantenere il rapporto altezza/larghezza dell'immagine. Questa opzione è ciò di cui hai bisogno nella tua domanda.

   function getResizeArgs($width, $height, $newwidth, $newheight, $option) {
      if ($option === 'stretch') {
         if ($width === $newwidth && $height === $newheight) {
            return false;
         }
         $dst_w = $newwidth;
         $dst_h = $newheight;
         $src_w = $width;
         $src_h = $height;
         $src_x = 0;
         $src_y = 0;
      } else if ($option === 'shrink') {
         if ($width <= $newwidth && $height <= $newheight) {
            return false;
         } else if ($width / $height >= $newwidth / $newheight) {
            $dst_w = $newwidth;
            $dst_h = (int) round(($newwidth * $height) / $width);
         } else {
            $dst_w = (int) round(($newheight * $width) / $height);
            $dst_h = $newheight;
         }
         $src_x = 0;
         $src_y = 0;
         $src_w = $width;
         $src_h = $height;
      } else if ($option === 'fill') {
         if ($width === $newwidth && $height === $newheight) {
            return false;
         }
         if ($width / $height >= $newwidth / $newheight) {
            $src_w = (int) round(($newwidth * $height) / $newheight);
            $src_h = $height;
            $src_x = (int) round(($width - $src_w) / 2);
            $src_y = 0;
         } else {
            $src_w = $width;
            $src_h = (int) round(($width * $newheight) / $newwidth);
            $src_x = 0;
            $src_y = (int) round(($height - $src_h) / 2);
         }
         $dst_w = $newwidth;
         $dst_h = $newheight;
      }
      if ($src_w < 1 || $src_h < 1) {
         throw new \Exception('Image width or height is too small');
      }
      return array(
          'dst_x' => 0,
          'dst_y' => 0,
          'src_x' => $src_x,
          'src_y' => $src_y,
          'dst_w' => $dst_w,
          'dst_h' => $dst_h,
          'src_w' => $src_w,
          'src_h' => $src_h
      );
   }
   $args = getResizeArgs($width, $height, 150, 170, 'fill');

Passaggio 6, ridimensiona l'immagine Utilizzo $args, $width, $height, $format e $resource abbiamo ottenuto dall'alto la seguente funzione e otteniamo la nuova risorsa dell'immagine ridimensionata:

   function runResize($width, $height, $format, $resource, $args) {
      if ($args === false) {
         return; //if $args equal to false, this means no resize occurs;
      }
      $newimage = imagecreatetruecolor($args['dst_w'], $args['dst_h']);
      if ($format === 'png') {
         imagealphablending($newimage, false);
         imagesavealpha($newimage, true);
         $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127);
         imagefill($newimage, 0, 0, $transparentindex);
      } else if ($format === 'gif') {
         $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127);
         imagefill($newimage, 0, 0, $transparentindex);
         imagecolortransparent($newimage, $transparentindex);
      }
      imagecopyresampled($newimage, $resource, $args['dst_x'], $args['dst_y'], $args['src_x'], $args['src_y'], $args['dst_w'], $args['dst_h'], $args['src_w'], $args['src_h']);
      imagedestroy($resource);
      return $newimage;
   }
   $newresource = runResize($width, $height, $format, $resource, $args);

Passaggio 7, ottieni nuovi contenuti, Utilizzare la seguente funzione per ottenere i contenuti dalla nuova risorsa GD:

   function getContentsFromGDResource($resource, $format) {
      ob_start();
      switch ($format) {
         case 'gif':
            imagegif($resource);
            break;
         case 'jpeg':
            imagejpeg($resource, NULL, 100);
            break;
         case 'png':
            imagepng($resource, NULL, 9);
      }
      $contents = ob_get_contents();
      ob_end_clean();
      return $contents;
   }
   $newcontents = getContentsFromGDResource($newresource, $format);

Passaggio 8: ottieni l'estensione, Utilizzare la seguente funzione per ottenere l'estensione dal formato immagine (nota, il formato immagine non è uguale all'estensione immagine):

   function getExtensionFromFormat($format) {
      switch ($format) {
         case 'gif':
            return 'gif';
            break;
         case 'jpeg':
            return 'jpg';
            break;
         case 'png':
            return 'png';
      }
   }
   $extension = getExtensionFromFormat($format);

Passaggio 9: salva l'immagine Se abbiamo un utente chiamato Mike, puoi fare quanto segue, verrà salvato nella stessa cartella di questo script php:

$user_name = 'mike';
$filename = $user_name . '.' . $extension;
file_put_contents($filename, $newcontents);

Passaggio 10 distruggere la risorsa Non dimenticare di distruggere la risorsa GD!

imagedestroy($newresource);

oppure puoi scrivere tutto il tuo codice in una classe e utilizzare semplicemente quanto segue:

   public function __destruct() {
      @imagedestroy($this->resource);
   }

SUGGERIMENTI

Ti consiglio di non convertire il formato del file caricato dall'utente, incontrerai molti problemi.

Ti suggerisco di lavorare su qualcosa in questo senso:

  1. Esegui una getimagesize() sul file caricato per verificare il tipo e la dimensione dell'immagine
  2. Salva qualsiasi immagine JPEG caricata di dimensioni inferiori a 700x700 px nella cartella di destinazione "così com'è"
  3. Utilizza la libreria GD per immagini di medie dimensioni (vedi questo articolo per un esempio di codice: Ridimensiona le immagini utilizzando PHP e la libreria GD)
  4. Usa ImageMagick per immagini di grandi dimensioni.Se preferisci, puoi utilizzare ImageMagick in background.

Per utilizzare ImageMagick in background, sposta i file caricati in una cartella temporanea e pianifica un lavoro CRON che "converte" tutti i file in jpeg e li ridimensiona di conseguenza.Vedere la sintassi dei comandi in: elaborazione della riga di comando imagemagick

È possibile richiedere all'utente che il file sia caricato e pianificato per l'elaborazione.È possibile pianificare l'esecuzione giornaliera del lavoro CRON a un intervallo specifico.L'immagine di origine potrebbe essere eliminata dopo l'elaborazione per garantire che un'immagine non venga elaborata due volte.

Ho sentito parlare molto bene della libreria Imagick, purtroppo non sono riuscito a installarla sul mio computer di lavoro e nemmeno a casa (e credetemi, ho passato ore e ore su forum di ogni genere).

Successivamente, ho deciso di provare questa lezione PHP:

http://www.verot.net/php_class_upload.htm

È davvero interessante e posso ridimensionare tutti i tipi di immagini (posso anche convertirle in JPG).

ImageMagick è multithread, quindi sembra essere più veloce, ma in realtà utilizza molte più risorse di GD.Se eseguissi diversi script PHP in parallelo utilizzando tutti GD, batterebbero ImageMagick in velocità per operazioni semplici.ExactImage è meno potente di ImageMagick ma molto più veloce, sebbene non sia disponibile tramite PHP, dovrai installarlo sul server ed eseguirlo exec.

Per immagini più grandi utilizzare phpPollice().Ecco come usarlo: http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/.Funziona anche con immagini danneggiate di grandi dimensioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top