programing

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

itsource 2022. 9. 21. 22:07
반응형

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

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

고해상도 이미지를 촬영해, Web 표시용의 사이즈(가로 약 700픽셀×높이 700픽셀)로 깔끔하게 축소하기 위해서, GD기능의 image copy resampled를 사용하고 있습니다.

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

큰 이미지를 사용하여 이러한 크기 조정 작업을 수행하면 메모리 사용량이 매우 크게 증가하는 경향이 있습니다(이미지가 클수록 스크립트의 메모리 사용량이 80MB를 초과할 수 있습니다).이 크기 조정 작업을 보다 효율적으로 수행할 수 있는 방법이 있습니까?Image Magick 등의 대체 이미지 라이브러리를 사용해야 합니까?

현재 크기 조정 코드는 다음과 같습니다.

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. 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.png라는 파일의 이름을 world.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);

스텝.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단계 이미지 저장 mike라는 이름의 사용자가 있는 경우 다음 작업을 수행할 수 있으며 이 php 스크립트와 동일한 폴더에 저장됩니다.

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

10단계 자원 파괴 GD 자원 파괴 잊지 마세요!

imagedestroy($newresource);

또는 모든 코드를 클래스에 쓰고 다음 명령을 사용할 수도 있습니다.

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

팁들

사용자가 업로드하는 파일 형식을 변환하지 않는 것이 좋습니다.여러 가지 문제가 발생합니다.

다음과 같은 방법으로 작업하는 것이 좋습니다.

  1. 업로드한 파일에서 getimagesize()를 실행하여 이미지 유형과 크기를 확인합니다.
  2. 업로드된 JPEG 이미지 크기가 700x700px 미만인 경우 대상 폴더에 "있는 그대로" 저장
  3. 중간 크기의 이미지에는 GD 라이브러리를 사용합니다(코드 샘플은 다음 문서를 참조하십시오).PHP GD 라이브러리를 사용하여 이미지 크기 조정)
  4. 큰 이미지에는 Image Magick을 사용합니다.원하는 경우 ImageMagick을 백그라운드에서 사용할 수 있습니다.

ImageMagick을 백그라운드에서 사용하려면 업로드된 파일을 임시 폴더로 이동하고 모든 파일을 jpeg로 "변환"하고 그에 따라 크기를 조정하는 CRON 작업을 예약합니다.명령어 구문 참조: imagemagick-command line processing

사용자에게 파일을 업로드하고 처리하도록 예약할 수 있습니다.특정 간격으로 매일 실행되도록 CRON 작업을 예약할 수 있습니다.이미지가 두 번 처리되지 않도록 소스 이미지를 처리한 후 삭제할 수 있습니다.

Imagick 라이브러리에 대해 많은 이야기를 들었습니다만, 안타깝게도 회사 컴퓨터에도 집에도 설치할 수 없었습니다(모든 종류의 포럼에 많은 시간을 할애했습니다).

그 후, 저는 이 PHP 클래스를 시험해 보기로 결정했습니다.

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

꽤 멋지고, 모든 화상의 크기를 조정할 수 있습니다(JPG로 변환할 수도 있습니다).

Image Magick은 멀티 스레드이기 때문에 속도가 빨라 보이지만 실제로는 GD보다 훨씬 많은 리소스를 사용합니다.GD를 사용하여 여러 PHP 스크립트를 병렬로 실행하면 ImageMagick보다 빠른 속도로 간단한 조작이 가능합니다. Image는 Image 훨씬 수 서버에 Magick을 통해 PHP에서는 사용할 수 없지만 서버에 설치하고 실행해야 합니다.exec.

큰 이미지의 경우 phpThumb()를 사용합니다.사용방법은 다음과 같습니다.http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/또, 파손된 큰 이미지에도 유효합니다.

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

반응형