Question

Quel est le moyen le plus efficace de redimensionner de grandes images en PHP ?

J'utilise actuellement le DG fonction imagecopyresampled pour prendre des images haute résolution et les redimensionner proprement à une taille pour la visualisation sur le Web (environ 700 pixels de large sur 700 pixels de haut).

Cela fonctionne très bien sur les petites photos (moins de 2 Mo) et l'ensemble de l'opération de redimensionnement prend moins d'une seconde sur le serveur.Cependant, le site servira à terme aux photographes susceptibles de télécharger des images d'une taille allant jusqu'à 10 Mo (ou des images d'une taille allant jusqu'à 5 000 x 4 000 pixels).

Effectuer ce type d'opération de redimensionnement avec des images volumineuses a tendance à augmenter considérablement l'utilisation de la mémoire (des images plus grandes peuvent augmenter l'utilisation de la mémoire pour le script au-delà de 80 Mo).Existe-t-il un moyen de rendre cette opération de redimensionnement plus efficace ?Dois-je utiliser une autre bibliothèque d'images telle que ImageMagick?

À l'heure actuelle, le code de redimensionnement ressemble à ceci

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);
Était-ce utile?

La solution

Les gens disent qu'ImageMagick est beaucoup plus rapide.Au mieux, comparez simplement les deux bibliothèques et mesurez-le.

  1. Préparez 1000 images typiques.
  2. Écrivez deux scripts - un pour GD, un pour ImageMagick.
  3. Exécutez-les tous les deux plusieurs fois.
  4. Comparez les résultats (temps d'exécution total, utilisation du processeur et des E / S, qualité de l'image des résultats).

Quelque chose qui est le meilleur pour tout le monde ne pourrait pas être le meilleur pour vous.

De plus, à mon avis, ImageMagick possède une bien meilleure interface API.

Autres conseils

Voici un extrait de la documentation php.net que j'ai utilisé dans un projet et qui fonctionne bien :

<?
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 utilise ImageMagick autant que possible pour la vitesse (en revenant à GD si nécessaire) et semble assez bien mettre en cache pour réduire la charge sur le serveur.C'est assez léger à essayer (pour redimensionner une image, appelez simplement phpThumb.php avec une requête GET qui inclut le nom du fichier graphique et les dimensions de sortie), vous pouvez donc l'essayer pour voir si elle répond à vos besoins.

Pour des images plus grandes, utilisez libjpeg pour redimensionner lors du chargement de l'image dans ImageMagick et réduisant ainsi considérablement l'utilisation de la mémoire et améliorant les performances, ce n'est pas possible avec 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();

De votre question, il semble que vous soyez un peu nouveau dans GD, je partagerai une de mes expériences, peut-être que c'est un peu hors sujet, mais je pense que ce sera utile à quelqu'un de nouveau pour GD comme vous:

Étape 1, validez le fichier. Utilisez la fonction suivante pour vérifier si le $_FILES['image']['tmp_name'] le fichier est un fichier valide :

   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']);

Étape 2, obtenez le format de fichier Essayez la fonction suivante avec l'extension finfo pour vérifier le format du fichier (contenu).Vous diriez pourquoi n'utilisez-vous pas simplement $_FILES["image"]["type"] vérifier le format du fichier ?Parce qu'il SEULEMENT vérifiez l'extension du fichier et non le contenu du fichier, si quelqu'un renomme un fichier initialement appelé monde.png à monde.jpg, $_FILES["image"]["type"] renverra jpeg et non png, donc $_FILES["image"]["type"] peut renvoyer un résultat erroné.

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

Étape 3, obtenir la ressource GD Obtenez la ressource GD à partir du contenu que nous avons auparavant :

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

Étape 4, obtenez la dimension de l'image Vous pouvez maintenant obtenir la dimension de l'image avec le code simple suivant :

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

Maintenant, Voyons alors quelle variable nous avons obtenue de l'image originale :

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

Étape 5, calculez les arguments de l'image redimensionnée Cette étape est liée à votre question, le but de la fonction suivante est d'obtenir les arguments de redimensionnement pour la fonction GD imagecopyresampled(), le code est un peu long, mais il fonctionne très bien, il a même trois options :étirer, rétrécir et remplir.

