Сведения о вопросе

HOLY

15:16, 27th August, 2020

Теги

php   image   gd   jpeg    

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

Просмотров: 536   Ответов: 9

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

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

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

Выполнение такого рода операций изменения размера с большими изображениями приводит к увеличению использования памяти с очень большим запасом (большие изображения могут резко увеличить использование памяти для сценария после 80 MB). Есть ли способ сделать эту операцию изменения размера более эффективной? Должен ли я использовать альтернативную библиотеку изображений, такую как 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);



  Сведения об ответе

ЯЯ__4

11:08, 26th August, 2020

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

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

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

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


  Сведения об ответе

fo_I_K

21:06, 1st October, 2020

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

<?
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


  Сведения об ответе

ASER

16:13, 1st August, 2020

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


  Сведения об ответе

9090

12:21, 24th August, 2020

Для больших изображений используйте 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();


  Сведения об ответе

FAriza

00:15, 28th August, 2020

От вас 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"] для проверки формата файлов? Поскольку он ONLY проверяет расширение файла, а не содержимое файла , если кто-то переименует файл с первоначальным именем 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);

Step.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);
   }

TIPS

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


  Сведения об ответе

SEEYOU

13:07, 9th August, 2020

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

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

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

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


  Сведения об ответе

DO__IT

19:54, 11th August, 2020

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

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

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

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


  Сведения об ответе

lats

04:01, 29th August, 2020

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


  Сведения об ответе

PIRLO

21:33, 18th August, 2020

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться