Эффективное изменение размера изображения 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 намного быстрее. В лучшем случае просто сравните обе библиотеки и измерьте это.
- подготовьте 1000 типичных изображений.
- написать два сценария -- один для GD, один для ImageMagick.
- выполнить оба из них несколько раз.
- сравнить результаты (выполнение время, процессор и использование ввода/вывода, результат качество изображения.)
то, что лучше всех остальных, не может быть лучшим для вас.
кроме того, в мое мнение, 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 до мире.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); }советы
я рекомендую не конвертировать формат файла, который пользователь загружает, вы встретите много проблем.
Я предлагаю вам работать что-то в этом роде:
- выполните getimagesize () в загруженном файле, чтобы проверить тип и размер изображения
- сохраните любое загруженное изображение JPEG размером менее 700x700px в папку назначения "как есть"
- используйте библиотеку GD для изображений среднего размера (см. Эту статью для примера кода:изменение размера изображений с помощью PHP и GD библиотеки)
- используйте ImageMagick для больших изображений. Вы можете использовать ImageMagick в фон, если вы предпочитаете.
чтобы использовать ImageMagick в фоновом режиме, переместите загруженные файлы во временную папку и запланируйте задание CRON, которое "преобразует"все файлы в jpeg и соответственно изменяет их размер. См. синтаксис команды по адресу:imagemagick-обработка командной строки
вы можете предложить пользователю, что файл загружается и обрабатывается. Задание хрона можно запланировать для ежедневного выполнения с определенным интервалом. Исходное изображение может быть удалено после обработки, чтобы гарантировать, что изображение не обрабатывается дважды.
Я слышал большие вещи о библиотеке 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/. Он также работает для больших поврежденных изображений.
Comments