Эффективное изменение размера изображения JPEG в PHP

каков наиболее эффективный способ изменения размера больших изображений в PHP?

в настоящее время я использую GD функция imagecopyresampled для получения изображений с высоким разрешением и чистого изменения их размера до размера для веб-просмотра (примерно 700 пикселей в ширину и 700 пикселей в высоту).

это отлично работает на небольших (менее 2 МБ) фотографиях, и вся операция изменения размера занимает менее секунды на сервере. Однако сайт в конечном итоге будет обслуживать фотографов, которые могут быть загрузка изображений размером до 10 Мб (или изображений размером до 5000x4000 пикселей).

выполнение такого рода операции изменения размера с большими изображениями имеет тенденцию увеличивать использование памяти очень большим запасом (большие изображения могут увеличить использование памяти для скрипта за 80 МБ). Есть ли способ сделать эту операцию изменения размера более эффективной? Должен ли я использовать альтернативную библиотеку изображений, такую как ImageMagick?

сейчас, кода размер выглядит это

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

9 ответов


люди говорят, что ImageMagick намного быстрее. В лучшем случае просто сравните обе библиотеки и измерьте это.

  1. подготовьте 1000 типичных изображений.
  2. написать два сценария -- один для GD, один для ImageMagick.
  3. выполнить оба из них несколько раз.
  4. сравнить результаты (выполнение использование времени, CPU и I/O, результат качество изображения.)

Что-то, что лучше всех остальных, не может быть лучшим для вас.

кроме того, в на мой взгляд, ImageMagick имеет гораздо лучший интерфейс API.


вот фрагмент из php.net документы, которые я использовал в проекте и отлично работает:

<?
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 использует ImageMagick, когда это возможно для скорости (при необходимости возвращается к GD) и, похоже, кэширует довольно хорошо, чтобы уменьшить нагрузку на сервер. Это довольно легкий, чтобы попробовать (чтобы изменить размер изображения, просто позвоните сайта.php с запросом GET, который включает графическое имя файла и выходные размеры), поэтому вы можете дать ему шанс увидеть, соответствует ли он вашим потребностям.


для больших изображений используйте libjpeg для изменения размера при загрузке изображения в ImageMagick и тем самым значительно уменьшая использование памяти и улучшая производительность, это невозможно с 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();

от вас вопрос, похоже, вы новичок в GD, я поделюсь своим опытом, может быть, это немного не по теме, но я думаю, что это будет полезно для кого-то нового для GD, как вы:

Шаг 1, проверить файл. использовать следующую функцию, чтобы проверить, если $_FILES['image']['tmp_name'] файл является допустимым файлом:

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

Шаг 2, получить формат файла попробуйте следующую функцию с расширением finfo, чтобы проверить формат файла(оглавление). Вы сказал бы, почему бы вам просто не использовать $_FILES["image"]["type"] чтобы проверить формат файла? Потому что это только проверьте расширение файла не содержимое файла, если кто-то переименует файл, первоначально называемый мире.формат PNG to мире.формат JPG, $_FILES["image"]["type"] вернет jpeg не png, так что $_FILES["image"]["type"] может вернуть неправильный результат.

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

шаг.3, получить GD resource получить GD ресурс из содержимого у нас есть раньше:

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

Шаг 4, получить изображение размер теперь вы можете получить размер изображения со следующим простым кодом:

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

теперь давайте посмотрим, какую переменную мы получили от исходного изображения:

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

Шаг 5, вычислить аргументы измененного изображения этот шаг связан с вашим вопросом, цель следующей функции-получить аргументы изменения размера для функции GD imagecopyresampled(), код довольно длинный, но он отлично работает, у него даже есть три варианта: stretch, сжать и заполнить.

стрейч: размер выходного изображения совпадает с новым размером, который вы установили. Не будет поддерживать соотношение высота/ширина.

shrink: размер изображения выхода не превысит новый размер вы даете, и держите Коэффициент высоты/ширины изображения.

заполнить: размер изображения выхода будет этим же как новое измерение вы даете, оно будет обрезка и изменение размера изображение если нужно, и держите Коэффициент высоты/ширины изображения. этот вариант то, что вам нужно в вашем вопросе.

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

Шаг 6, изменение размера изображения использовать $args, $width, $height, $format и $ resource мы получили сверху в следующую функцию и получили новый ресурс измененного изображения:

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

Шаг 7, получить новое содержимое используйте следующую функцию, чтобы получить содержимое нового ресурса 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);

Шаг 8 получить расширение, используйте следующую функцию, чтобы получить расширение из формата изображения(обратите внимание, формат изображения не равен расширению изображения):

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

Шаг 9 Сохранить Изображение если у нас есть пользователь по имени Майк, вы можете сделать следующее, он сохранит в той же папке, что и этот PHP-скрипт:

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

Шаг 10 уничтожить ресурс не забудьте уничтожить GD ресурс!

imagedestroy($newresource);

или вы можете писать весь код в класс, и просто использовать следующее:

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

советы

я рекомендую не конвертировать формат файла, который пользователь загружает, вы встретите много проблем.


Я предлагаю вам поработать в этом направлении:

  1. выполните getimagesize () в загруженном файле, чтобы проверить тип и размер изображения
  2. сохранить любое загруженное изображение JPEG размером менее 700x700px в папку назначения "как есть"
  3. используйте библиотеку GD для изображений среднего размера (см. Эту статью для примера кода:изменение размера изображений с помощью PHP и GD библиотеки)
  4. используйте ImageMagick для больших изображений. Вы можете использовать ImageMagick в фон, если хотите.

чтобы использовать ImageMagick в фоновом режиме, переместите загруженные файлы во временную папку и запланируйте задание CRON, которое "преобразует"все файлы в jpeg и соответственно изменяет их размер. См. синтаксис команды по адресу:imagemagick-обработка командной строки

вы можете запросить пользователя, что файл загружен и запланирован для обработки. Задание CRON может выполняться ежедневно с определенным интервалом. Исходное изображение может быть удалено после обработки, чтобы гарантировать, что изображение не обрабатывается дважды.


Я слышал большие вещи о библиотеке Imagick, к сожалению, я не мог установить его на своем рабочем компьютере и ни дома (и поверьте мне, я провел часы и часы на всех форумах).

Afterwords, я решил попробовать этот класс PHP:

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

Это довольно круто, и я могу изменять размер всех видов изображений (я также могу конвертировать их в JPG).


ImageMagick многопоточен, поэтому он кажется быстрее, но на самом деле использует намного больше ресурсов, чем GD. Если вы запустили несколько PHP-скриптов параллельно, используя GD, они побьют ImageMagick в скорости для простых операций. ExactImage менее мощный, чем ImageMagick, но намного быстрее, хотя и не доступен через PHP, вам придется установить его на сервере и запустить его через exec.


для больших изображений используйте phpThumb (). Вот как его использовать: http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/ - ... Он также работает для больших поврежденных изображений.