Pregunta

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

Actualmente estoy usando el Dios función imagecopyresampled para tomar imágenes de alta resolución y cambiar su tamaño limpiamente a un tamaño para visualización 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 atenderá a fotógrafos que puedan cargar imágenes de hasta 10 MB de tamaño (o imágenes de hasta 5000x4000 píxeles de tamaño).

Realizar este tipo de operación de cambio de tamaño con imágenes grandes tiende a aumentar el uso de la memoria por un margen muy grande (las imágenes más grandes pueden aumentar el uso de la memoria del script a más de 80 MB).¿Hay alguna manera de hacer que esta operación de cambio de tamaño sea más eficiente?¿Debería utilizar una biblioteca de imágenes alternativa como ImagenMagia?

En este momento, el código de cambio de tamaño se parece a 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);
¿Fue útil?

Solución

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

  1. Prepare 1000 imágenes típicas.
  2. Escriba dos scripts: uno para GD, uno para ImageMagick.
  3. Ejecútelos a ambos unas cuantas veces.
  4. Compare los resultados (tiempo de ejecución total, CPU y uso de E/S, calidad de imagen de resultados).

Algo que, aunque sea mejor para todos, no podría ser lo mejor para ti.

Además, en mi opinión, ImageMagick tiene una interfaz API mucho mejor.

Otros consejos

Aquí hay un fragmento de los documentos de php.net que usé 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

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

Para imágenes más grandes, use libjpeg para cambiar el tamaño al cargar la 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();

De tu pregunta, parece que eres un poco nuevo en GD, compartiré algo de experiencia mía, tal vez esto esté un poco fuera de tema, pero creo que será útil para alguien nuevo en GD como tú:

Paso 1, validar el archivo. Utilice la siguiente función para comprobar si el $_FILES['image']['tmp_name'] El archivo 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, obtenga el formato de archivo 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 del archivo?Porque SOLO verifique la extensión del archivo, no el contenido del archivo, si alguien cambia el nombre de un archivo originalmente llamado mundo.png a mundo.jpg, $_FILES["image"]["type"] devolverá jpeg, no png, entonces $_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 recurso GD Obtenga recursos de 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, obtenga la dimensión de la imagen Ahora puedes obtener la dimensión de la imagen con el siguiente código simple:

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

Ahora, Veamos entonces qué variable obtuvimos de la imagen original:

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

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

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

encoger:La dimensión de la imagen de salida no excederá la nueva dimensión que proporcione y mantendrá la relación alto/ancho de la imagen.

llenar:La dimensión de la imagen de salida será la misma que la nueva dimensión que usted proporcione, recortar y cambiar el tamaño imagen si es necesario y mantenga la relación alto/ancho de la imagen. Esta opción es la que necesitas en tu 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');

Paso 6, cambiar el tamaño de la imagen Usar $args, $width, $height, $format y $resource que obtuvimos desde 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, obtén 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 la 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 imagen 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 destruir recurso ¡No olvides destruir el recurso GD!

imagedestroy($newresource);

o puedes escribir todo tu código en una clase y simplemente usar lo siguiente:

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

CONSEJOS

Recomiendo no convertir el formato de archivo que sube el usuario, encontrará muchos problemas.

Te sugiero que trabajes algo en este sentido:

  1. Realice un getimagesize() en el archivo cargado para verificar el tipo y tamaño de la imagen.
  2. Guarde cualquier imagen JPEG cargada de tamaño inferior a 700 x 700 px en la carpeta de destino "tal cual"
  3. Utilice la biblioteca GD para imágenes de tamaño mediano (consulte este artículo para ver un ejemplo de código: Cambiar el tamaño de las imágenes usando PHP y la biblioteca GD)
  4. Utilice ImageMagick para imágenes grandes.Puedes usar ImageMagick en segundo plano si lo prefieres.

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 les cambie el tamaño en consecuencia.Consulte la sintaxis del comando en: procesamiento de línea de comando imagemagick

Puede indicarle al usuario que el archivo está cargado y programado para ser procesado.El trabajo CRON podría programarse para que se ejecute diariamente en un intervalo específico.La imagen de origen podría eliminarse después del procesamiento para garantizar que una imagen no se procese dos veces.

He escuchado grandes cosas sobre la biblioteca Imagick, lamentablemente no pude instalarla en mi computadora de trabajo ni en casa (y créanme, pasé horas y horas en todo tipo de foros).

Después, he decidido probar esta clase de PHP:

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

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

ImageMagick es multiproceso, por lo que parece ser más rápido, pero en realidad utiliza muchos más recursos que GD.Si ejecutara varios scripts PHP en paralelo, todos usando GD, superarí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. exec.

Para imágenes más grandes utilice phppulgar().Aquí se explica cómo usarlo: http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/.También funciona con imágenes grandes y corruptas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top