Redimensionamiento eficiente de imágenes JPEG en PHP


¿Cuál es la forma más eficiente de cambiar el tamaño de imágenes grandes en PHP?

Actualmente estoy usando la función GD imagecopyresampled para tomar imágenes de alta resolución y redimensionarlas limpiamente a un tamaño para ver en la web (aproximadamente 700 píxeles de ancho por 700 píxeles de alto).

Esto funciona muy bien en fotos pequeñas (menos de 2 MB) y toda la operación de cambio de tamaño toma menos de un segundo en el servidor. Sin embargo, el sitio eventualmente dará servicio a los fotógrafos que pueden estar cargando imágenes hasta 10 MB de tamaño (o imágenes de hasta 5000x4000 píxeles de tamaño).

Hacer este tipo de operación de redimensionamiento con imágenes grandes tiende a aumentar el uso de memoria por un margen muy grande (las imágenes más grandes pueden aumentar el uso de memoria para el script más allá de 80 MB). ¿Hay alguna manera de hacer que esta operación de redimensionamiento sea más eficiente? ¿Debería usar una biblioteca de imágenes alternativa como ImageMagick ?

En este momento, el código de redimensionamiento se ve algo como esto

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);
Author: ManuSharma, 2008-08-15

9 answers

La gente dice que ImageMagick es mucho más rápido. En el mejor de los casos, solo compare ambas bibliotecas y mida eso.

  1. Prepare 1000 imágenes típicas.
  2. Escribir dos scripts one uno para GD, uno para ImageMagick.
  3. Ejecutar ambos un par de veces.
  4. Comparar resultados (ejecución total tiempo, CPU y uso de E/S, resultado calidad de imagen).

Algo que el mejor todos los demás, no podría ser el mejor para usted.

También, en mi opinión, ImageMagick tiene una API mucho mejor interfaz.

 44
Author: Grzegorz Gierlik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-16 16:32:40

Aquí hay un fragmento de la php.net documentos que he utilizado en un proyecto y funciona 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

 19
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-27 08:05:12

PhpThumb utiliza ImageMagick siempre que sea posible para la velocidad (volviendo a GD si es necesario) y parece almacenar en caché bastante bien para reducir la carga en el servidor. Es bastante ligero para probar (para cambiar el tamaño de una imagen, simplemente llame a phpThumb.php con una consulta GET que incluye el nombre del archivo gráfico y las dimensiones de salida), por lo que puede darle una oportunidad para ver si cumple con sus necesidades.

 11
Author: phenry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-15 20:53:14

Para imágenes más grandes use libjpeg para cambiar el tamaño de la carga de imagen en ImageMagick y, por lo tanto, reducir significativamente el uso de memoria y mejorar el rendimiento, no es posible 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();
 10
Author: Steve-o,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-06 09:15:58

De ti quesion, parece que eres un poco nuevo en GD, compartiré alguna experiencia mía, tal vez esto es un poco fuera de tema, pero creo que será útil para alguien nuevo en GD como usted:

Paso 1, validar archivo. Utilice la siguiente función para comprobar si el archivo $_FILES['image']['tmp_name'] es un archivo válido:

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

Paso 2, get file format Pruebe la siguiente función con la extensión finfo para verificar el formato del archivo(contenido). Dirías por qué no usas $_FILES["image"]["type"] para comprobar el formato de archivo? Debido a que SOLOcomprobar la extensión del archivo no el contenido del archivo, si alguien cambiar el nombre de un archivo originalmente llamado mundo.png a mundo.jpg, $_FILES["image"]["type"] devolverá jpeg no png, por lo que $_FILES["image"]["type"] puede devolver un resultado incorrecto.

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

Paso.3, Obtener el recurso GD Obtener el recurso GD de los contenidos que tenemos antes:

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

Paso 4, obtener dimensión de imagen Ahora puede obtener dimensión de imagen con el siguiente simple código:

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

Ahora, Veamos qué variable obtuvimos de la imagen original: {[22]]}

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

