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

StackOverflow https://stackoverflow.com/questions/12661

  •  08-06-2019
  •  | 
  •  

Вопрос

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

В настоящее время я использую ГД Функция imagecopyresampled позволяет получать изображения с высоким разрешением и аккуратно уменьшать их размер до размера для просмотра в Интернете (примерно 700 пикселей в ширину и 700 пикселей в высоту).

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

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

Прямо сейчас код изменения размера выглядит примерно так

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);
Это было полезно?

Решение

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

  1. Подготовьте 1000 типичных изображений.
  2. Напишите два сценария - один для GD, один для ImageMagick.
  3. Запустите их обоих несколько раз.
  4. Сравните результаты (общее время выполнения, использование ЦП и ввода -вывода, качество изображения результатов).

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

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

Из вас, Quesion, кажется, что вы новичок в 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 к мир.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 Получите ресурс 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(), код довольно длинный, но работает отлично, у него даже есть три варианта:растянуть, сжать и заполнить.

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

сокращать:Размер выходного изображения не будет превышать заданный вами новый размер и сохранит соотношение высоты и ширины изображения.

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

   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 размером менее 700x700 пикселей в папку назначения «как есть».
  3. Используйте библиотеку GD для изображений среднего размера (пример кода см. в этой статье: Изменение размера изображений с использованием библиотеки PHP и GD)
  4. Используйте ImageMagick для больших изображений.Если хотите, вы можете использовать ImageMagick в фоновом режиме.

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

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

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

После этого я решил попробовать этот класс 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/.Это также работает для больших поврежденных изображений.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top