Ресайз картинок (фотографий) на PHP

Недавно возникла необходимость изменять размер фотографий (да и вообще картинок) средствами PHP. Покопавшись в интернете, нашел небольшую библиотеку. Но у нее обнаружился ряд недостатков, а именно:

1. Если делать из большой фотографии маленькую — сильно страдает качество (нет экстраполяции)
2. Нет разворота по EXIF. Если загружать прямо с фотоаппарата, без предварительной обработки, вертикальные снимки становятся горизонтальными. То есть отображаются боком.
3. Нет проверки на корректность исходного файла (битый снимок, например; или файл с неверным расширением).
4. Невозможно применить фильтр sharpen. Он очень даже нужен, поскольку при сильном уменьшении фото теряет четкость. Актуально при использовании resample.
5. Оригинальное изображение нигде не сохраняется в библиотеке, а переписывается. Так что если сделать сначала маленькую картинку, а потом среднюю — последняя будет наипоганейшего качества, т.к. увеличится из маленткой (!). Да и вообще ресайз в несколько этапов сильно хуже, чем в один. Приходится загружать исходник заново через функцию load. Неудобно…

Что добавлено:

1. Функция resample($width, $height). В оригинальном классе есть только resize. Различие между ними в том, что resample экстраполирует (сглаживает) изображение, а resize — нет. Поэтому в последнем случае картинка получается как бы из «кубиков». Особенно хорошо видно при сильном уменьшении.
2. resampleToHeight($height) — аналогично resizeToHeight но с экстраполяцией.
3. resampleToWidth($width) — тоже самое, но по ширине.
4. sharpen($strenght = 18) — чем меньше параметр, тем сильнее sharpen. По дефолту 18, почти всегда достаточно.
5. exifrotate — запускается из функции load автоматически, для формата JPEG.

При «неправильном» (битом, не читающемся, не поддерживающемся и т.д.) файле, вызов load возвращает false. На данный момент библиотека GD поддерживает 3 формата: png, gif и jpg. В связи с этим, мой класс тоже поддерживает только вышеозначенные типы.

Излишне напоминать, что для работы необходима поддержка GD и EXIF в PHP.

Пару примеров:

Изменяем размер картинки picture.jpg до ширины 250 и высоты 400. Пропорции не сохраняются:

  1. <?php
  2. include(‘SimpleImage.php’); //Подключаем библиотеку
  3. $image = new SimpleImage(); //Инициируем экземпляр класса
  4. $image>load(‘picture.jpg’); //Загружаем фото (картинку)
  5. $image>resample(250,400); //Изменяем размер со сглаживанием.
  6. $image>sharpen(); //Придаем резкость (не обязательно)
  7. $image>save(‘picture2.jpg’); //Сохраняем в файл

Делаем ресайз по ширине, при этом сохраняются пропорции:

  1. <?php
  2. include(‘SimpleImage.php’);
  3. $image = new SimpleImage();
  4. $image>load(‘picture.jpg’);
  5. $image>resampleToWidth(250);
  6. header(‘Content-Type: image/jpeg’); //Обязательно отправляем перед выводом заголовок
  7. $image>output(); //Выводим на экран, вместо сохранения.

Что делают остальные функции легко догадаться изучив код.

