Development Tip

PHP에서 효율적인 JPEG 이미지 크기 조정

yourdevel 2020. 10. 5. 21:04
반응형

PHP에서 효율적인 JPEG 이미지 크기 조정


PHP에서 큰 이미지의 크기를 조정하는 가장 효율적인 방법은 무엇입니까?

저는 현재 GD 기능인 imagecopyresampled를 사용하여 고해상도 이미지를 가져 와서 웹보기 용 크기 (약 너비 700 픽셀, 높이 700 픽셀)로 깔끔하게 크기를 조정하고 있습니다.

이것은 작은 (2MB 미만) 사진에서 잘 작동하며 전체 크기 조정 작업은 서버에서 1 초도 채 걸리지 않습니다. 그러나이 사이트는 결국 최대 10MB 크기의 이미지 (또는 최대 5000x4000 픽셀 크기의 이미지)를 업로드 할 수있는 사진 작가에게 서비스를 제공 할 것입니다.

큰 이미지로 이러한 종류의 크기 조정 작업을 수행하면 메모리 사용량이 매우 크게 증가하는 경향이 있습니다 (이미지가 클수록 스크립트의 메모리 사용량이 80MB를 초과 할 수 있음). 이 크기 조정 작업을보다 효율적으로 만들 수있는 방법이 있습니까? 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);

사람들은 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로 대체) 서버의 부하를 줄이기 위해 캐시를 꽤 잘하는 것처럼 보입니다. 시도해 보는 것은 매우 가볍기 때문에 (이미지 크기를 조정하려면 그래픽 파일 이름과 출력 크기를 포함하는 GET 쿼리를 사용하여 phpThumb.php를 호출하기 만하면 됨) 필요에 맞는지 확인하기 위해 시도해 볼 수 있습니다.


더 큰 이미지의 경우 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.pngworld.jpg을 , $_FILES["image"]["type"], PNG하지 JPEG 돌아갑니다 때문에 $_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(). 코드는 다소 길지만 훌륭하게 작동하며 세 가지 옵션도 있습니다 : 늘이기, 축소 , 채우기.

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

Step 8 get extension, Use the following function to get extension of from image format(note, image format is not equal to image extension):

   function getExtensionFromFormat($format) {
      switch ($format) {
         case 'gif':
            return 'gif';
            break;
         case 'jpeg':
            return 'jpg';
            break;
         case 'png':
            return 'png';
      }
   }
   $extension = getExtensionFromFormat($format);

Step 9 save image If we have a user named mike, you can do the following, it will save to the same folder as this php script:

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

Step 10 destroy resource Don't forget destroy GD resource!

imagedestroy($newresource);

or you can write all your code into a class, and simply use the following:

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

TIPS

I recommend not to convert file format that user upload, you will meet many problems.


I suggest that you work something along these lines:

  1. Perform a getimagesize( ) on the uploaded file to check image type and size
  2. Save any uploaded JPEG image smaller than 700x700px in to the destination folder "as-is"
  3. Use GD library for medium size images (see this article for code sample: Resize Images Using PHP and GD Library)
  4. Use ImageMagick for large images. You can use ImageMagick in background if you prefer.

To use ImageMagick in background, move the uploaded files to a temporary folder and schedule a CRON job that "convert"s all files to jpeg and resizes them accordingly. See command syntax at: imagemagick-command line processing

You can prompt the user that file is uploaded and scheduled to be processed. The CRON job could be scheduled to run daily at a specific interval. The source image could be deleted after processing to assure that an image is not processed twice.


I've heard big things about the Imagick library, unfortunately I couldn't install it at my work computer and neither at home (and trust me, I spent hours and hours on all kinds of forums).

Afterwords, I've decided to try this PHP class:

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

It's pretty cool and I can resize all kinds of images (I can convert them to JPG also).


ImageMagick is multithreaded, so it appears to be faster, but actually uses a lot more resources than GD. If you ran several PHP scripts in parallel all using GD then they'd beat ImageMagick in speed for simple operations. ExactImage is less powerful than ImageMagick but a lot faster, though not available through PHP, you'll have to install it on the server and run it through exec.


For larger images use phpThumb(). Here is how to use it: http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/. It also works for large corrupted images.

참고URL : https://stackoverflow.com/questions/12661/efficient-jpeg-image-resizing-in-php

반응형