extensible:La dimension de l'image de sortie est la même que la nouvelle dimension que vous avez définie.Ne conservera pas le rapport hauteur/largeur.

rétrécir:La dimension de l'image de sortie ne dépassera pas la nouvelle dimension que vous donnez et conservera le rapport hauteur/largeur de l'image.

remplir:La dimension de l'image de sortie sera la même que la nouvelle dimension que vous donnez, elle sera recadrer et redimensionner image si nécessaire, et conservez le rapport hauteur/largeur de l’image. Cette option est ce dont vous avez besoin dans votre question.

   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');

Étape 6, redimensionner l'image Utiliser $args, $width, $height, $format et $resource que nous avons obtenu d'en haut dans la fonction suivante et obtenons la nouvelle ressource de l'image redimensionnée :

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

Étape 7, obtenez de nouveaux contenus, Utilisez la fonction suivante pour obtenir le contenu de la nouvelle ressource 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);

Étape 8 : obtenir une extension, Utilisez la fonction suivante pour obtenir l'extension du format d'image (remarque, le format d'image n'est pas égal à l'extension d'image) :

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

Étape 9 : enregistrer l'image Si nous avons un utilisateur nommé Mike, vous pouvez faire ce qui suit, il sera enregistré dans le même dossier que ce script php :

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

Étape 10 : détruire la ressource N'oubliez pas de détruire la ressource GD !

imagedestroy($newresource);

ou vous pouvez écrire tout votre code dans une classe et utiliser simplement ce qui suit :

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

CONSEILS

Je recommande de ne pas convertir le format de fichier téléchargé par l'utilisateur, vous rencontrerez de nombreux problèmes.

Je vous suggère de travailler quelque chose dans ce sens :

  1. Effectuez une getimagesize( ) sur le fichier téléchargé pour vérifier le type et la taille de l'image
  2. Enregistrez toute image JPEG téléchargée de moins de 700 x 700 px dans le dossier de destination « telle quelle ».
  3. Utilisez la bibliothèque GD pour les images de taille moyenne (voir cet article pour un exemple de code : Redimensionner les images à l'aide de PHP et de la bibliothèque GD)
  4. Utilisez ImageMagick pour les grandes images.Vous pouvez utiliser ImageMagick en arrière-plan si vous préférez.

Pour utiliser ImageMagick en arrière-plan, déplacez les fichiers téléchargés vers un dossier temporaire et planifiez une tâche CRON qui "convertit" tous les fichiers en jpeg et les redimensionne en conséquence.Voir la syntaxe des commandes à l'adresse : traitement en ligne de commande imagemagick

Vous pouvez indiquer à l'utilisateur que le fichier est téléchargé et dont le traitement est programmé.Le travail CRON peut être programmé pour s'exécuter quotidiennement à un intervalle spécifique.L'image source peut être supprimée après le traitement pour garantir qu'une image n'est pas traitée deux fois.

J'ai entendu beaucoup de choses sur la bibliothèque Imagick, malheureusement je n'ai pas pu l'installer sur mon ordinateur de travail ni à la maison (et croyez-moi, j'ai passé des heures et des heures sur toutes sortes de forums).

Ensuite, j'ai décidé d'essayer cette classe PHP :

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

C'est plutôt cool et je peux redimensionner toutes sortes d'images (je peux aussi les convertir en JPG).

ImageMagick est multithread, il semble donc plus rapide, mais utilise en réalité beaucoup plus de ressources que GD.Si vous exécutiez plusieurs scripts PHP en parallèle en utilisant GD, ils surpasseraient ImageMagick en vitesse pour des opérations simples.ExactImage est moins puissant qu'ImageMagick mais beaucoup plus rapide, bien que non disponible via PHP, vous devrez l'installer sur le serveur et l'exécuter via exec.

Pour des images plus grandes, utilisez phpThumb().Voici comment l'utiliser : http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/.Cela fonctionne également pour les grandes images corrompues.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top