Непосредственно сам класс:

  1. <?php
  2. class SimpleImage
  3. {
  4. public $nativeimg;
  5. public $image;
  6. public $image_type;
  7. public function load($filename)
  8. {
  9. $image_info = getimagesize($filename);
  10. $this>image_type = $image_info[2];
  11. if( $this>image_type == IMAGETYPE_JPEG )
  12. {
  13. $this>nativeimg = imagecreatefromjpeg($filename);
  14. if($this>getHeight() < $this>getWidth())
  15. $this>exifrotate($filename);
  16. return true;
  17. }
  18. elseif( $this>image_type == IMAGETYPE_GIF )
  19. {
  20. $this>nativeimg = imagecreatefromgif($filename);
  21. return true;
  22. }
  23. elseif( $this>image_type == IMAGETYPE_PNG )
  24. {
  25. $this>nativeimg = imagecreatefrompng($filename);
  26. return true;
  27. }
  28. else
  29. return false;
  30. }
  31. public function save($filename, $image_type=IMAGETYPE_JPEG, $compression=90, $owner=null, $permissions=null)
  32. {
  33. if( $image_type == IMAGETYPE_JPEG )
  34. {
  35. imagejpeg($this>image,$filename,$compression);
  36. }
  37. elseif( $image_type == IMAGETYPE_GIF )
  38. {
  39. imagegif($this>image,$filename);
  40. }
  41. elseif( $image_type == IMAGETYPE_PNG )
  42. {
  43. imagepng($this>image,$filename);
  44. }
  45. if( $permissions != null)
  46. {
  47. chown($filename, $permissions);
  48. }
  49. if( $permissions != null)
  50. {
  51. chmod($filename, $owner);
  52. }
  53. }
  54. public function output($image_type=IMAGETYPE_JPEG)
  55. {
  56. if( $image_type == IMAGETYPE_JPEG )
  57. {
  58. imagejpeg($this>image);
  59. }
  60. elseif( $image_type == IMAGETYPE_GIF )
  61. {
  62. imagegif($this>image);
  63. }
  64. elseif( $image_type == IMAGETYPE_PNG )
  65. {
  66. imagepng($this>image);
  67. }
  68. }
  69. public function getWidth()
  70. {
  71. return imagesx($this>nativeimg);
  72. }
  73. public function getHeight()
  74. {
  75. return imagesy($this>nativeimg);
  76. }
  77. public function resizeToHeight($height)
  78. {
  79. $ratio = $height / $this>getHeight();
  80. $width = $this>getWidth() * $ratio;
  81. $this>resize($width,$height);
  82. }
  83. public function resizeToWidth($width)
  84. {
  85. $ratio = $width / $this>getWidth();
  86. $height = $this>getheight() * $ratio;
  87. $this>resize($width,$height);
  88. }
  89. public function resampleToHeight($height)
  90. {
  91. $ratio = $height / $this>getHeight();
  92. $width = $this>getWidth() * $ratio;
  93. $this>resample($width,$height);
  94. }
  95. public function resampleToWidth($width)
  96. {
  97. $ratio = $width / $this>getWidth();
  98. $height = $this>getheight() * $ratio;
  99. $this>resample($width,$height);
  100. }
  101. public function scale($scale)
  102. {
  103. $width = $this>getWidth() * $scale/100;
  104. $height = $this>getheight() * $scale/100;
  105. $this>resize($width,$height);
  106. }
  107. public function resize($width,$height)
  108. {
  109. $new_image = imagecreatetruecolor($width, $height);
  110. imagecopyresized($new_image, $this>nativeimg, 0, 0, 0, 0, $width, $height, $this>getWidth(), $this>getHeight());
  111. $this>image = $new_image;
  112. }
  113. public function resample($width,$height)
  114. {
  115. $new_image = imagecreatetruecolor($width, $height);
  116. imagecopyresampled($new_image, $this>nativeimg, 0, 0, 0, 0, $width, $height, $this>getWidth(), $this>getHeight());
  117. $this>image = $new_image;
  118. }
  119. public function sharpen($strenght = 18)
  120. {
  121. /*Less value in the middle means more sharpen*/
  122. $sharpen_matrix=array( array(0, 1, 0),
  123. array(1, $strenght, 1),
  124. array(0, 1, 0));
  125. $divisor=array_sum(array_map(‘array_sum’, $sharpen_matrix));
  126. imageconvolution($this>image, $sharpen_matrix, $divisor, 0);
  127. }
  128. public function exifrotate($path)
  129. {
  130. if(function_exists(‘exif_read_data’))
  131. {
  132. $exif = exif_read_data($path);
  133. if(!empty($exif[‘Orientation’]))
  134. {
  135. switch($exif[‘Orientation’])
  136. {
  137. case 8:
  138. $this>nativeimg = imagerotate($this>nativeimg, 90, 0);
  139. break;
  140. case 3:
  141. $this>nativeimg = imagerotate($this>nativeimg, 180, 0);
  142. break;
  143. case 6:
  144. $this>nativeimg = imagerotate($this>nativeimg, 90, 0);
  145. break;
  146. }
  147. }
  148. }
  149. }
  150. }