Какой самый эффективный способ изменить размер больших изображений в 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);
Вот отрывок из документации 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
источник
$quality + 1
на($quality + 1)
? Как бы то ни было, вы просто изменяете размер с помощью бесполезного дополнительного пикселя. Где проверка на короткое замыкание, когда$dst_w * $quality
>$src_w
?imagecopyresized()
. По сути, он сначала изменяет размер изображения до приемлемого размера (final dimensions
умножается наquality
), а затем передискретизирует его, а не просто передискретизирует полноразмерное изображение. Это может привести к более низкому качеству конечного изображения, но для больших изображений он использует гораздо меньше ресурсов, чемimagecopyresampled()
один, поскольку алгоритм передискретизации должен иметь дело только с изображением, размер которого по умолчанию в 3 раза превышает окончательный размер, по сравнению с полноразмерным изображением ( который может быть намного больше, особенно для фотографий, размер которых изменяется для миниатюр).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();
источник
Судя по вашему вопросу, похоже, что вы новичок в 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); }
ЧАЕВЫЕ
Я рекомендую не конвертировать формат файла, который загружает пользователь, вы столкнетесь со многими проблемами.
источник
Я предлагаю вам поработать что-нибудь в этом направлении:
Чтобы использовать ImageMagick в фоновом режиме, переместите загруженные файлы во временную папку и запланируйте задание CRON, которое «конвертирует» все файлы в формат jpeg и соответствующим образом изменяет их размер. См. Синтаксис команды по адресу: imagemagick-command line processing
Вы можете предложить пользователю загрузить файл и запланировать его обработку. Задание 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/ . Он также работает с большими поврежденными изображениями.
источник