Paso 5, calcular argumentos de imagen redimensionados Este paso está relacionado con su pregunta, el propósito de la siguiente función es obtener argumentos de redimensionamiento para la función GD imagecopyresampled(), el código es un poco largo, pero funciona muy bien, incluso tiene tres opciones: estirar, reducir y rellenar.

estirar: la dimensión de la imagen de salida es la misma que la nueva dimensión que establece. No mantendrá la relación altura / ancho.

reducir: la dimensión de la imagen de salida no excederá la nueva dimensión que dé, y mantendrá la relación altura/ancho de la imagen.

llenar: la dimensión de la imagen de salida será la misma que la nueva dimensión que le dé, lo hará recortar y redimensionar imagen si es necesario, y mantener la relación altura/anchura de la imagen. Esta opción es lo que necesita en su pregunta.

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

Medida 6, cambiar el tamaño de la imagen Use $args, $width, $height, $format y resource resource que obtuvimos de arriba en la siguiente función y obtenemos el nuevo recurso de la imagen redimensionada:

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

Paso 7, obtener nuevos contenidos , Utilice la siguiente función para obtener contenidos del nuevo recurso 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);

Paso 8 obtener extensión, Utilice la siguiente función para obtener la extensión del formato de imagen (nota, el formato de imagen no es igual a la extensión de imagen):

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

Paso 9 guardar image Si tenemos un usuario llamado mike, puede hacer lo siguiente, se guardará en la misma carpeta que este script php:

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

Paso 10 destruye el recurso ¡No olvides destruir el recurso GD!

imagedestroy($newresource);

O puede escribir todo su código en una clase, y simplemente use lo siguiente:

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

CONSEJOS

Recomiendo no convertir el formato de archivo que el usuario cargue, se encontrará con muchos problemas.

 9
Author: nut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-24 11:16:27

Te sugiero que trabajes algo en este sentido:

  1. Realiza un getimagesize( ) en el archivo cargado para comprobar el tipo y tamaño de la imagen
  2. Guarde cualquier imagen JPEG cargada menor que 700x700px en la carpeta de destino"tal cual"
  3. Use la biblioteca GD para imágenes de tamaño mediano (consulte este artículo para ver el ejemplo de código: Cambie el tamaño de las imágenes usando PHP y la Biblioteca GD)
  4. Utilice ImageMagick para imágenes grandes. Puede utilizar ImageMagick en segundo plano si preferir.

Para usar ImageMagick en segundo plano, mueva los archivos cargados a una carpeta temporal y programe un trabajo CRON que "convierta"todos los archivos a jpeg y los cambie de tamaño en consecuencia. Ver sintaxis de comandos en: imagemagick-procesamiento de línea de comandos

Puede solicitar al usuario que el archivo se cargue y se programe para ser procesado. El trabajo CRON podría programarse para ejecutarse diariamente en un intervalo específico. La imagen de origen podría eliminarse después del procesamiento para asegurar que una imagen es no procesado dos veces.

 4
Author: Salman A,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-25 05:58:45

He oído grandes cosas sobre la biblioteca Imagick, desafortunadamente no pude instalarla en mi computadora de trabajo y tampoco en casa (y confía en mí, pasé horas y horas en todo tipo de foros).

Afterwords, he decidido probar esta clase PHP:

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

Es bastante genial y puedo cambiar el tamaño de todo tipo de imágenes (también puedo convertirlas a JPG).

 3
Author: alessioalex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-15 13:21:05

ImageMagick es multiproceso, por lo que parece ser más rápido, pero en realidad utiliza muchos más recursos que GD. Si ejecuta varios scripts PHP en paralelo, todos usando GD, entonces vencerían a ImageMagick en velocidad para operaciones simples. ExactImage es menos potente que ImageMagick pero mucho más rápido, aunque no está disponible a través de PHP, tendrás que instalarlo en el servidor y ejecutarlo a través de exec.

 3
Author: Alasdair,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-30 05:37:32

Para imágenes más grandes use phpThumb(). Aquí está cómo usarlo: http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions / . También funciona para grandes imágenes corruptas.

 2
Author: Adnan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-16 16:37:00