PHP中有效的JPEG图像大小调整

什么是在PHP中调整大图像的最有效的方法?

我目前使用GDfunctionimagecopyresampled采取高分辨率的图像,并干净地resize他们的网页浏览的大小(大约700像素宽700像素高)。

这对于较小的(2 MB以下)照片效果很好,整个resize操作在服务器上的时间不到一秒钟。 但是,该网站最终将为可能上传图片大小为10 MB(或图片尺寸高达5000 x4000像素)的摄影师提供服务。

使用大图像进行这种resize操作会导致内存使用量大幅增加(较大的图像可能会使内存使用率超过80 MB)。 有没有办法使这个resize操作更有效率? 我应该使用一个替代图像库,如ImageMagick ?

现在,resize代码看起来像这样

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. 比较结果(总执行时间,CPU和I / O使用情况,结果图像质量)。

其他人最好的东西,不可能是最适合你的东西。

另外,在我看来,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),并且caching起来非常好,可以减轻服务器的负担。 这是非常轻量级的尝试(调整图像大小,只需调用phpThumb.php GET查询,包括graphics文件名和输出尺寸),所以你可以试试看,看看它是否符合您的需求。

对于较大的图像,使用libjpeg调整ImageMagick中的图像加载大小,从而显着减less内存使用量并提高性能,但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,validation文件。 使用下面的函数来检查$_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扩展名尝试以下function来检查文件(内容)的文件格式。 你会说为什么你不使用$_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); 

现在,让我们看看我们从原始图像中得到了什么variables:

  $contents, $format, $resource, $width, $height OK, lets move on 

第5步,计算resize的图像参数这一步与你的问题有关,以下函数的目的是为GD函数获取resize的参数imagecopyresampled() ,代码有点长,但是效果很好,甚至有三个选项:拉伸,收缩和填充。

stretch :输出图像的尺寸与您设置的新尺寸相同。 不会保持高度/宽度的比例。

缩小 :输出图像的尺寸不会超过您给出的新尺寸,并保持图像的高宽比。

填充 :输出图像的尺寸将与您input的新尺寸相同,如果需要,将裁剪和调整图像大小 ,并保持图像的高宽比。 这个选项是你的问题所需要的。

  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到下面的函数中,得到resize的图像的新资源:

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

提示

我build议不要转换用户上传的文件格式,你会遇到很多问题。

我build议你沿着这样的方向工作:

  1. 在上传的文件上执行getimagesize()以检查图像types和大小
  2. 将小于700×700像素的上传的JPEG图像保存到目标文件夹“原样”
  3. 对于中等大小的图像使用GD库(参见本文代码示例: 使用PHP和GD库调整图像大小 )
  4. 使用ImageMagick处理大图像。 如果您愿意,可以在后台使用ImageMagick。

要在后台使用ImageMagick,请将上传的文件移动到临时文件夹,然后安排一个CRON作业,将所有文件“转换为JPEG”并相应地调整它们的大小。 请参阅: imagemagick命令行处理中的命令语法

您可以提示用户该文件已上传并计划进行处理。 CRON工作可以按照特定的时间间隔每天运行。 源图像可以在处理后被删除,以确保图像不被处理两次。

我听说过Imagick图书馆的大部分内容,但不幸的是,我无法将其安装在工作计算机上,也不能在家中安装(相信我,我花了几个小时在各种论坛上)。

Afterwords,我决定尝试这个PHP类:

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

这很酷,我可以调整各种图像(我也可以将它们转换为JPG)。

ImageMagick是multithreading的,所以它看起来更快,但实际上比GD使用更多的资源。 如果你使用GD并行地运行了几个PHP脚本,那么他们就可以很快的击败ImageMagick来进行简单的操作。 ExactImage不如ImageMagick强大,但速度要快得多,虽然不能通过PHP获得,但您必须将其安装在服务器上并通过exec运行。

对于较大的图像使用phpThumb() 。 这里是如何使用它: http : //abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/ 。 它也适用于大量损坏的图像。