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

php image gd jpeg

82878 просмотра

9 ответа

Какой самый эффективный способ изменить размер больших изображений в 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);
Автор: maxsilver Источник Размещён: 11.11.2019 01:45

Ответы (9)


45 плюса

Решение

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

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

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

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

Автор: Grzegorz Gierlik Размещён: 15.08.2008 10:08

19 плюса

Вот фрагмент из документации 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

Автор: Kevin Размещён: 15.08.2008 09:50

12 плюса

phpThumb использует ImageMagick всегда, когда это возможно, для скорости (в случае необходимости прибегая к GD) и, похоже, довольно хорошо кеширует данные, чтобы уменьшить нагрузку на сервер. Это довольно легкий способ попробовать (чтобы изменить размер изображения, просто вызовите phpThumb.php с GET-запросом, который включает графическое имя файла и выходные размеры), так что вы можете попробовать, чтобы он соответствовал вашим потребностям.

Автор: phenry Размещён: 15.08.2008 08:53

10 плюса

Для больших изображений используйте 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();
Автор: Steve-o Размещён: 06.01.2011 09:15

9 плюса

Из вашего вопроса, кажется, вы немного новичок в 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"]для проверки формата файла? Поскольку он проверяет ТОЛЬКО расширение файла, а не его содержимое, если кто-то переименует файл, первоначально названный world.png, в world.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(), код довольно длинный, но он прекрасно работает, у него даже есть три параметра: растянуть, сжать и заполните.

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

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

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

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

   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 сохранить изображение Если у нас есть пользователь с именем mike, вы можете сделать следующее, он сохранится в той же папке, что и этот скрипт php:

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

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

imagedestroy($newresource);

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

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

ЧАЕВЫЕ

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

Автор: nut Размещён: 30.10.2013 06:28

4 плюса

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

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

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

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

Автор: Salman A Размещён: 04.04.2009 08:25

3 плюса

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

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

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

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

Автор: alessioalex Размещён: 15.02.2011 01:21

3 плюса

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

Автор: Alasdair Размещён: 30.11.2011 05:37

2 плюса

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

Автор: Adnan Размещён: 05.06.2009 06:40
Вопросы из категории :
32x32